diff --git a/src/components/main/layout/block.rs b/src/components/main/layout/block.rs index 8d2b3e1fae9c..0f965a80c28b 100644 --- a/src/components/main/layout/block.rs +++ b/src/components/main/layout/block.rs @@ -11,11 +11,12 @@ //! 'relative', 'absolute', or 'fixed'. use layout::box_::Box; -use layout::construct::FlowConstructor; +use layout::construct::{FlowConstructor, OptVector}; use layout::context::LayoutContext; use layout::display_list_builder::{DisplayListBuilder, ExtraDisplayListData}; use layout::floats::{FloatKind, Floats, PlacementInfo}; use layout::flow::{BaseFlow, BlockFlowClass, FlowClass, Flow, ImmutableFlowUtils}; +use layout::flow::{mut_base, PreorderFlowTraversal, PostorderFlowTraversal, MutableFlowUtils}; use layout::flow; use layout::model::{MaybeAuto, Specified, Auto, specified_or_none, specified}; use layout::wrapper::ThreadSafeLayoutNode; @@ -23,9 +24,10 @@ use style::computed_values::{position}; use std::cell::RefCell; use geom::{Point2D, Rect, SideOffsets2D, Size2D}; -use gfx::display_list::{DisplayListCollection}; +use gfx::display_list::{DisplayListCollection, DisplayList}; use servo_util::geometry::Au; use servo_util::geometry; +use servo_util::smallvec::{SmallVec, SmallVec0}; /// Information specific to floated blocks. pub struct FloatedBlockInfo { @@ -56,41 +58,6 @@ impl FloatedBlockInfo { } } -/// Details of a potential Containing Block. -/// -/// If a flow has the block as its Containing Block, then this represents its -/// Containing Block's details. -/// Else, this represents intermediate data to be passed on to its descendants who -/// may have this block as their Containing Block (e.g., absolutely positioned -/// elements.) -/// -/// This allows us to calculate width, height, etc. for the flow for which -/// this represents the Containing Block. -#[deriving(Clone)] -pub struct ContainingBlockDetails { - width: Au, - height: Au, - // Absolute offset of the left margin edge of the current flow from the - // left edge of this Containing Block. This includes the margins, etc. of - // all the intermediate flows till the current flow. - static_x_offset: Au, - // Absolute offset of the top margin edge of the current flow from the top - // edge of this Containing Block. This includes the margins, etc. of all - // the intermediate flows till the current flow. - static_y_offset: Au, -} - -impl ContainingBlockDetails { - fn default() -> ContainingBlockDetails { - ContainingBlockDetails { - width: Au::new(0), - height: Au::new(0), - static_x_offset: Au::new(0), - static_y_offset: Au::new(0), - } - } -} - /// The solutions for the widths-and-margins constraint equation. struct WidthConstraintSolution { left: Au, @@ -133,6 +100,169 @@ impl HeightConstraintSolution { margin_bottom: margin_bottom, } } + + /// Solve the vertical constraint equation for absolute non-replaced elements. + /// + /// CSS Section 10.6.4 + /// Constraint equation: + /// top + bottom + height + margin-top + margin-bottom + /// = absolute containing block height - (vertical padding and border) + /// [aka available_height] + /// + /// Return the solution for the equation. + fn solve_vertical_constraints_abs_position(height: MaybeAuto, + top_margin: MaybeAuto, + bottom_margin: MaybeAuto, + top: MaybeAuto, + bottom: MaybeAuto, + content_height: Au, + available_height: Au, + static_y_offset: Au) + -> HeightConstraintSolution { + // Distance from the top edge of the Absolute Containing Block to the + // top margin edge of a hypothetical box that would have been the + // first box of the element. + let static_position_top = static_y_offset; +; + let (top, bottom, height, margin_top, margin_bottom) = match (top, bottom, height) { + (Auto, Auto, Auto) => { + let margin_top = top_margin.specified_or_zero(); + let margin_bottom = bottom_margin.specified_or_zero(); + let top = static_position_top; + // Now it is the same situation as top Specified and bottom + // and height Auto. + + let height = content_height; + let sum = top + height + margin_top + margin_bottom; + (top, available_height - sum, height, margin_top, margin_bottom) + } + (Specified(top), Specified(bottom), Specified(height)) => { + match (top_margin, bottom_margin) { + (Auto, Auto) => { + let total_margin_val = (available_height - top - bottom - height); + (top, bottom, height, + total_margin_val.scale_by(0.5), + total_margin_val.scale_by(0.5)) + } + (Specified(margin_top), Auto) => { + let sum = top + bottom + height + margin_top; + (top, bottom, height, margin_top, available_height - sum) + } + (Auto, Specified(margin_bottom)) => { + let sum = top + bottom + height + margin_bottom; + (top, bottom, height, available_height - sum, margin_bottom) + } + (Specified(margin_top), Specified(margin_bottom)) => { + // Values are over-constrained. Ignore value for 'bottom'. + let sum = top + height + margin_top + margin_bottom; + (top, available_height - sum, height, margin_top, margin_bottom) + } + } + } + + // For the rest of the cases, auto values for margin are set to 0 + + // If only one is Auto, solve for it + (Auto, Specified(bottom), Specified(height)) => { + let margin_top = top_margin.specified_or_zero(); + let margin_bottom = bottom_margin.specified_or_zero(); + let sum = bottom + height + margin_top + margin_bottom; + (available_height - sum, bottom, height, margin_top, margin_bottom) + } + (Specified(top), Auto, Specified(height)) => { + let margin_top = top_margin.specified_or_zero(); + let margin_bottom = bottom_margin.specified_or_zero(); + let sum = top + height + margin_top + margin_bottom; + (top, available_height - sum, height, margin_top, margin_bottom) + } + (Specified(top), Specified(bottom), Auto) => { + let margin_top = top_margin.specified_or_zero(); + let margin_bottom = bottom_margin.specified_or_zero(); + let sum = top + bottom + margin_top + margin_bottom; + (top, bottom, available_height - sum, margin_top, margin_bottom) + } + + // If height is auto, then height is content height. Solve for the + // non-auto value. + (Specified(top), Auto, Auto) => { + let margin_top = top_margin.specified_or_zero(); + let margin_bottom = bottom_margin.specified_or_zero(); + let height = content_height; + let sum = top + height + margin_top + margin_bottom; + (top, available_height - sum, height, margin_top, margin_bottom) + } + (Auto, Specified(bottom), Auto) => { + let margin_top = top_margin.specified_or_zero(); + let margin_bottom = bottom_margin.specified_or_zero(); + let height = content_height; + let sum = bottom + height + margin_top + margin_bottom; + (available_height - sum, bottom, height, margin_top, margin_bottom) + } + + (Auto, Auto, Specified(height)) => { + let margin_top = top_margin.specified_or_zero(); + let margin_bottom = bottom_margin.specified_or_zero(); + let top = static_position_top; + let sum = top + height + margin_top + margin_bottom; + (top, available_height - sum, height, margin_top, margin_bottom) + } + }; + HeightConstraintSolution::new(top, bottom, height, margin_top, margin_bottom) + } +} + +/// The real assign-heights traversal for flows with position 'absolute'. +/// +/// This is a traversal of an Absolute Flow tree. +/// - Relatively positioned flows and the Root flow start new Absolute flow trees. +/// - The kids of a flow in this tree will be the flows for which it is the +/// absolute Containing Block. +/// - Thus, leaf nodes and inner non-root nodes are all Absolute Flows. +/// +/// A Flow tree can have several Absolute Flow trees (depending on the number +/// of relatively positioned flows it has). +/// +/// Note that flows with position 'fixed' just form a flat list as they all +/// have the Root flow as their CB. +struct AbsoluteAssignHeightsTraversal<'a>(&'a mut LayoutContext); + +impl<'a> PreorderFlowTraversal for AbsoluteAssignHeightsTraversal<'a> { + #[inline] + fn process(&mut self, flow: &mut Flow) -> bool { + let block_flow = flow.as_block(); + + // The root of the absolute flow tree is definitely not absolutely + // positioned. Nothing to process here. + if block_flow.is_root_of_absolute_flow_tree() { + return true; + } + + block_flow.calculate_abs_height_and_margins(**self); + true + } +} + +/// The store-overflow traversal particular to absolute flows. +/// +/// Propagate overflow up the Absolute flow tree and update overflow up to and +/// not including the root of the Absolute flow tree. +/// After that, it is up to the normal store-overflow traversal to propagate +/// it further up. +struct AbsoluteStoreOverflowTraversal<'a>{ + layout_context: &'a mut LayoutContext, +} + +impl<'a> PostorderFlowTraversal for AbsoluteStoreOverflowTraversal<'a> { + #[inline] + fn process(&mut self, flow: &mut Flow) -> bool { + // This will be taken care of by the normal store-overflow traversal. + if flow.is_root_of_absolute_flow_tree() { + return true; + } + + flow.store_overflow(self.layout_context); + true + } } // A block formatting context. @@ -147,13 +277,8 @@ pub struct BlockFlow { /// Whether this block flow is the root flow. is_root: bool, - positioning: position::T, - - // Details of the nearest positioned ancestor - aka the Containing Block - // for any absolutely positioned elements. - absolute_cb_details: ContainingBlockDetails, - // Details about the Initial Containing Block and our offset wrt it - fixed_cb_details: ContainingBlockDetails, + /// Static y offset of an absolute flow from its CB. + static_y_offset: Au, /// Additional floating flow members. float: Option<~FloatedBlockInfo> @@ -161,16 +286,13 @@ pub struct BlockFlow { impl BlockFlow { pub fn from_node(constructor: &mut FlowConstructor, - node: &ThreadSafeLayoutNode, - positioning: position::T) + node: &ThreadSafeLayoutNode) -> BlockFlow { BlockFlow { base: BaseFlow::new((*node).clone()), box_: Some(Box::new(constructor, node)), is_root: false, - positioning: positioning, - absolute_cb_details: ContainingBlockDetails::default(), - fixed_cb_details: ContainingBlockDetails::default(), + static_y_offset: Au::new(0), float: None } } @@ -183,9 +305,7 @@ impl BlockFlow { base: BaseFlow::new((*node).clone()), box_: Some(Box::new(constructor, node)), is_root: false, - positioning: position::static_, - absolute_cb_details: ContainingBlockDetails::default(), - fixed_cb_details: ContainingBlockDetails::default(), + static_y_offset: Au::new(0), float: Some(~FloatedBlockInfo::new(float_kind)) } } @@ -195,9 +315,7 @@ impl BlockFlow { base: base, box_: None, is_root: true, - positioning: position::static_, - absolute_cb_details: ContainingBlockDetails::default(), - fixed_cb_details: ContainingBlockDetails::default(), + static_y_offset: Au::new(0), float: None } } @@ -207,40 +325,103 @@ impl BlockFlow { base: base, box_: None, is_root: false, - positioning: position::static_, - absolute_cb_details: ContainingBlockDetails::default(), - fixed_cb_details: ContainingBlockDetails::default(), + static_y_offset: Au::new(0), float: Some(~FloatedBlockInfo::new(float_kind)) } } - pub fn is_float(&self) -> bool { - self.float.is_some() - } - - pub fn is_fixed(&self) -> bool { - self.positioning == position::fixed - } - - pub fn is_absolutely_positioned(&self) -> bool { - self.positioning == position::absolute || self.is_fixed() + /// Return the static x offset from the appropriate Containing Block for this flow. + pub fn static_x_offset(&self) -> Au { + assert!(self.is_absolutely_positioned()); + if self.is_fixed() { + self.base.fixed_static_x_offset + } else { + self.base.absolute_static_x_offset + } } - /// Return the appropriate Containing Block for this flow. + /// Return the size of the Containing Block for this flow. /// - /// Right now, this only gets the Containing Block for absolutely + /// Right now, this only gets the Containing Block size for absolutely /// positioned elements. - pub fn containing_block(&self) -> ContainingBlockDetails { + /// Note: Assume this is called in a top-down traversal, so it is ok to + /// reference the CB. + pub fn containing_block_size(&mut self, viewport_size: Size2D) -> Size2D { assert!(self.is_absolutely_positioned()); if self.is_fixed() { - self.fixed_cb_details + // Initial containing block is the CB for the root + viewport_size } else { - self.absolute_cb_details + let cb = self.base.absolute_cb.resolve().unwrap(); + cb.generated_cb_size() + } + } + + /// Traverse the Absolute flow tree in preorder. + /// + /// Traverse all your direct absolute descendants, who will then traverse + /// their direct absolute descendants. + /// Also, set the static y offsets for each descendant (using the value + /// which was bubbled up during normal assign-height). + /// + /// Return true if the traversal is to continue or false to stop. + fn traverse_preorder_absolute_flows(&mut self, + traversal: &mut T) + -> bool { + let flow = self as &mut Flow; + if traversal.should_prune(flow) { + return true + } + + if !traversal.process(flow) { + return false + } + + let cb_top_edge_offset = flow.generated_cb_position().y; + let mut descendant_offset_iter = mut_base(flow).abs_descendants.iter_with_offset(); + // Pass in the respective static y offset for each descendant. + for (ref mut descendant_link, ref y_offset) in descendant_offset_iter { + match descendant_link.resolve() { + Some(flow) => { + let block = flow.as_block(); + // The stored y_offset is wrt to the flow box. + // Translate it to the CB (which is the padding box). + block.static_y_offset = **y_offset - cb_top_edge_offset; + if !block.traverse_preorder_absolute_flows(traversal) { + return false + } + } + None => fail!("empty Rawlink to a descendant") + } } + + true } - pub fn is_positioned(&self) -> bool { - self.positioning == position::relative || self.is_absolutely_positioned() + /// Traverse the Absolute flow tree in postorder. + /// + /// Return true if the traversal is to continue or false to stop. + fn traverse_postorder_absolute_flows(&mut self, + traversal: &mut T) + -> bool { + let flow = self as &mut Flow; + if traversal.should_prune(flow) { + return true + } + + for descendant_link in mut_base(flow).abs_descendants.iter() { + match descendant_link.resolve() { + Some(abs_flow) => { + let block = abs_flow.as_block(); + if !block.traverse_postorder_absolute_flows(traversal) { + return false + } + } + None => fail!("empty Rawlink to a descendant") + } + } + + traversal.process(flow) } pub fn teardown(&mut self) { @@ -320,10 +501,9 @@ impl BlockFlow { /// + left margin, right margin, and content width for the flow's box /// + x-coordinate of the flow's box /// + x-coordinate of the absolute flow itself (wrt to its Containing Block) - fn calculate_abs_widths_and_margins(&mut self) { - let absolute_cb = self.containing_block(); - let containing_block_width = absolute_cb.width; - let static_x_offset = absolute_cb.static_x_offset; + fn calculate_abs_widths_and_margins(&mut self, ctx: &mut LayoutContext) { + let containing_block_width = self.containing_block_size(ctx.screen_size).width; + let static_x_offset = self.static_x_offset(); for box_ in self.box_.iter() { let style = box_.style(); @@ -332,12 +512,6 @@ impl BlockFlow { box_.compute_padding(style, containing_block_width); - // Top and bottom margins for blocks are 0 if auto. - let margin_top = MaybeAuto::from_style(style.Margin.get().margin_top, - containing_block_width).specified_or_zero(); - let margin_bottom = MaybeAuto::from_style(style.Margin.get().margin_bottom, - containing_block_width).specified_or_zero(); - let (width, margin_left, margin_right) = (MaybeAuto::from_style(style.Box.get().width, containing_block_width), MaybeAuto::from_style(style.Margin.get().margin_left, containing_block_width), @@ -356,10 +530,10 @@ impl BlockFlow { available_width, static_x_offset); - box_.margin.set(SideOffsets2D::new(margin_top, - solution.margin_right, - margin_bottom, - solution.margin_left)); + let mut margin = box_.margin.get(); + margin.left = solution.margin_left; + margin.right = solution.margin_right; + box_.margin.set(margin); // The associated box is the border box of this flow. let mut position_ref = box_.border_box.borrow_mut(); @@ -378,9 +552,10 @@ impl BlockFlow { /// /// Also, set the x-coordinate for box_. fn calculate_widths_and_margins(&mut self, - containing_block_width: Au) { + containing_block_width: Au, + ctx: &mut LayoutContext) { if self.is_absolutely_positioned() { - self.calculate_abs_widths_and_margins(); + self.calculate_abs_widths_and_margins(ctx); return; } for box_ in self.box_.iter() { @@ -400,6 +575,8 @@ impl BlockFlow { let available_width = containing_block_width - box_.noncontent_width(); // Top and bottom margins for blocks are 0 if auto. + // FIXME: This is wrong. We shouldn't even be touching margin-top + // and margin-bottom here. let margin_top = MaybeAuto::from_style(style.Margin.get().margin_top, containing_block_width).specified_or_zero(); let margin_bottom = MaybeAuto::from_style(style.Margin.get().margin_bottom, @@ -446,8 +623,8 @@ impl BlockFlow { available_width: Au, static_x_offset: Au) -> WidthConstraintSolution { - // TODO: Check for direction of Containing Block (NOT parent flow) - // when right-to-left is implemented + // TODO: Check for direction of parent flow (NOT Containing Block) + // when right-to-left is implemented. // Assume direction is 'ltr' for now // Distance from the left edge of the Absolute Containing Block to the @@ -624,6 +801,66 @@ impl BlockFlow { return (width, margin_left, margin_right); } + + /// Collect and update static y-offsets bubbled up by kids. + /// + /// This would essentially give us offsets of all absolutely positioned + /// direct descendants and all fixed descendants, in tree order. + /// + /// Assume that this is called in a bottom-up traversal (specifically, the + /// assign-height traversal). So, kids have their flow origin already set. + /// In the case of absolute flow kids, they have their hypothetical box + /// position already set. + fn collect_static_y_offsets_from_kids(&mut self) { + let mut abs_descendant_y_offsets = SmallVec0::new(); + let mut fixed_descendant_y_offsets = SmallVec0::new(); + + for kid in self.base.child_iter() { + let mut gives_abs_offsets = true; + if kid.is_block_like() { + let kid_block = kid.as_block(); + if kid_block.is_fixed() { + // It won't contribute any offsets for position 'absolute' + // descendants because it would be the CB for them. + gives_abs_offsets = false; + // Add the offset for the current fixed flow too. + fixed_descendant_y_offsets.push(kid_block.get_hypothetical_top_edge()); + } else if kid_block.is_absolutely_positioned() { + // It won't contribute any offsets for descendants because it + // would be the CB for them. + gives_abs_offsets = false; + // Give the offset for the current absolute flow alone. + abs_descendant_y_offsets.push(kid_block.get_hypothetical_top_edge()); + } else if kid_block.is_positioned() { + // It won't contribute any offsets because it would be the CB + // for the descendants. + gives_abs_offsets = false; + } + } + + if gives_abs_offsets { + let kid_base = flow::mut_base(kid); + // Consume all the static y-offsets bubbled up by kid. + for y_offset in kid_base.abs_descendants.static_y_offsets.move_iter() { + // The offsets are wrt the kid flow box. Translate them to current flow. + y_offset = y_offset + kid_base.position.origin.y; + abs_descendant_y_offsets.push(y_offset); + } + } + + // Get all the fixed offsets. + let kid_base = flow::mut_base(kid); + // Consume all the static y-offsets bubbled up by kid. + for y_offset in kid_base.fixed_descendants.static_y_offsets.move_iter() { + // The offsets are wrt the kid flow box. Translate them to current flow. + y_offset = y_offset + kid_base.position.origin.y; + fixed_descendant_y_offsets.push(y_offset); + } + } + self.base.abs_descendants.static_y_offsets = abs_descendant_y_offsets; + self.base.fixed_descendants.static_y_offsets = fixed_descendant_y_offsets; + } + /// Assign height for current flow. /// /// + Collapse margins for flow's children and set in-flow child flows' @@ -703,11 +940,13 @@ impl BlockFlow { // its children. if !self.is_absolutely_positioned() { for box_ in self.box_.iter() { - if !self.is_root && box_.border.get().top == Au(0) && box_.padding.get().top == Au(0) { + if !self.is_root() && box_.border.get().top == Au(0) + && box_.padding.get().top == Au(0) { + collapsible = box_.margin.get().top; top_margin_collapsible = true; } - if !self.is_root && box_.border.get().bottom == Au(0) && + if !self.is_root() && box_.border.get().bottom == Au(0) && box_.padding.get().bottom == Au(0) { bottom_margin_collapsible = true; } @@ -720,7 +959,7 @@ impl BlockFlow { for kid in self.base.child_iter() { // At this point, cur_y is at bottom margin edge of previous kid - if kid.is_block_like() && kid.as_block().is_absolutely_positioned() { + if kid.is_absolutely_positioned() { // Assume that the `hypothetical box` for an absolute flow // starts immediately after the bottom margin edge of the // previous flow. @@ -744,6 +983,8 @@ impl BlockFlow { } } + self.collect_static_y_offsets_from_kids(); + // The bottom margin collapses with its last in-flow block-level child's bottom margin // if the parent has no bottom border, no bottom padding. // The bottom margin for an absolutely positioned element does not @@ -763,7 +1004,7 @@ impl BlockFlow { let screen_height = ctx.screen_size.height; - let mut height = if self.is_root { + let mut height = if self.is_root() { // FIXME(pcwalton): The max is taken here so that you can scroll the page, but this is // not correct behavior according to CSS 2.1 § 10.5. Instead I think we should treat // the root element as having `overflow: scroll` and use the layers-based scrolling @@ -832,6 +1073,47 @@ impl BlockFlow { let extra_height = height - (cur_y - top_offset) + bottom_offset; self.base.floats.translate(Point2D(left_offset, -extra_height)); } + + if self.is_root_of_absolute_flow_tree() { + // Assign heights for all flows in this Absolute flow tree. + // This is preorder because the height of an absolute flow may depend on + // the height of its CB, which may also be an absolute flow. + self.traverse_preorder_absolute_flows(&mut AbsoluteAssignHeightsTraversal(ctx)); + // Store overflow for all absolute descendants. + self.traverse_postorder_absolute_flows(&mut AbsoluteStoreOverflowTraversal { + layout_context: ctx, + }); + } + if self.is_root() { + self.assign_height_store_overflow_fixed_flows(ctx); + } + } + + /// Assign height for all fixed descendants. + /// + /// A flat iteration over all fixed descendants, passing their respective + /// static y offsets. + /// Also, store overflow immediately because nothing else depends on a + /// fixed flow's height. + fn assign_height_store_overflow_fixed_flows(&mut self, ctx: &mut LayoutContext) { + assert!(self.is_root()); + let mut descendant_offset_iter = self.base.fixed_descendants.iter_with_offset(); + // Pass in the respective static y offset for each descendant. + for (ref mut descendant_link, ref y_offset) in descendant_offset_iter { + match descendant_link.resolve() { + Some(fixed_flow) => { + { + let block = fixed_flow.as_block(); + // The stored y_offset is wrt to the flow box (which + // will is also the CB, so it is the correct final value). + block.static_y_offset = **y_offset; + block.calculate_abs_height_and_margins(ctx); + } + fixed_flow.store_overflow(ctx); + } + None => fail!("empty Rawlink to a descendant") + } + } } /// Add placement information about current float flow for use by the parent. @@ -944,67 +1226,6 @@ impl BlockFlow { box_.border_box.set(position); } - /// Pass down static y offset for absolute containing block - /// - /// Assume this is called in a top-down traversal (specifically, during - /// the build display list traversal because assign-heights is bottom-up). - /// So, our static y offset value is already set. - fn pass_down_static_y_offset(&mut self) { - assert!(self.box_.is_some(), - "All BlockFlows have a box_ in the current implementation"); - for box_ in self.box_.iter() { - let parent_is_positioned = self.is_positioned(); - // Static offset to the top outer edge of this flow - let parent_abs_cb_static_y_offset = self.absolute_cb_details.static_y_offset; - let parent_fixed_cb_static_y_offset = self.fixed_cb_details.static_y_offset; - let kid_fixed_cb_height = self.fixed_cb_details.height; - let mut kid_abs_cb_height; - let parent_top_margin_edge = box_.border_box.get().origin.y - box_.margin.get().top; - let parent_top_padding_edge = box_.border_box.get().origin.y + box_.border.get().top; - if parent_is_positioned { - // Send in parent as the CB - // Padding box height - kid_abs_cb_height = box_.border_box.get().size.height - - box_.border.get().top - box_.border.get().bottom; - } else { - kid_abs_cb_height = self.absolute_cb_details.height; - } - - for child in self.base.child_iter() { - if child.is_block_flow() { - let child_block = child.as_block(); - child_block.absolute_cb_details.height = kid_abs_cb_height; - child_block.fixed_cb_details.height = kid_fixed_cb_height; - for box_ in child_block.box_.iter() { - let child_top_margin_edge = if child_block.is_absolutely_positioned() { - child_block.get_hypothetical_top_edge() - } else { - // Top margin edge = top border y-coordinate - margin-top - // Note: We don't consider top edge of the - // base.position box directly here because that - // box may include clearance. - let relative_top_margin_edge = box_.border_box.get().origin.y - - box_.margin.get().top; - // In the parent flow's coordinate system - child_block.base.position.origin.y + relative_top_margin_edge - }; - if parent_is_positioned { - // The static y offset will be the distance from the top padding - child_block.absolute_cb_details.static_y_offset = child_top_margin_edge - - parent_top_padding_edge; - } else { - let offset = parent_abs_cb_static_y_offset - + (child_top_margin_edge - parent_top_margin_edge); - child_block.absolute_cb_details.static_y_offset = offset; - } - child_block.fixed_cb_details.static_y_offset = parent_fixed_cb_static_y_offset - + (child_top_margin_edge - parent_top_margin_edge) - } - } - } - } - } - /// Add display items for current block. /// /// Set the absolute position for children after doing any offsetting for @@ -1021,19 +1242,14 @@ impl BlockFlow { if self.is_float() { self.build_display_list_float(builder, container_block_size, dirty, index, lists); - self.pass_down_static_y_offset(); return index; } else if self.is_absolutely_positioned() { self.build_display_list_abs(builder, container_block_size, absolute_cb_abs_position, dirty, index, lists); - // Pass down y-offset after calculating flow box dimensions. - self.pass_down_static_y_offset(); return index; } - self.pass_down_static_y_offset(); - // FIXME: Shouldn't this be the abs_rect _after_ relative positioning? let abs_rect = Rect(self.base.abs_position, self.base.position.size); if !abs_rect.intersects(dirty) { @@ -1115,116 +1331,6 @@ impl BlockFlow { false } - /// Solve the vertical constraint equation for absolute non-replaced elements. - /// - /// CSS Section 10.6.4 - /// Constraint equation: - /// top + bottom + height + margin-top + margin-bottom - /// = absolute containing block height - (vertical padding and border) - /// [aka available_height] - /// - /// Return the solution for the equation. - fn solve_vertical_constraints_abs_position(&self, - height: MaybeAuto, - top_margin: MaybeAuto, - bottom_margin: MaybeAuto, - top: MaybeAuto, - bottom: MaybeAuto, - content_height: Au, - available_height: Au, - static_y_offset: Au) - -> HeightConstraintSolution { - // Distance from the top edge of the Absolute Containing Block to the - // top margin edge of a hypothetical box that would have been the - // first box of the element. - let static_position_top = static_y_offset; - - let (top, bottom, height, margin_top, margin_bottom) = match (top, bottom, height) { - (Auto, Auto, Auto) => { - let margin_top = top_margin.specified_or_zero(); - let margin_bottom = bottom_margin.specified_or_zero(); - let top = static_position_top; - // Now it is the same situation as top Specified and bottom - // and height Auto. - - let height = content_height; - let sum = top + height + margin_top + margin_bottom; - (top, available_height - sum, height, margin_top, margin_bottom) - } - (Specified(top), Specified(bottom), Specified(height)) => { - match (top_margin, bottom_margin) { - (Auto, Auto) => { - let total_margin_val = (available_height - top - bottom - height); - (top, bottom, height, - total_margin_val.scale_by(0.5), - total_margin_val.scale_by(0.5)) - } - (Specified(margin_top), Auto) => { - let sum = top + bottom + height + margin_top; - (top, bottom, height, margin_top, available_height - sum) - } - (Auto, Specified(margin_bottom)) => { - let sum = top + bottom + height + margin_bottom; - (top, bottom, height, available_height - sum, margin_bottom) - } - (Specified(margin_top), Specified(margin_bottom)) => { - // Values are over-constrained. Ignore value for 'bottom'. - let sum = top + height + margin_top + margin_bottom; - (top, available_height - sum, height, margin_top, margin_bottom) - } - } - } - - // For the rest of the cases, auto values for margin are set to 0 - - // If only one is Auto, solve for it - (Auto, Specified(bottom), Specified(height)) => { - let margin_top = top_margin.specified_or_zero(); - let margin_bottom = bottom_margin.specified_or_zero(); - let sum = bottom + height + margin_top + margin_bottom; - (available_height - sum, bottom, height, margin_top, margin_bottom) - } - (Specified(top), Auto, Specified(height)) => { - let margin_top = top_margin.specified_or_zero(); - let margin_bottom = bottom_margin.specified_or_zero(); - let sum = top + height + margin_top + margin_bottom; - (top, available_height - sum, height, margin_top, margin_bottom) - } - (Specified(top), Specified(bottom), Auto) => { - let margin_top = top_margin.specified_or_zero(); - let margin_bottom = bottom_margin.specified_or_zero(); - let sum = top + bottom + margin_top + margin_bottom; - (top, bottom, available_height - sum, margin_top, margin_bottom) - } - - // If height is auto, then height is content height. Solve for the - // non-auto value. - (Specified(top), Auto, Auto) => { - let margin_top = top_margin.specified_or_zero(); - let margin_bottom = bottom_margin.specified_or_zero(); - let height = content_height; - let sum = top + height + margin_top + margin_bottom; - (top, available_height - sum, height, margin_top, margin_bottom) - } - (Auto, Specified(bottom), Auto) => { - let margin_top = top_margin.specified_or_zero(); - let margin_bottom = bottom_margin.specified_or_zero(); - let height = content_height; - let sum = bottom + height + margin_top + margin_bottom; - (available_height - sum, bottom, height, margin_top, margin_bottom) - } - - (Auto, Auto, Specified(height)) => { - let margin_top = top_margin.specified_or_zero(); - let margin_bottom = bottom_margin.specified_or_zero(); - let top = static_position_top; - let sum = top + height + margin_top + margin_bottom; - (top, available_height - sum, height, margin_top, margin_bottom) - } - }; - HeightConstraintSolution::new(top, bottom, height, margin_top, margin_bottom) - } - /// Calculate and set the height, offsets, etc. for absolutely positioned flow. /// /// The layout for its in-flow children has been done during normal layout. @@ -1232,9 +1338,9 @@ impl BlockFlow { /// + height for the flow /// + y-coordinate of the flow wrt its Containing Block. /// + height, vertical margins, and y-coordinate for the flow's box. - fn calculate_abs_height_and_margins(&mut self) { - let absolute_cb = self.containing_block(); - let containing_block_height = absolute_cb.height; + fn calculate_abs_height_and_margins(&mut self, ctx: &LayoutContext) { + let containing_block_height = self.containing_block_size(ctx.screen_size).height; + let static_y_offset = self.static_y_offset; for box_ in self.box_.iter() { // This is the stored content height value from assign-height @@ -1250,16 +1356,17 @@ impl BlockFlow { let (top, bottom) = (MaybeAuto::from_style(style.PositionOffsets.get().top, containing_block_height), MaybeAuto::from_style(style.PositionOffsets.get().bottom, containing_block_height)); - let available_height = containing_block_height - box_.border_and_padding_vertical(); - - let solution = self.solve_vertical_constraints_abs_position(height_used_val, - margin_top, - margin_bottom, - top, - bottom, - content_height, - available_height, - absolute_cb.static_y_offset); + let available_height = containing_block_height - box_.border_and_padding_vert(); + + let solution = HeightConstraintSolution::solve_vertical_constraints_abs_position( + height_used_val, + margin_top, + margin_bottom, + top, + bottom, + content_height, + available_height, + static_y_offset); let mut margin = box_.margin.get(); margin.top = solution.margin_top; @@ -1269,7 +1376,7 @@ impl BlockFlow { let mut position = box_.border_box.get(); position.origin.y = box_.margin.get().top; // Border box height - let border_and_padding = box_.border_and_padding_vertical(); + let border_and_padding = box_.border_and_padding_vert(); position.size.height = solution.height + border_and_padding; box_.border_box.set(position); @@ -1280,25 +1387,17 @@ impl BlockFlow { } /// Add display items for Absolutely Positioned flow. - /// - /// Note: We calculate the actual dimensions of the Absolute Flow here, - /// because only now do we have access to the used value for the - /// Containing Block's height. We couldn't do this during assign-heights - /// because it was a bottom-up traversal. - /// This is ok because absolute flows are out-of-flow and nothing else - /// depends on their height, etc. pub fn build_display_list_abs( &mut self, builder: &DisplayListBuilder, _: &Size2D, absolute_cb_abs_position: Point2D, dirty: &Rect, - index: uint, + mut index: uint, lists: &RefCell>) -> bool { - self.calculate_abs_height_and_margins(); - let flow_origin = if self.is_fixed() { + // The viewport is initially at (0, 0). self.base.position.origin } else { // Absolute position of Containing Block + position of absolute flow @@ -1306,6 +1405,13 @@ impl BlockFlow { absolute_cb_abs_position + self.base.position.origin }; + if self.is_fixed() { + lists.with_mut(|lists| { + index = lists.lists.len(); + lists.add_list(DisplayList::::new()); + }); + } + // Set the absolute position, which will be passed down later as part // of containing block details for absolute descendants. self.base.abs_position = flow_origin; @@ -1329,6 +1435,8 @@ impl BlockFlow { /// Return the top outer edge of the Hypothetical Box for an absolute flow. /// + /// This is wrt its parent flow box. + /// /// During normal layout assign-height, the absolute flow's position is /// roughly set to its static position (the position it would have had in /// the normal flow). @@ -1346,6 +1454,10 @@ impl Flow for BlockFlow { self } + fn is_store_overflow_delayed(&mut self) -> bool { + self.is_absolutely_positioned() + } + /* Recursively (bottom-up) determine the context's preferred and minimum widths. When called on this context, all child contexts have had their min/pref widths set. This function must decide @@ -1406,24 +1518,13 @@ impl Flow for BlockFlow { "block" }); - if self.is_root { + if self.is_root() { debug!("Setting root position"); self.base.position.origin = Au::zero_point(); self.base.position.size.width = ctx.screen_size.width; self.base.floats = Floats::new(); // Root element is not floated self.base.flags_info.flags.set_inorder(false); - // Initial containing block is the CB for the root - let initial_cb = ContainingBlockDetails { - width: ctx.screen_size.width, - height: ctx.screen_size.height, - // The left margin edge of the root touches the viewport (which is its CB). - static_x_offset: Au::new(0), - // The top margin edge of the root touches the viewport (which is its CB). - static_y_offset: Au::new(0), - }; - self.absolute_cb_details = initial_cb.clone(); - self.fixed_cb_details = initial_cb; } // The position was set to the containing block by the flow's parent. @@ -1438,7 +1539,7 @@ impl Flow for BlockFlow { self.base.flags_info.flags.set_inorder(false); } - self.calculate_widths_and_margins(containing_block_width); + self.calculate_widths_and_margins(containing_block_width, ctx); for box_ in self.box_.iter() { // Move in from the left border edge @@ -1459,15 +1560,11 @@ impl Flow for BlockFlow { self.base.flags_info.flags.inorder() || self.base.num_floats > 0 }; - let kid_abs_cb_width; let kid_abs_cb_x_offset; if self.is_positioned() { match self.box_ { Some(ref box_) => { // Pass yourself as a new Containing Block - // Padding box width - kid_abs_cb_width = box_.border_box.get().size.width - box_.border.get().left - - box_.border.get().right; // The static x offset for any immediate kid flows will be the // left padding kid_abs_cb_x_offset = box_.padding.get().left; @@ -1475,15 +1572,12 @@ impl Flow for BlockFlow { None => fail!("BlockFlow: no principal box found"), } } else { - // Pass the original Containing Block, with updated static offset - kid_abs_cb_width = self.absolute_cb_details.width; // For kids, the left margin edge will be at our left content edge. // The current static offset is at our left margin // edge. So move in to the left content edge. - kid_abs_cb_x_offset = self.absolute_cb_details.static_x_offset + left_content_edge; + kid_abs_cb_x_offset = self.base.absolute_static_x_offset + left_content_edge; } - let kid_fixed_cb_width = self.fixed_cb_details.width; - let kid_fixed_cb_x_offset = self.fixed_cb_details.static_x_offset + left_content_edge; + let kid_fixed_cb_x_offset = self.base.fixed_static_x_offset + left_content_edge; // FIXME(ksh8281): avoid copy let flags_info = self.base.flags_info.clone(); @@ -1492,10 +1586,8 @@ impl Flow for BlockFlow { if kid.is_block_flow() { let kid_block = kid.as_block(); - kid_block.absolute_cb_details.width = kid_abs_cb_width; - kid_block.absolute_cb_details.static_x_offset = kid_abs_cb_x_offset; - kid_block.fixed_cb_details.width = kid_fixed_cb_width; - kid_block.fixed_cb_details.static_x_offset = kid_fixed_cb_x_offset; + kid_block.base.absolute_static_x_offset = kid_abs_cb_x_offset; + kid_block.base.fixed_static_x_offset = kid_fixed_cb_x_offset; } let child_base = flow::mut_base(kid); // Left margin edge of kid flow is at our left content edge @@ -1544,7 +1636,7 @@ impl Flow for BlockFlow { debug!("assign_height: assigning height for block"); // This is the only case in which a block flow can start an inorder // subtraversal. - if self.is_root && self.base.num_floats > 0 { + if self.is_root() && self.base.num_floats > 0 { self.assign_height_inorder(ctx); return; } @@ -1597,10 +1689,64 @@ impl Flow for BlockFlow { self.is_root = true } + /// Return true if store overflow is delayed for this flow. + /// + /// Currently happens only for absolutely positioned flows. + fn is_store_overflow_delayed(&mut self) -> bool { + self.is_absolutely_positioned() + } + + fn is_root(&self) -> bool { + self.is_root + } + + fn is_float(&self) -> bool { + self.float.is_some() + } + + /// The 'position' property of this flow. + fn positioning(&self) -> position::T { + match self.box_ { + Some(ref box_) => { + box_.style.get().Box.get().position + } + None => fail!("BlockFlow does not have a box_") + } + } + + /// Return true if this is the root of an Absolute flow tree. + /// + /// It has to be either relatively positioned or the Root flow. + fn is_root_of_absolute_flow_tree(&self) -> bool { + self.is_relatively_positioned() || self.is_root() + } + + /// Return the dimensions of the CB generated _by_ this flow for absolute descendants. + /// + /// For Blocks, this will be the padding box. + fn generated_cb_size(&self) -> Size2D { + match self.box_ { + Some(ref box_) => { + box_.padding_box_size() + } + None => fail!("Containing Block must have a box") + } + } + + /// Return position of the CB generated by this flow from the start of this flow. + fn generated_cb_position(&self) -> Point2D { + match self.box_ { + Some(ref box_) => { + // Border box y coordinate + border top + box_.border_box.get().origin + Point2D(box_.border.get().left, box_.border.get().top)} + None => fail!("Containing Block must have a box") + } + } + fn debug_str(&self) -> ~str { let txt = if self.is_float() { ~"FloatFlow: " - } else if self.is_root { + } else if self.is_root() { ~"RootFlow: " } else { ~"BlockFlow: " diff --git a/src/components/main/layout/box_.rs b/src/components/main/layout/box_.rs index 6bebd15b5bca..7b8062e5dfc6 100644 --- a/src/components/main/layout/box_.rs +++ b/src/components/main/layout/box_.rs @@ -605,12 +605,18 @@ impl Box { specified(padding, content_box_width) } + pub fn padding_box_size(&self) -> Size2D { + let border_box_size = self.border_box.get().size; + Size2D(border_box_size.width - self.border.get().left - self.border.get().right, + border_box_size.height - self.border.get().top - self.border.get().bottom) + } + pub fn border_and_padding_horiz(&self) -> Au { self.border.get().left + self.border.get().right + self.padding.get().left + self.padding.get().right } - pub fn border_and_padding_vertical(&self) -> Au { + pub fn border_and_padding_vert(&self) -> Au { self.border.get().top + self.border.get().bottom + self.padding.get().top + self.padding.get().bottom } @@ -989,7 +995,7 @@ impl Box { /// Arguments: /// * `builder`: The display list builder, which manages the coordinate system and options. /// * `dirty`: The dirty rectangle in the coordinate system of the owning flow. - /// * `origin`: The total offset from the display list root flow to the owning flow of this + /// * `flow_origin`: Position of the origin of the owning flow wrt the display list root flow. /// box. /// * `list`: The display list to which items should be appended. /// @@ -1002,15 +1008,16 @@ impl Box { &self, builder: &DisplayListBuilder, dirty: &Rect, - offset: Point2D, + flow_origin: Point2D, flow: &Flow, index: uint, lists: &RefCell>) { + // Box position wrt to the owning flow. let box_bounds = self.border_box.get(); - let absolute_box_bounds = box_bounds.translate(&offset); + let absolute_box_bounds = box_bounds.translate(&flow_origin); debug!("Box::build_display_list at rel={}, abs={}: {:s}", box_bounds, absolute_box_bounds, self.debug_str()); - debug!("Box::build_display_list: dirty={}, offset={}", *dirty, offset); + debug!("Box::build_display_list: dirty={}, flow_origin={}", *dirty, flow_origin); if self.style().InheritedBox.get().visibility != visibility::visible { return; @@ -1023,10 +1030,15 @@ impl Box { return; } - self.paint_inline_background_border_if_applicable(index, lists, &absolute_box_bounds, &offset); + self.paint_inline_background_border_if_applicable(index, lists, &absolute_box_bounds, &flow_origin); // Add the background to the list, if applicable. self.paint_background_if_applicable(builder, index, lists, &absolute_box_bounds); + // Add a border, if applicable. + // + // TODO: Outlines. + self.paint_borders_if_applicable(index, lists, &absolute_box_bounds); + match self.specific { UnscannedTextBox(_) => fail!("Shouldn't see unscanned boxes here."), ScannedTextBox(ref text_box) => { @@ -1221,15 +1233,11 @@ impl Box { // iframe is actually going to be displayed. match self.specific { IframeBox(ref iframe_box) => { - self.finalize_position_and_size_of_iframe(iframe_box, offset, builder.ctx) + self.finalize_position_and_size_of_iframe(iframe_box, flow_origin, builder.ctx) } GenericBox | ImageBox(_) | ScannedTextBox(_) | UnscannedTextBox(_) => {} } - // Add a border, if applicable. - // - // TODO: Outlines. - self.paint_borders_if_applicable(index, lists, &absolute_box_bounds); } /// Returns the *minimum width* and *preferred width* of this box as defined by CSS 2.1. diff --git a/src/components/main/layout/construct.rs b/src/components/main/layout/construct.rs index d7da3a11592c..745ee21c1738 100644 --- a/src/components/main/layout/construct.rs +++ b/src/components/main/layout/construct.rs @@ -28,6 +28,8 @@ use layout::box_::{UnscannedTextBoxInfo}; use layout::context::LayoutContext; use layout::floats::FloatKind; use layout::flow::{Flow, MutableOwnedFlowUtils}; +use layout::flow::{Descendants, AbsDescendants, FixedDescendants}; +use layout::flow_list::{Rawlink}; use layout::inline::InlineFlow; use layout::text::TextRunScanner; use layout::util::{LayoutDataAccess, OpaqueNode}; @@ -59,9 +61,10 @@ pub enum ConstructionResult { /// created nodes have their `ConstructionResult` set to. NoConstructionResult, - /// This node contributed a flow at the proper position in the tree. Nothing more needs to be - /// done for this node. - FlowConstructionResult(~Flow), + /// This node contributed a flow at the proper position in the tree. + /// Nothing more needs to be done for this node. It has bubbled up fixed + /// and absolute descendant flows that have a CB above it. + FlowConstructionResult(~Flow, AbsDescendants, FixedDescendants), /// This node contributed some object or objects that will be needed to construct a proper flow /// later up the tree, but these objects have not yet found their home. @@ -72,7 +75,7 @@ impl ConstructionResult { fn destroy(&mut self) { match *self { NoConstructionResult => {} - FlowConstructionResult(ref mut flow) => flow.destroy(), + FlowConstructionResult(ref mut flow, _, _) => flow.destroy(), ConstructionItemConstructionResult(ref mut item) => item.destroy(), } } @@ -112,6 +115,12 @@ struct InlineBoxesConstructionResult { /// Any boxes that succeed the {ib} splits. boxes: ~[Box], + + /// Any absolute descendants that we're bubbling up. + abs_descendants: AbsDescendants, + + /// Any fixed descendants that we're bubbling up. + fixed_descendants: FixedDescendants, } /// Represents an {ib} split that has not yet found the containing block that it belongs to. This @@ -155,7 +164,7 @@ impl InlineBlockSplit { /// Methods on optional vectors. /// /// TODO(pcwalton): I think this will no longer be necessary once Rust #8981 lands. -trait OptVector { +pub trait OptVector { /// Turns this optional vector into an owned one. If the optional vector is `None`, then this /// simply returns an empty owned vector. fn to_vec(self) -> ~[T]; @@ -316,17 +325,27 @@ impl<'a> FlowConstructor<'a> { } } - /// Builds the children flows underneath a node with `display: block`. After this call, - /// other `BlockFlow`s or `InlineFlow`s will be populated underneath this node, depending on - /// whether {ib} splits needed to happen. - fn build_children_of_block_flow(&mut self, flow: &mut ~Flow, node: &ThreadSafeLayoutNode) { + /// Build block flow for current node using information from children nodes. + /// + /// Consume results from children and combine them, handling {ib} splits. + /// Block flows and inline flows thus created will become the children of + /// this block flow. + /// Also, deal with the absolute and fixed descendants bubbled up by + /// children nodes. + fn build_block_flow_using_children(&mut self, + mut flow: ~Flow, + node: &ThreadSafeLayoutNode) + -> ConstructionResult { // Gather up boxes for the inline flows we might need to create. let mut opt_boxes_for_inline_flow = None; let mut first_box = true; + // List of absolute descendants, in tree order. + let mut abs_descendants = Descendants::new(); + let mut fixed_descendants = Descendants::new(); for kid in node.children() { match kid.swap_out_construction_result() { NoConstructionResult => {} - FlowConstructionResult(kid_flow) => { + FlowConstructionResult(kid_flow, kid_abs_descendants, kid_fixed_descendants) => { // Strip ignorable whitespace from the start of this flow per CSS 2.1 § // 9.2.1.1. if first_box { @@ -340,14 +359,19 @@ impl<'a> FlowConstructor<'a> { opt_boxes_for_inline_flow.as_ref() .map_default(0, |boxes| boxes.len())); self.flush_inline_boxes_to_flow_if_necessary(&mut opt_boxes_for_inline_flow, - flow, + &mut flow, node); - flow.add_new_child(kid_flow) + flow.add_new_child(kid_flow); + abs_descendants.push_descendants(kid_abs_descendants); + fixed_descendants.push_descendants(kid_fixed_descendants); + } ConstructionItemConstructionResult(InlineBoxesConstructionItem( InlineBoxesConstructionResult { splits: opt_splits, - boxes: boxes + boxes: boxes, + abs_descendants: kid_abs_descendants, + fixed_descendants: kid_fixed_descendants, })) => { // Add any {ib} splits. match opt_splits { @@ -377,7 +401,7 @@ impl<'a> FlowConstructor<'a> { |boxes| boxes.len())); self.flush_inline_boxes_to_flow_if_necessary( &mut opt_boxes_for_inline_flow, - flow, + &mut flow, node); // Push the flow generated by the {ib} split onto our list of @@ -388,7 +412,9 @@ impl<'a> FlowConstructor<'a> { } // Add the boxes to the list we're maintaining. - opt_boxes_for_inline_flow.push_all_move(boxes) + opt_boxes_for_inline_flow.push_all_move(boxes); + abs_descendants.push_descendants(kid_abs_descendants); + fixed_descendants.push_descendants(kid_fixed_descendants); } ConstructionItemConstructionResult(WhitespaceConstructionItem(..)) => { // Nothing to do here. @@ -400,30 +426,45 @@ impl<'a> FlowConstructor<'a> { // splits, after stripping ignorable whitespace. strip_ignorable_whitespace_from_end(&mut opt_boxes_for_inline_flow); self.flush_inline_boxes_to_flow_if_necessary(&mut opt_boxes_for_inline_flow, - flow, + &mut flow, node); - // The flow is done. If it ended up with no kids, add the flow to the leaf set. - flow.finish(self.layout_context) + // The flow is done. + flow.finish(self.layout_context); + let is_positioned = flow.as_block().is_positioned(); + let is_fixed_positioned = flow.as_block().is_fixed(); + let is_absolutely_positioned = flow.as_block().is_absolutely_positioned(); + if is_positioned { + // This is the CB for all the absolute descendants. + flow.set_abs_descendants(abs_descendants); + abs_descendants = Descendants::new(); + + if is_fixed_positioned { + // Send itself along with the other fixed descendants. + fixed_descendants.push(Rawlink::some(flow)); + } else if is_absolutely_positioned { + // This is now the only absolute flow in the subtree which hasn't yet + // reached its CB. + abs_descendants.push(Rawlink::some(flow)); + } + } + FlowConstructionResult(flow, abs_descendants, fixed_descendants) } /// Builds a flow for a node with `display: block`. This yields a `BlockFlow` with possibly /// other `BlockFlow`s or `InlineFlow`s underneath it, depending on whether {ib} splits needed /// to happen. - fn build_flow_for_block(&mut self, node: &ThreadSafeLayoutNode, positioning: position::T) - -> ~Flow { - let mut flow = ~BlockFlow::from_node(self, node, positioning) as ~Flow; - self.build_children_of_block_flow(&mut flow, node); - flow + fn build_flow_for_block(&mut self, node: &ThreadSafeLayoutNode) -> ConstructionResult { + let flow = ~BlockFlow::from_node(self, node) as ~Flow; + self.build_block_flow_using_children(flow, node) } /// Builds the flow for a node with `float: {left|right}`. This yields a float `BlockFlow` with /// a `BlockFlow` underneath it. fn build_flow_for_floated_block(&mut self, node: &ThreadSafeLayoutNode, float_kind: FloatKind) - -> ~Flow { - let mut flow = ~BlockFlow::float_from_node(self, node, float_kind) as ~Flow; - self.build_children_of_block_flow(&mut flow, node); - flow + -> ConstructionResult { + let flow = ~BlockFlow::float_from_node(self, node, float_kind) as ~Flow; + self.build_block_flow_using_children(flow, node) } @@ -434,24 +475,30 @@ impl<'a> FlowConstructor<'a> { -> ConstructionResult { let mut opt_inline_block_splits = None; let mut opt_box_accumulator = None; + let mut abs_descendants = Descendants::new(); + let mut fixed_descendants = Descendants::new(); // Concatenate all the boxes of our kids, creating {ib} splits as necessary. for kid in node.children() { match kid.swap_out_construction_result() { NoConstructionResult => {} - FlowConstructionResult(flow) => { + FlowConstructionResult(flow, kid_abs_descendants, kid_fixed_descendants) => { // {ib} split. Flush the accumulator to our new split and make a new // accumulator to hold any subsequent boxes we come across. let split = InlineBlockSplit { predecessor_boxes: util::replace(&mut opt_box_accumulator, None).to_vec(), flow: flow, }; - opt_inline_block_splits.push(split) + opt_inline_block_splits.push(split); + abs_descendants.push_descendants(kid_abs_descendants); + fixed_descendants.push_descendants(kid_fixed_descendants); } ConstructionItemConstructionResult(InlineBoxesConstructionItem( InlineBoxesConstructionResult { splits: opt_splits, - boxes: boxes + boxes: boxes, + abs_descendants: kid_abs_descendants, + fixed_descendants: kid_fixed_descendants, })) => { // Bubble up {ib} splits. @@ -476,7 +523,9 @@ impl<'a> FlowConstructor<'a> { } // Push residual boxes. - opt_box_accumulator.push_all_move(boxes) + opt_box_accumulator.push_all_move(boxes); + abs_descendants.push_descendants(kid_abs_descendants); + fixed_descendants.push_descendants(kid_fixed_descendants); } ConstructionItemConstructionResult(WhitespaceConstructionItem(whitespace_node, whitespace_style)) @@ -534,10 +583,14 @@ impl<'a> FlowConstructor<'a> { } // Finally, make a new construction result. - if opt_inline_block_splits.len() > 0 || opt_box_accumulator.len() > 0 { + if opt_inline_block_splits.len() > 0 || opt_box_accumulator.len() > 0 + || abs_descendants.len() > 0 { + let construction_item = InlineBoxesConstructionItem(InlineBoxesConstructionResult { splits: opt_inline_block_splits, boxes: opt_box_accumulator.to_vec(), + abs_descendants: abs_descendants, + fixed_descendants: fixed_descendants, }); ConstructionItemConstructionResult(construction_item) } else { @@ -613,6 +666,8 @@ impl<'a> FlowConstructor<'a> { boxes: ~[ Box::new(self, node) ], + abs_descendants: Descendants::new(), + fixed_descendants: Descendants::new(), }); ConstructionItemConstructionResult(construction_item) } @@ -663,6 +718,10 @@ impl<'a> PostorderNodeMutTraversal for FlowConstructor<'a> { } } + (_, _, position::absolute) | (_, _, position::fixed) => { + node.set_flow_construction_result(self.build_flow_for_block(node)) + } + // Inline items contribute inline box construction results. (display::inline, float::none, _) => { let construction_result = self.build_boxes_for_inline(node); @@ -674,20 +733,15 @@ impl<'a> PostorderNodeMutTraversal for FlowConstructor<'a> { // TODO(pcwalton): Make this only trigger for blocks and handle the other `display` // properties separately. - (_, _, position::absolute) | (_, _, position::fixed) => { - let flow = self.build_flow_for_block(node, positioning); - node.set_flow_construction_result(FlowConstructionResult(flow)) - } (_, float::none, _) => { - let flow = self.build_flow_for_block(node, positioning); - node.set_flow_construction_result(FlowConstructionResult(flow)) + node.set_flow_construction_result(self.build_flow_for_block(node)) } // Floated flows contribute float flow construction results. (_, float_value, _) => { let float_kind = FloatKind::from_property(float_value); - let flow = self.build_flow_for_floated_block(node, float_kind); - node.set_flow_construction_result(FlowConstructionResult(flow)) + node.set_flow_construction_result( + self.build_flow_for_floated_block(node, float_kind)) } } diff --git a/src/components/main/layout/flow.rs b/src/components/main/layout/flow.rs index 182a6ccb91bc..d8abda6d7e89 100644 --- a/src/components/main/layout/flow.rs +++ b/src/components/main/layout/flow.rs @@ -29,6 +29,7 @@ use css::node_style::StyledNode; use layout::block::{BlockFlow}; use layout::box_::Box; use layout::context::LayoutContext; +use layout::construct::OptVector; use layout::display_list_builder::{DisplayListBuilder, ExtraDisplayListData}; use layout::floats::Floats; use layout::incremental::RestyleDamage; @@ -45,12 +46,15 @@ use geom::rect::Rect; use gfx::display_list::{ClipDisplayItemClass, DisplayListCollection, DisplayList}; use layout::display_list_builder::ToGfxColor; use gfx::color::Color; +use servo_util::smallvec::{SmallVec, SmallVec0}; use servo_util::geometry::Au; use std::cast; use std::cell::RefCell; use std::sync::atomics::Relaxed; +use std::vec::VecMutIterator; +use std::iter::Zip; use style::ComputedValues; -use style::computed_values::text_align; +use style::computed_values::{text_align, position}; /// Virtual methods that make up a float context. /// @@ -116,6 +120,63 @@ pub trait Flow { /// Marks this flow as the root flow. The default implementation is a no-op. fn mark_as_root(&mut self) {} + // Note that the following functions are mostly called using static method + // dispatch, so it's ok to have them in this trait. Plus, they have + // different behaviour for different types of Flow, so they can't go into + // the Immutable / Mutable Flow Utils traits without additional casts. + + /// Return true if store overflow is delayed for this flow. + /// + /// Currently happens only for absolutely positioned flows. + fn is_store_overflow_delayed(&mut self) -> bool { + false + } + + fn is_root(&self) -> bool { + false + } + + fn is_float(&self) -> bool { + false + } + + /// The 'position' property of this flow. + fn positioning(&self) -> position::T { + position::static_ + } + + /// Return true if this flow has position 'fixed'. + fn is_fixed(&self) -> bool { + self.positioning() == position::fixed + } + + fn is_positioned(&self) -> bool { + self.is_relatively_positioned() || self.is_absolutely_positioned() + } + + fn is_relatively_positioned(&self) -> bool { + self.positioning() == position::relative + } + + fn is_absolutely_positioned(&self) -> bool { + self.positioning() == position::absolute || self.is_fixed() + } + + /// Return true if this is the root of an Absolute flow tree. + fn is_root_of_absolute_flow_tree(&self) -> bool { + false + } + + /// Return the dimensions of the CB generated _by_ this flow for absolute descendants. + fn generated_cb_size(&self) -> Size2D { + fail!("generated_cb_size not yet implemented") + } + + /// Return position of the CB generated by this flow from the start of this flow. + fn generated_cb_position(&self) -> Point2D { + fail!("this is not the CB-generating flow you're looking for") + } + /// Returns a debugging string describing this flow. fn debug_str(&self) -> ~str { ~"???" @@ -231,6 +292,16 @@ pub trait MutableOwnedFlowUtils { /// properly computed. (This is not, however, a memory safety problem.) fn finish(&mut self, context: &mut LayoutContext); + /// Set absolute descendants for this flow. + /// + /// Set this flow as the Containing Block for all the absolute descendants. + fn set_abs_descendants(&mut self, abs_descendants: AbsDescendants); + + /// Set fixed descendants for this flow. + /// + /// Set yourself as the Containing Block for all the fixed descendants. + fn set_fixed_descendants(&mut self, fixed_descendants: AbsDescendants); + /// Destroys the flow. fn destroy(&mut self); } @@ -484,6 +555,61 @@ impl FlowFlags { } } +/// The Descendants of a flow. +/// +/// Also, details about their position wrt this flow. +/// FIXME: This should use @pcwalton's reference counting scheme (Coming Soon). +pub struct Descendants { + /// Links to every Descendant. + descendant_links: SmallVec0, + /// Static y offsets of all descendants from the start of this flow box. + static_y_offsets: SmallVec0, +} + +impl Descendants { + pub fn new() -> Descendants { + Descendants { + descendant_links: SmallVec0::new(), + static_y_offsets: SmallVec0::new(), + } + } + + pub fn len(&self) -> uint { + self.descendant_links.len() + } + + pub fn push(&mut self, given_descendant: Rawlink) { + self.descendant_links.push(given_descendant); + } + + /// Push the given descendants on to the existing descendants. + /// + /// Ignore any static y offsets, because they are None before layout. + pub fn push_descendants(&mut self, mut given_descendants: Descendants) { + for elem in given_descendants.descendant_links.move_iter() { + self.descendant_links.push(elem); + } + } + + /// Return an iterator over the descendant flows. + pub fn iter<'a>(&'a mut self) -> DescendantIter<'a> { + self.descendant_links.mut_slice_from(0).mut_iter() + } + + /// Return an iterator over (descendant, static y offset). + pub fn iter_with_offset<'a>(&'a mut self) -> DescendantOffsetIter<'a> { + self.descendant_links.mut_slice_from(0).mut_iter().zip( + self.static_y_offsets.mut_slice_from(0).mut_iter()) + } +} + +pub type AbsDescendants = Descendants; +pub type FixedDescendants = Descendants; + +type DescendantIter<'a> = VecMutIterator<'a, Rawlink>; + +type DescendantOffsetIter<'a> = Zip, VecMutIterator<'a, Au>>; + /// Data common to all flows. pub struct BaseFlow { restyle_damage: RestyleDamage, @@ -526,6 +652,22 @@ pub struct BaseFlow { /// The position of this flow in page coordinates, computed during display list construction. abs_position: Point2D, + /// Details about descendants with position 'absolute' for which we are + /// the CB. This is in tree order. This includes any direct children. + abs_descendants: AbsDescendants, + /// Details about descendants with position 'fixed'. + /// TODO: Optimize this, because this will be set only for the root. + fixed_descendants: FixedDescendants, + + /// Offset wrt the nearest positioned ancestor - aka the Containing Block + /// for any absolutely positioned elements. + absolute_static_x_offset: Au, + /// Offset wrt the Initial Containing Block. + fixed_static_x_offset: Au, + + /// Reference to the Containing Block, if this flow is absolutely positioned. + absolute_cb: Rawlink, + /// Whether this flow has been destroyed. /// /// TODO(pcwalton): Pack this into the flags? Need to be careful because manipulation of this @@ -582,6 +724,11 @@ impl BaseFlow { floats: Floats::new(), num_floats: 0, abs_position: Point2D(Au::new(0), Au::new(0)), + abs_descendants: Descendants::new(), + fixed_descendants: Descendants::new(), + absolute_static_x_offset: Au::new(0), + fixed_static_x_offset: Au::new(0), + absolute_cb: Rawlink::none(), destroyed: false, @@ -716,25 +863,53 @@ impl<'a> MutableFlowUtils for &'a mut Flow { /// Calculate and set overflow for current flow. /// /// CSS Section 11.1 - /// This is ideally the union of all flows for which we define the + /// This is the union of rectangles of the flows for which we define the /// Containing Block. /// /// Assumption: This is called in a bottom-up traversal, so kids' overflows have /// already been set. - /// So, currently this is a union of the overflows of all kids and our own - /// flow rectangle. - /// FIXME: Handle the overflow of absolute flow descendants, because their - /// assign-heights happen after the normal - /// assign-height-and-store-overflow traversal + /// Assumption: Absolute descendants have had their overflow calculated. fn store_overflow(self, _: &mut LayoutContext) { let my_position = mut_base(self).position; let mut overflow = my_position; - for kid in mut_base(self).child_iter() { - let mut kid_overflow = base(kid).overflow; - kid_overflow = kid_overflow.translate(&my_position.origin); - overflow = overflow.union(&kid_overflow) + + if self.is_block_container() { + for kid in child_iter(self) { + if kid.is_store_overflow_delayed() { + // Absolute flows will be handled by their CB. If we are + // their CB, they will show up in `abs_descendants`. + continue; + } + let mut kid_overflow = base(kid).overflow; + kid_overflow = kid_overflow.translate(&my_position.origin); + overflow = overflow.union(&kid_overflow) + } + + for descendant_link in mut_base(self).abs_descendants.iter() { + match descendant_link.resolve() { + Some(flow) => { + let mut kid_overflow = base(flow).overflow; + kid_overflow = kid_overflow.translate(&my_position.origin); + overflow = overflow.union(&kid_overflow) + } + None => fail!("empty Rawlink to a descendant") + } + } + + if self.is_root() { + for fixed_descendant_link in mut_base(self).fixed_descendants.iter() { + match fixed_descendant_link.resolve() { + Some(flow) => { + let mut kid_overflow = base(flow).overflow; + kid_overflow = kid_overflow.translate(&my_position.origin); + overflow = overflow.union(&kid_overflow) + } + None => fail!("empty Rawlink to a descendant") + } + } + } } - mut_base(self).overflow = overflow + mut_base(self).overflow = overflow; } /// Push display items for current flow and its children onto `list`. @@ -773,23 +948,21 @@ impl<'a> MutableFlowUtils for &'a mut Flow { } if self.is_block_container() { + let block = self.as_block(); let mut child_lists = DisplayListCollection::new(); child_lists.add_list(DisplayList::new()); let child_lists = RefCell::new(child_lists); - let is_positioned = self.as_block().is_positioned(); let container_block_size; let abs_cb_position; - let flow_pos = base(self).abs_position; - match self.as_block().box_ { + // TODO(pradeep): Move this into a generated CB function and stuff in Flow. + match block.box_ { Some(ref box_) => { // FIXME: This should be the size of the content box (which is the // Containing Block formed by a BlockFlow), not the border box. container_block_size = box_.border_box.get().size; - abs_cb_position = if is_positioned { - let padding_box_pos = flow_pos + box_.border_box.get().origin - + Point2D(box_.border.get().left, box_.border.get().top); - padding_box_pos + abs_cb_position = if block.is_positioned() { + block.base.abs_position + block.generated_cb_position() } else { absolute_cb_abs_position }; @@ -797,12 +970,48 @@ impl<'a> MutableFlowUtils for &'a mut Flow { None => fail!("Flow: block container should have a box_") } - for kid in child_iter(self) { + for kid in block.base.child_iter() { + if kid.is_absolutely_positioned() { + // All absolute flows will be handled by their CB. + continue; + } kid.build_display_lists(builder, &container_block_size, abs_cb_position, dirty, 0u, &child_lists); } + // TODO: Maybe we should handle position 'absolute' and 'fixed' + // descendants before normal descendants just in case there is a + // problem when display-list building is parallel and both the + // original parent and this flow access the same absolute flow. + // Note that this can only be done once we have paint order + // working cos currently the later boxes paint over the absolute + // and fixed boxes :| + for abs_descendant_link in block.base.abs_descendants.iter() { + match abs_descendant_link.resolve() { + Some(flow) => { + // TODO(pradeep): Send in your abs_position directly. + flow.build_display_lists(builder, &container_block_size, + abs_cb_position, + dirty, 0u, &child_lists); + } + None => fail!("empty Rawlink to a descendant") + } + } + + if block.is_root() { + for fixed_descendant_link in block.base.fixed_descendants.iter() { + match fixed_descendant_link.resolve() { + Some(flow) => { + flow.build_display_lists(builder, &container_block_size, + abs_cb_position, + dirty, 0u, &child_lists); + } + None => fail!("empty Rawlink to a descendant") + } + } + } + let mut child_lists = Some(child_lists.unwrap()); // Find parent ClipDisplayItemClass and push all child display items // under it @@ -863,6 +1072,51 @@ impl MutableOwnedFlowUtils for ~Flow { } } + /// Set absolute descendants for this flow. + /// + /// Set yourself as the Containing Block for all the absolute descendants. + /// + /// Assumption: This is called in a bottom-up traversal, so that nothing + /// else is accessing the descendant flows. + fn set_abs_descendants(&mut self, abs_descendants: AbsDescendants) { + let self_link = Rawlink::some(*self); + let block = self.as_block(); + block.base.abs_descendants = abs_descendants; + + for descendant_link in block.base.abs_descendants.iter() { + match descendant_link.resolve() { + Some(flow) => { + let base = mut_base(flow); + base.absolute_cb = self_link.clone(); + } + None => fail!("empty Rawlink to a descendant") + } + } + } + + /// Set fixed descendants for this flow. + /// + /// Set yourself as the Containing Block for all the fixed descendants. + /// + /// Assumption: This is called in a bottom-up traversal, so that nothing + /// else is accessing the descendant flows. + /// Assumption: This is the root flow. + fn set_fixed_descendants(&mut self, fixed_descendants: FixedDescendants) { + let self_link = Rawlink::some(*self); + let block = self.as_block(); + block.base.fixed_descendants = fixed_descendants; + + for descendant_link in block.base.fixed_descendants.iter() { + match descendant_link.resolve() { + Some(flow) => { + let base = mut_base(flow); + base.absolute_cb = self_link.clone(); + } + None => fail!("empty Rawlink to a descendant") + } + } + } + /// Destroys the flow. fn destroy(&mut self) { let self_borrowed: &mut Flow = *self; diff --git a/src/components/main/layout/flow_list.rs b/src/components/main/layout/flow_list.rs index 232b718fba81..330bf78c2a8f 100644 --- a/src/components/main/layout/flow_list.rs +++ b/src/components/main/layout/flow_list.rs @@ -19,6 +19,10 @@ pub struct Rawlink { priv obj: *mut (), } +/// Doubly-linked list of Flows. +/// +/// The forward links are strong references. +/// The backward links are weak references. pub struct FlowList { priv length: uint, priv list_head: Link, @@ -51,7 +55,7 @@ impl Rawlink { } /// Like Option::Some for Rawlink - fn some(n: &mut Flow) -> Rawlink { + pub fn some(n: &mut Flow) -> Rawlink { unsafe { cast::transmute(n) } } @@ -65,7 +69,7 @@ impl Rawlink { } } - fn resolve(&mut self) -> Option<&mut Flow> { + pub fn resolve(&mut self) -> Option<&mut Flow> { if self.obj.is_null() { None } else { diff --git a/src/components/main/layout/layout_task.rs b/src/components/main/layout/layout_task.rs index 8a52d93b2843..0fdacec78ade 100644 --- a/src/components/main/layout/layout_task.rs +++ b/src/components/main/layout/layout_task.rs @@ -212,7 +212,11 @@ impl<'a> PostorderFlowTraversal for AssignHeightsAndStoreOverflowTraversal<'a> { #[inline] fn process(&mut self, flow: &mut Flow) -> bool { flow.assign_height(self.layout_context); - flow.store_overflow(self.layout_context); + // Skip store-overflow for absolutely positioned flows. That will be + // done in a separate traversal. + if !flow.is_store_overflow_delayed() { + flow.store_overflow(self.layout_context); + } true } @@ -427,7 +431,17 @@ impl LayoutTask { None => fail!("no layout data for root node"), }; let mut flow = match result { - FlowConstructionResult(flow) => flow, + FlowConstructionResult(mut flow, abs_descendants, fixed_descendants) => { + // Note: Assuming that the root has display 'static' (as per + // CSS Section 9.3.1). Otherwise, if it were absolutely + // positioned, it would return a reference to itself in + // `abs_descendants` and would lead to a circular reference. + // Set Root as CB for any remaining absolute descendants. + flow.set_abs_descendants(abs_descendants); + // Set Root as CB for all fixed descendants. + flow.set_fixed_descendants(fixed_descendants); + flow + } _ => fail!("Flow construction didn't result in a flow at the root of the tree!"), }; flow.mark_as_root(); diff --git a/src/test/ref/basic.list b/src/test/ref/basic.list index c6713e02f909..f30a45531e25 100644 --- a/src/test/ref/basic.list +++ b/src/test/ref/basic.list @@ -21,13 +21,11 @@ # inline_text_align_a.html inline_text_align_b.html == font_size_em.html font_size_em_ref.html == font_size_percentage.html font_size_em_ref.html -== position_fixed_a.html position_fixed_b.html == img_size_a.html img_size_b.html == img_dynamic_remove.html img_dynamic_remove_ref.html == upper_id_attr.html upper_id_attr_ref.html # inline_border_a.html inline_border_b.html == anon_block_inherit_a.html anon_block_inherit_b.html -== position_relative_a.html position_relative_b.html == attr_exists_selector.html attr_exists_selector_ref.html == data_img_a.html data_img_b.html == background_style_attr.html background_ref.html @@ -36,3 +34,13 @@ # == simple_iframe.html simple_iframe_ref.html -- disabled due to iframe crashiness == object_element_a.html object_element_b.html == height_compute_reset.html height_compute.html +# Positioning tests +== position_abs_cb_with_non_cb_kid_a.html position_abs_cb_with_non_cb_kid_b.html +== position_abs_height_width_a.html position_abs_height_width_b.html +== position_abs_left_a.html position_abs_left_b.html +== position_abs_static_y_a.html position_abs_static_y_b.html +== position_abs_width_percentage_a.html position_abs_width_percentage_b.html +== position_fixed_a.html position_fixed_b.html +== position_fixed_simple_a.html position_fixed_simple_b.html +== position_fixed_static_y_a.html position_fixed_static_y_b.html +== position_relative_a.html position_relative_b.html diff --git a/src/test/ref/overflow_position_abs_inside_normal_a.html b/src/test/ref/overflow_position_abs_inside_normal_a.html new file mode 100644 index 000000000000..aa9a3f11b7b5 --- /dev/null +++ b/src/test/ref/overflow_position_abs_inside_normal_a.html @@ -0,0 +1,32 @@ + + + `overflow: hidden` on #second has no effect on #abs because its CB is #first. + + + + + +
+
+
+
+
+
+ + diff --git a/src/test/ref/overflow_position_abs_inside_normal_b.html b/src/test/ref/overflow_position_abs_inside_normal_b.html new file mode 100644 index 000000000000..6d0bb81e4bea --- /dev/null +++ b/src/test/ref/overflow_position_abs_inside_normal_b.html @@ -0,0 +1,17 @@ + + + `overflow: hidden` on #second has no effect on #abs because its CB is #first. + + + + + +
+ + diff --git a/src/test/ref/overflow_position_abs_simple_a.html b/src/test/ref/overflow_position_abs_simple_a.html new file mode 100644 index 000000000000..315b48bf8dbe --- /dev/null +++ b/src/test/ref/overflow_position_abs_simple_a.html @@ -0,0 +1,24 @@ + + + + + +
+
+
+
+ + diff --git a/src/test/ref/overflow_position_abs_simple_b.html b/src/test/ref/overflow_position_abs_simple_b.html new file mode 100644 index 000000000000..d899d1f950cf --- /dev/null +++ b/src/test/ref/overflow_position_abs_simple_b.html @@ -0,0 +1,15 @@ + + + + + +
+
+ + diff --git a/src/test/ref/overflow_simple_a.html b/src/test/ref/overflow_simple_a.html new file mode 100644 index 000000000000..9b8b53eff89a --- /dev/null +++ b/src/test/ref/overflow_simple_a.html @@ -0,0 +1,22 @@ + + + + + +
+
+
+
+ + diff --git a/src/test/ref/overflow_simple_b.html b/src/test/ref/overflow_simple_b.html new file mode 100644 index 000000000000..d899d1f950cf --- /dev/null +++ b/src/test/ref/overflow_simple_b.html @@ -0,0 +1,15 @@ + + + + + +
+
+ + diff --git a/src/test/ref/position_abs_cb_with_non_cb_kid_a.html b/src/test/ref/position_abs_cb_with_non_cb_kid_a.html new file mode 100644 index 000000000000..6eb85991133b --- /dev/null +++ b/src/test/ref/position_abs_cb_with_non_cb_kid_a.html @@ -0,0 +1,38 @@ + + Absolute Containing Blocks with an absolute child and a non-CB child which contains an absolute flow. + + + + +
+
+
+
+
+
+
+
+
+
+
+
+ + diff --git a/src/test/ref/position_abs_cb_with_non_cb_kid_b.html b/src/test/ref/position_abs_cb_with_non_cb_kid_b.html new file mode 100644 index 000000000000..fb7007bf6443 --- /dev/null +++ b/src/test/ref/position_abs_cb_with_non_cb_kid_b.html @@ -0,0 +1,32 @@ + + + + + +
+
+
+
+
+
+ + diff --git a/src/test/ref/position_abs_static_y_a.html b/src/test/ref/position_abs_static_y_a.html new file mode 100644 index 000000000000..de14db70dc3b --- /dev/null +++ b/src/test/ref/position_abs_static_y_a.html @@ -0,0 +1,31 @@ + + + + + +
+
+
+
+
+
+ + diff --git a/src/test/ref/position_abs_static_y_b.html b/src/test/ref/position_abs_static_y_b.html new file mode 100644 index 000000000000..7b149c7433d7 --- /dev/null +++ b/src/test/ref/position_abs_static_y_b.html @@ -0,0 +1,28 @@ + + + + + +
+
+
+
+
+ + diff --git a/src/test/ref/position_fixed_static_y_a.html b/src/test/ref/position_fixed_static_y_a.html new file mode 100644 index 000000000000..5d44f77006f4 --- /dev/null +++ b/src/test/ref/position_fixed_static_y_a.html @@ -0,0 +1,40 @@ + + + + + +
+
+
+ +
+
+
+ + diff --git a/src/test/ref/position_fixed_static_y_b.html b/src/test/ref/position_fixed_static_y_b.html new file mode 100644 index 000000000000..0b9ab5568ef0 --- /dev/null +++ b/src/test/ref/position_fixed_static_y_b.html @@ -0,0 +1,35 @@ + + + + + +
+
+
+
+
+ +