aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorbors-servo <release+servo@mozilla.com>2014-03-03 13:37:33 -0500
committerbors-servo <release+servo@mozilla.com>2014-03-03 13:37:33 -0500
commitada9224d0ef5b403562ebaaeb2e5f66729ffc589 (patch)
tree54320088be90a63abfaebb14bd3034f7aa28c48b /src
parentb8de1b3ca7208efdf7c8934e75197dac943533cb (diff)
parent75f1142107af9a96ea3a997048e03c0e37b51b1c (diff)
downloadservo-ada9224d0ef5b403562ebaaeb2e5f66729ffc589.tar.gz
servo-ada9224d0ef5b403562ebaaeb2e5f66729ffc589.zip
auto merge of #1681 : pradeep90/servo/absolute-position, r=pcwalton
+ Re-implement fixed positioning using the absolute positioning code. + Add reftests for absolute positioning and fixed positioning. + Refactor assign_widths in BlockFlow to isolate the calculation of widths and margins. + Pass down details of the Containing Block for absolute and fixed flows during layout. Use it to calculate the static position of absolute flows. + Defer calculation of absolute flow dimensions till we build the display list. This implements https://github.com/mozilla/servo/issues/1537 and https://github.com/mozilla/servo/issues/787
Diffstat (limited to 'src')
-rw-r--r--src/components/main/layout/block.rs1172
-rw-r--r--src/components/main/layout/box_.rs53
-rw-r--r--src/components/main/layout/construct.rs144
-rw-r--r--src/components/main/layout/flow.rs342
-rw-r--r--src/components/main/layout/flow_list.rs8
-rw-r--r--src/components/main/layout/inline.rs17
-rw-r--r--src/components/main/layout/layout_task.rs33
-rw-r--r--src/components/main/layout/parallel.rs18
-rw-r--r--src/test/ref/basic.list12
-rw-r--r--src/test/ref/overflow_position_abs_inside_normal_a.html32
-rw-r--r--src/test/ref/overflow_position_abs_inside_normal_b.html17
-rw-r--r--src/test/ref/overflow_position_abs_simple_a.html24
-rw-r--r--src/test/ref/overflow_position_abs_simple_b.html15
-rw-r--r--src/test/ref/overflow_simple_a.html22
-rw-r--r--src/test/ref/overflow_simple_b.html15
-rw-r--r--src/test/ref/position_abs_cb_with_non_cb_kid_a.html38
-rw-r--r--src/test/ref/position_abs_cb_with_non_cb_kid_b.html32
-rw-r--r--src/test/ref/position_abs_height_width_a.html26
-rw-r--r--src/test/ref/position_abs_height_width_b.html29
-rw-r--r--src/test/ref/position_abs_left_a.html30
-rw-r--r--src/test/ref/position_abs_left_b.html18
-rw-r--r--src/test/ref/position_abs_static_y_a.html31
-rw-r--r--src/test/ref/position_abs_static_y_b.html28
-rw-r--r--src/test/ref/position_abs_width_percentage_a.html26
-rw-r--r--src/test/ref/position_abs_width_percentage_b.html27
-rw-r--r--src/test/ref/position_fixed_simple_a.html38
-rw-r--r--src/test/ref/position_fixed_simple_b.html24
-rw-r--r--src/test/ref/position_fixed_static_y_a.html40
-rw-r--r--src/test/ref/position_fixed_static_y_b.html35
29 files changed, 2086 insertions, 260 deletions
diff --git a/src/components/main/layout/block.rs b/src/components/main/layout/block.rs
index ccaf15b456b..0f965a80c28 100644
--- a/src/components/main/layout/block.rs
+++ b/src/components/main/layout/block.rs
@@ -3,22 +3,31 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
//! CSS block formatting contexts.
+//!
+//! Terminology Note:
+//! As per the CSS Spec, the term 'absolute positioning' here refers to
+//! elements with position = 'absolute' or 'fixed'.
+//! The term 'positioned element' refers to elements with position =
+//! '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;
+use style::computed_values::{position};
use std::cell::RefCell;
use geom::{Point2D, Rect, SideOffsets2D, Size2D};
-use gfx::display_list::{DisplayList, 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 {
@@ -49,7 +58,214 @@ impl FloatedBlockInfo {
}
}
-/// A block formatting context.
+/// The solutions for the widths-and-margins constraint equation.
+struct WidthConstraintSolution {
+ left: Au,
+ right: Au,
+ width: Au,
+ margin_left: Au,
+ margin_right: Au
+}
+
+impl WidthConstraintSolution {
+ fn new(left: Au, right: Au, width: Au, margin_left: Au, margin_right: Au)
+ -> WidthConstraintSolution {
+ WidthConstraintSolution {
+ left: left,
+ right: right,
+ width: width,
+ margin_left: margin_left,
+ margin_right: margin_right,
+ }
+ }
+}
+
+/// The solutions for the heights-and-margins constraint equation.
+struct HeightConstraintSolution {
+ top: Au,
+ bottom: Au,
+ height: Au,
+ margin_top: Au,
+ margin_bottom: Au
+}
+
+impl HeightConstraintSolution {
+ fn new(top: Au, bottom: Au, height: Au, margin_top: Au, margin_bottom: Au)
+ -> HeightConstraintSolution {
+ HeightConstraintSolution {
+ top: top,
+ bottom: bottom,
+ height: height,
+ margin_top: margin_top,
+ 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.
pub struct BlockFlow {
/// Data common to all flows.
base: BaseFlow,
@@ -57,11 +273,12 @@ pub struct BlockFlow {
/// The associated box.
box_: Option<Box>,
- //TODO: is_fixed and is_root should be bit fields to conserve memory.
+ /// TODO: is_root should be a bit field to conserve memory.
/// Whether this block flow is the root flow.
is_root: bool,
- is_fixed: bool,
+ /// Static y offset of an absolute flow from its CB.
+ static_y_offset: Au,
/// Additional floating flow members.
float: Option<~FloatedBlockInfo>
@@ -69,14 +286,13 @@ pub struct BlockFlow {
impl BlockFlow {
pub fn from_node(constructor: &mut FlowConstructor,
- node: &ThreadSafeLayoutNode,
- is_fixed: bool)
+ node: &ThreadSafeLayoutNode)
-> BlockFlow {
BlockFlow {
base: BaseFlow::new((*node).clone()),
box_: Some(Box::new(constructor, node)),
is_root: false,
- is_fixed: is_fixed,
+ static_y_offset: Au::new(0),
float: None
}
}
@@ -89,7 +305,7 @@ impl BlockFlow {
base: BaseFlow::new((*node).clone()),
box_: Some(Box::new(constructor, node)),
is_root: false,
- is_fixed: false,
+ static_y_offset: Au::new(0),
float: Some(~FloatedBlockInfo::new(float_kind))
}
}
@@ -99,7 +315,7 @@ impl BlockFlow {
base: base,
box_: None,
is_root: true,
- is_fixed: false,
+ static_y_offset: Au::new(0),
float: None
}
}
@@ -109,13 +325,103 @@ impl BlockFlow {
base: base,
box_: None,
is_root: false,
- is_fixed: false,
+ static_y_offset: Au::new(0),
float: Some(~FloatedBlockInfo::new(float_kind))
}
}
- pub fn is_float(&self) -> bool {
- self.float.is_some()
+ /// 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 size of the Containing Block for this flow.
+ ///
+ /// Right now, this only gets the Containing Block size for absolutely
+ /// positioned elements.
+ /// 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<Au>) -> Size2D<Au> {
+ assert!(self.is_absolutely_positioned());
+ if self.is_fixed() {
+ // Initial containing block is the CB for the root
+ viewport_size
+ } else {
+ 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<T:PreorderFlowTraversal>(&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
+ }
+
+ /// Traverse the Absolute flow tree in postorder.
+ ///
+ /// Return true if the traversal is to continue or false to stop.
+ fn traverse_postorder_absolute_flows<T:PostorderFlowTraversal>(&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) {
@@ -189,7 +495,244 @@ impl BlockFlow {
(width_Au, left_margin_Au, right_margin_Au)
}
- // Return (content width, left margin, right, margin)
+ /// Calculate and set the width, offsets, etc. for an Absolute Flow.
+ ///
+ /// Calculate:
+ /// + 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, 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();
+
+ // The text alignment of a block flow is the text alignment of its box's style.
+ self.base.flags_info.flags.set_text_align(style.InheritedText.get().text_align);
+
+ box_.compute_padding(style, containing_block_width);
+
+ 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),
+ MaybeAuto::from_style(style.Margin.get().margin_right, containing_block_width));
+
+ let (left, right) =
+ (MaybeAuto::from_style(style.PositionOffsets.get().left, containing_block_width),
+ MaybeAuto::from_style(style.PositionOffsets.get().right, containing_block_width));
+ let available_width = containing_block_width - box_.border_and_padding_horiz();
+ // TODO: Extract this later into a SolveConstraints trait or something
+ let solution = self.solve_horiz_constraints_abs_position(width,
+ margin_left,
+ margin_right,
+ left,
+ right,
+ available_width,
+ static_x_offset);
+
+ 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();
+ // Left border edge.
+ position_ref.get().origin.x = box_.margin.get().left;
+
+ // Border box width
+ position_ref.get().size.width = solution.width + box_.border_and_padding_horiz();
+
+ // Set the x-coordinate of the absolute flow wrt to its containing block.
+ self.base.position.origin.x = solution.left;
+ }
+ }
+
+ /// Calculate and set the width and horizontal margins for this BlockFlow's box.
+ ///
+ /// Also, set the x-coordinate for box_.
+ fn calculate_widths_and_margins(&mut self,
+ containing_block_width: Au,
+ ctx: &mut LayoutContext) {
+ if self.is_absolutely_positioned() {
+ self.calculate_abs_widths_and_margins(ctx);
+ return;
+ }
+ for box_ in self.box_.iter() {
+ let style = box_.style();
+
+ // The text alignment of a block flow is the text alignment of its box's style.
+ self.base.flags_info.flags.set_text_align(style.InheritedText.get().text_align);
+
+ // Calculate used value of width for replaced element.
+ // After that, margin calculation is the same as for non-replaced content.
+ box_.assign_replaced_width_if_necessary(containing_block_width);
+
+ // Can compute padding here since we know containing block width.
+ box_.compute_padding(style, containing_block_width);
+
+ // Margins are 0 right now so base.noncontent_width() is just borders + padding.
+ 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,
+ containing_block_width).specified_or_zero();
+
+ let (width, margin_left, margin_right) = if self.is_float() {
+ self.compute_float_margins(box_, containing_block_width)
+ } else {
+ self.compute_block_margins(box_, containing_block_width, available_width)
+ };
+
+ box_.margin.set(SideOffsets2D::new(margin_top,
+ margin_right,
+ margin_bottom,
+ margin_left));
+
+ // The associated box is the border box of this flow.
+ let mut position_ref = box_.border_box.borrow_mut();
+ position_ref.get().origin.x = box_.margin.get().left;
+ let padding_and_borders = box_.padding.get().left + box_.padding.get().right +
+ box_.border.get().left + box_.border.get().right;
+ position_ref.get().size.width = width + padding_and_borders;
+ }
+ }
+
+ /// Solve the horizontal constraint equation for absolute non-replaced elements.
+ ///
+ /// `static_x_offset`: total offset of current flow's hypothetical
+ /// position (static position) from its actual Containing Block.
+ ///
+ /// CSS Section 10.3.7
+ /// Constraint equation:
+ /// left + right + width + margin-left + margin-right
+ /// = absolute containing block width - (horizontal padding and border)
+ /// [aka available_width]
+ ///
+ /// Return the solution for the equation.
+ fn solve_horiz_constraints_abs_position(&self,
+ width: MaybeAuto,
+ left_margin: MaybeAuto,
+ right_margin: MaybeAuto,
+ left: MaybeAuto,
+ right: MaybeAuto,
+ available_width: Au,
+ static_x_offset: Au)
+ -> WidthConstraintSolution {
+ // 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
+ // left margin edge of a hypothetical box that would have been the
+ // first box of the element.
+ let static_position_left = static_x_offset;
+
+ let (left, right, width, margin_left, margin_right) = match (left, right, width) {
+ (Auto, Auto, Auto) => {
+ let margin_l = left_margin.specified_or_zero();
+ let margin_r = right_margin.specified_or_zero();
+ let left = static_position_left;
+ // Now it is the same situation as left Specified and right
+ // and width Auto.
+
+ // Set right to zero to calculate width
+ let width = self.get_shrink_to_fit_width(available_width - (left + margin_l + margin_r));
+ let sum = left + width + margin_l + margin_r;
+ (left, available_width - sum, width, margin_l, margin_r)
+ }
+ (Specified(left), Specified(right), Specified(width)) => {
+ match (left_margin, right_margin) {
+ (Auto, Auto) => {
+ let total_margin_val = (available_width - left - right - width);
+ if total_margin_val < Au(0) {
+ // margin-left becomes 0 because direction is 'ltr'.
+ // TODO: Handle 'rtl' when it is implemented.
+ (left, right, width, Au(0), total_margin_val)
+ } else {
+ // Equal margins
+ (left, right, width,
+ total_margin_val.scale_by(0.5),
+ total_margin_val.scale_by(0.5))
+ }
+ }
+ (Specified(margin_l), Auto) => {
+ let sum = left + right + width + margin_l;
+ (left, right, width, margin_l, available_width - sum)
+ }
+ (Auto, Specified(margin_r)) => {
+ let sum = left + right + width + margin_r;
+ (left, right, width, available_width - sum, margin_r)
+ }
+ (Specified(margin_l), Specified(margin_r)) => {
+ // Values are over-constrained.
+ // Ignore value for 'right' cos direction is 'ltr'.
+ // TODO: Handle 'rtl' when it is implemented.
+ let sum = left + width + margin_l + margin_r;
+ (left, available_width - sum, width, margin_l, margin_r)
+ }
+ }
+ }
+ // For the rest of the cases, auto values for margin are set to 0
+
+ // If only one is Auto, solve for it
+ (Auto, Specified(right), Specified(width)) => {
+ let margin_l = left_margin.specified_or_zero();
+ let margin_r = right_margin.specified_or_zero();
+ let sum = right + width + margin_l + margin_r;
+ (available_width - sum, right, width, margin_l, margin_r)
+ }
+ (Specified(left), Auto, Specified(width)) => {
+ let margin_l = left_margin.specified_or_zero();
+ let margin_r = right_margin.specified_or_zero();
+ let sum = left + width + margin_l + margin_r;
+ (left, available_width - sum, width, margin_l, margin_r)
+ }
+ (Specified(left), Specified(right), Auto) => {
+ let margin_l = left_margin.specified_or_zero();
+ let margin_r = right_margin.specified_or_zero();
+ let sum = left + right + margin_l + margin_r;
+ (left, right, available_width - sum, margin_l, margin_r)
+ }
+
+ // If width is auto, then width is shrink-to-fit. Solve for the
+ // non-auto value.
+ (Specified(left), Auto, Auto) => {
+ let margin_l = left_margin.specified_or_zero();
+ let margin_r = right_margin.specified_or_zero();
+ // Set right to zero to calculate width
+ let width = self.get_shrink_to_fit_width(
+ available_width - (left + margin_l + margin_r));
+ let sum = left + width + margin_l + margin_r;
+ (left, available_width - sum, width, margin_l, margin_r)
+ }
+ (Auto, Specified(right), Auto) => {
+ let margin_l = left_margin.specified_or_zero();
+ let margin_r = right_margin.specified_or_zero();
+ // Set left to zero to calculate width
+ let width = self.get_shrink_to_fit_width(
+ available_width - (right + margin_l + margin_r));
+ let sum = right + width + margin_l + margin_r;
+ (available_width - sum, right, width, margin_l, margin_r)
+ }
+
+ (Auto, Auto, Specified(width)) => {
+ let margin_l = left_margin.specified_or_zero();
+ let margin_r = right_margin.specified_or_zero();
+ // Setting 'left' to static position because direction is 'ltr'.
+ // TODO: Handle 'rtl' when it is implemented.
+ let left = static_position_left;
+ let sum = left + width + margin_l + margin_r;
+ (left, available_width - sum, width, margin_l, margin_r)
+ }
+ };
+ WidthConstraintSolution::new(left, right, width, margin_left, margin_right)
+ }
+
fn compute_block_margins(&self, box_: &Box, remaining_width: Au, available_width: Au)
-> (Au, Au, Au) {
let style = box_.style();
@@ -234,23 +777,103 @@ impl BlockFlow {
return (width, margin_left, margin_right);
}
+ /// Return shrink-to-fit width.
+ ///
+ /// This is where we use the preferred widths and minimum widths
+ /// calculated in the bubble-widths traversal.
+ fn get_shrink_to_fit_width(&self, available_width: Au) -> Au {
+ geometry::min(self.base.pref_width,
+ geometry::max(self.base.min_width, available_width))
+ }
+
// CSS Section 10.3.5
+ // TODO: This has to handle min-width and max-width.
fn compute_float_margins(&self, box_: &Box, remaining_width: Au) -> (Au, Au, Au) {
let style = box_.style();
let margin_left = MaybeAuto::from_style(style.Margin.get().margin_left,
remaining_width).specified_or_zero();
let margin_right = MaybeAuto::from_style(style.Margin.get().margin_right,
remaining_width).specified_or_zero();
- let shrink_to_fit = geometry::min(self.base.pref_width,
- geometry::max(self.base.min_width, remaining_width));
+ let shrink_to_fit = self.get_shrink_to_fit_width(remaining_width);
let width = MaybeAuto::from_style(style.Box.get().width,
remaining_width).specified_or_default(shrink_to_fit);
debug!("assign_widths_float -- width: {}", width);
return (width, margin_left, margin_right);
}
- // inline(always) because this is only ever called by in-order or non-in-order top-level
- // methods
+
+ /// 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'
+ /// y-coordinates now that we know their heights.
+ /// + Calculate and set the height of the current flow.
+ /// + Calculate height, vertical margins, and y-coordinate for the flow's
+ /// box. Ideally, this should be calculated using CSS Section 10.6.7
+ ///
+ /// For absolute flows, store the calculated content height for the flow.
+ /// Defer the calculation of the other values till a later traversal.
+ ///
+ /// inline(always) because this is only ever called by in-order or non-in-order top-level
+ /// methods
#[inline(always)]
fn assign_height_block_base(&mut self, ctx: &mut LayoutContext, inorder: bool) {
let mut cur_y = Au::new(0);
@@ -261,12 +884,15 @@ impl BlockFlow {
let mut left_offset = Au::new(0);
for box_ in self.box_.iter() {
- clearance = match box_.clear() {
- None => Au::new(0),
- Some(clear) => {
- self.base.floats.clearance(clear)
- }
- };
+ // Note: Ignoring clearance for absolute flows as of now.
+ if !self.is_absolutely_positioned() {
+ clearance = match box_.clear() {
+ None => Au::new(0),
+ Some(clear) => {
+ self.base.floats.clearance(clear)
+ }
+ };
+ }
top_offset = clearance + box_.margin.get().top + box_.border.get().top +
box_.padding.get().top;
@@ -276,7 +902,8 @@ impl BlockFlow {
left_offset = box_.offset();
}
- if inorder {
+ // Note: Ignoring floats for absolute flow as of now.
+ if inorder && !self.is_absolutely_positioned() {
// Floats for blocks work like this:
// self.floats -> child[0].floats
// visit child[0]
@@ -309,41 +936,60 @@ impl BlockFlow {
let mut top_margin_collapsible = false;
let mut bottom_margin_collapsible = false;
let mut first_in_flow = true;
- for box_ in self.box_.iter() {
- 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) &&
+ // Margins for an absolutely positioned element do not collapse with
+ // 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) {
+
+ collapsible = box_.margin.get().top;
+ top_margin_collapsible = true;
+ }
+ if !self.is_root() && box_.border.get().bottom == Au(0) &&
box_.padding.get().bottom == Au(0) {
- bottom_margin_collapsible = true;
+ bottom_margin_collapsible = true;
+ }
+ margin_top = box_.margin.get().top;
+ margin_bottom = box_.margin.get().bottom;
}
- margin_top = box_.margin.get().top;
- margin_bottom = box_.margin.get().bottom;
}
// At this point, cur_y is at the content edge of the flow's box_
for kid in self.base.child_iter() {
// At this point, cur_y is at bottom margin edge of previous kid
- kid.collapse_margins(top_margin_collapsible,
- &mut first_in_flow,
- &mut margin_top,
- &mut top_offset,
- &mut collapsing,
- &mut collapsible);
-
- let child_node = flow::mut_base(kid);
- cur_y = cur_y - collapsing;
- // At this point, after moving up by `collapsing`, cur_y is at the
- // top margin edge of kid
- child_node.position.origin.y = cur_y;
- cur_y = cur_y + child_node.position.size.height;
- // At this point, cur_y is at the bottom margin edge of kid
+
+ 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.
+ kid.as_block().base.position.origin.y = cur_y;
+ // Skip the collapsing for absolute flow kids and continue
+ // with the next flow.
+ } else {
+ kid.collapse_margins(top_margin_collapsible,
+ &mut first_in_flow,
+ &mut margin_top,
+ &mut top_offset,
+ &mut collapsing,
+ &mut collapsible);
+ let child_node = flow::mut_base(kid);
+ cur_y = cur_y - collapsing;
+ // At this point, after moving up by `collapsing`, cur_y is at the
+ // top margin edge of kid
+ child_node.position.origin.y = cur_y;
+ cur_y = cur_y + child_node.position.size.height;
+ // At this point, cur_y is at the bottom margin edge of kid
+ }
}
+ 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 boder, no bottom padding.
- collapsing = if bottom_margin_collapsible {
+ // if the parent has no bottom border, no bottom padding.
+ // The bottom margin for an absolutely positioned element does not
+ // collapse even with its children.
+ collapsing = if bottom_margin_collapsible && !self.is_absolutely_positioned() {
if margin_bottom < collapsible {
margin_bottom = collapsible;
}
@@ -358,27 +1004,37 @@ 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
// infrastructure to make it scrollable.
Au::max(screen_height, cur_y)
} else {
- // (cur_y - collapsing) will get you the bottom content edge
- // top_offset will be at top content edge
+ // (cur_y - collapsing) will get you the the bottom margin-edge of
+ // the bottom-most child.
+ // top_offset: top margin-edge of the topmost child.
// hence, height = content height
cur_y - top_offset - collapsing
};
+ if self.is_absolutely_positioned() {
+ // Store the content height for use in calculating the absolute
+ // flow's dimensions later.
+ for box_ in self.box_.iter() {
+ let mut temp_position = box_.border_box.get();
+ temp_position.size.height = height;
+ box_.border_box.set(temp_position);
+ }
+ return;
+ }
+
for box_ in self.box_.iter() {
let style = box_.style();
// At this point, `height` is the height of the containing block, so passing `height`
// as the second argument here effectively makes percentages relative to the containing
// block per CSS 2.1 § 10.5.
- // TODO: We need to pass in the correct containing block height
- // for absolutely positioned elems
height = match MaybeAuto::from_style(style.Box.get().height, height) {
Auto => height,
Specified(value) => value
@@ -400,27 +1056,9 @@ impl BlockFlow {
noncontent_height = box_.padding.get().top + box_.padding.get().bottom +
box_.border.get().top + box_.border.get().bottom;
- let (y, h) = box_.get_y_coord_and_new_height_if_fixed(screen_height,
- height,
- clearance + margin.top,
- self.is_fixed);
-
- position.origin.y = y;
- height = h;
-
- if self.is_fixed {
- for kid in self.base.child_iter() {
- let child_node = flow::mut_base(kid);
- child_node.position.origin.y = position.origin.y + top_offset;
- }
- }
-
- position.size.height = if self.is_fixed {
- height
- } else {
- // Border box height
- height + noncontent_height
- };
+ position.origin.y = clearance + margin.top;
+ // Border box height
+ position.size.height = height + noncontent_height;
noncontent_height = noncontent_height + clearance + margin.top + margin.bottom;
@@ -428,23 +1066,69 @@ impl BlockFlow {
box_.margin.set(margin);
}
- self.base.position.size.height = if self.is_fixed {
- height
- } else {
- // Height of margin box + clearance
- height + noncontent_height
- };
+ // Height of margin box + clearance
+ self.base.position.size.height = height + noncontent_height;
if inorder {
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);
+ }
}
- fn assign_height_float_inorder(&mut self) {
- // assign_height_float was already called by the traversal function
- // so this is well-defined
+ /// 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.
+ ///
+ /// Also, use information given by parent about other floats to find out
+ /// our relative position.
+ ///
+ /// This does not give any information about any float descendants because
+ /// they do not affect elements outside of the subtree rooted at this
+ /// float.
+ ///
+ /// This function is called on a kid flow by a parent.
+ /// Therefore, assign_height_float was already called on this kid flow by
+ /// the traversal function. So, the values used are well-defined.
+ fn assign_height_float_inorder(&mut self) {
let mut height = Au(0);
let mut clearance = Au(0);
let mut full_noncontent_width = Au(0);
@@ -480,6 +1164,14 @@ impl BlockFlow {
self.float.get_mut_ref().rel_pos = self.base.floats.last_float_pos().unwrap();
}
+ /// Assign height for current flow.
+ ///
+ /// + Set in-flow child flows' y-coordinates now that we know their
+ /// heights. This _doesn't_ do any margin collapsing for its children.
+ /// + Calculate height and y-coordinate for the flow's box. Ideally, this
+ /// should be calculated using CSS Section 10.6.7
+ ///
+ /// It does not calculate the height of the flow itself.
fn assign_height_float(&mut self, ctx: &mut LayoutContext) {
// Now that we've determined our height, propagate that out.
let has_inorder_children = self.base.num_floats > 0;
@@ -502,9 +1194,12 @@ impl BlockFlow {
cur_y = cur_y + top_offset;
}
+ // cur_y is now at the top content edge
+
for kid in self.base.child_iter() {
let child_base = flow::mut_base(kid);
child_base.position.origin.y = cur_y;
+ // cur_y is now at the bottom margin edge of kid
cur_y = cur_y + child_base.position.size.height;
}
@@ -531,26 +1226,31 @@ impl BlockFlow {
box_.border_box.set(position);
}
+ /// Add display items for current block.
+ ///
+ /// Set the absolute position for children after doing any offsetting for
+ /// position: relative.
pub fn build_display_list_block<E:ExtraDisplayListData>(
&mut self,
builder: &DisplayListBuilder,
container_block_size: &Size2D<Au>,
+ absolute_cb_abs_position: Point2D<Au>,
dirty: &Rect<Au>,
- mut index: uint,
+ index: uint,
lists: &RefCell<DisplayListCollection<E>>)
-> uint {
+
if self.is_float() {
self.build_display_list_float(builder, container_block_size, dirty, index, lists);
return index;
+ } else if self.is_absolutely_positioned() {
+ self.build_display_list_abs(builder, container_block_size,
+ absolute_cb_abs_position,
+ dirty, index, lists);
+ return index;
}
- if self.is_fixed {
- lists.with_mut(|lists| {
- index = lists.lists.len();
- lists.add_list(DisplayList::<E>::new());
- });
- }
-
+ // 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) {
return index;
@@ -572,11 +1272,12 @@ impl BlockFlow {
// add box that starts block context
for box_ in self.box_.iter() {
- box_.build_display_list(builder, dirty, self.base.abs_position + rel_offset, (&*self) as &Flow, index, lists);
+ box_.build_display_list(builder, dirty, self.base.abs_position + rel_offset,
+ (&*self) as &Flow, index, lists);
}
+
// TODO: handle any out-of-flow elements
let this_position = self.base.abs_position;
-
for child in self.base.child_iter() {
let child_base = flow::mut_base(child);
child_base.abs_position = this_position + child_base.position.origin + rel_offset;
@@ -629,6 +1330,119 @@ impl BlockFlow {
false
}
+
+ /// Calculate and set the height, offsets, etc. for absolutely positioned flow.
+ ///
+ /// The layout for its in-flow children has been done during normal layout.
+ /// This is just the calculation of:
+ /// + 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, 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
+ let content_height = box_.border_box.get().size.height;
+
+ let style = box_.style();
+
+ let (height_used_val, margin_top, margin_bottom) =
+ (MaybeAuto::from_style(style.Box.get().height, containing_block_height),
+ MaybeAuto::from_style(style.Margin.get().margin_top, containing_block_height),
+ MaybeAuto::from_style(style.Margin.get().margin_right, containing_block_height));
+
+ 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_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;
+ margin.bottom = solution.margin_bottom;
+ box_.margin.set(margin);
+
+ 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_vert();
+ position.size.height = solution.height + border_and_padding;
+ box_.border_box.set(position);
+
+ self.base.position.origin.y = solution.top;
+ self.base.position.size.height = solution.height + border_and_padding
+ + solution.margin_top + solution.margin_bottom;
+ }
+ }
+
+ /// Add display items for Absolutely Positioned flow.
+ pub fn build_display_list_abs<E:ExtraDisplayListData>(
+ &mut self,
+ builder: &DisplayListBuilder,
+ _: &Size2D<Au>,
+ absolute_cb_abs_position: Point2D<Au>,
+ dirty: &Rect<Au>,
+ mut index: uint,
+ lists: &RefCell<DisplayListCollection<E>>)
+ -> bool {
+ 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
+ // wrt Containing Block
+ absolute_cb_abs_position + self.base.position.origin
+ };
+
+ if self.is_fixed() {
+ lists.with_mut(|lists| {
+ index = lists.lists.len();
+ lists.add_list(DisplayList::<E>::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;
+ let abs_rect = Rect(flow_origin, self.base.position.size);
+ if !abs_rect.intersects(dirty) {
+ return true;
+ }
+
+ for box_ in self.box_.iter() {
+ box_.build_display_list(builder, dirty, flow_origin, (&*self) as &Flow, index, lists);
+ }
+
+ // Go deeper into the flow tree.
+ for child in self.base.child_iter() {
+ let child_base = flow::mut_base(child);
+ child_base.abs_position = flow_origin + child_base.position.origin;
+ }
+
+ false
+ }
+
+ /// 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).
+ fn get_hypothetical_top_edge(&self) -> Au {
+ self.base.position.origin.y
+ }
}
impl Flow for BlockFlow {
@@ -640,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
@@ -700,80 +1518,40 @@ 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);
}
// The position was set to the containing block by the flow's parent.
- let mut remaining_width = self.base.position.size.width;
- let mut x_offset = Au::new(0);
+ let containing_block_width = self.base.position.size.width;
+ let mut left_content_edge = Au::new(0);
+ let mut content_width = containing_block_width;
if self.is_float() {
- self.float.get_mut_ref().containing_width = remaining_width;
+ self.float.get_mut_ref().containing_width = containing_block_width;
// Parent usually sets this, but floats are never inorder
self.base.flags_info.flags.set_inorder(false);
}
- for box_ in self.box_.iter() {
- let style = box_.style();
-
- // The text alignment of a block flow is the text alignment of its box's style.
- self.base.flags_info.flags.set_text_align(style.InheritedText.get().text_align);
-
- box_.assign_width(remaining_width);
- // Can compute padding here since we know containing block width.
- box_.compute_padding(style, remaining_width);
-
- // Margins are 0 right now so base.noncontent_width() is just borders + padding.
- let available_width = remaining_width - box_.noncontent_width();
-
- // Top and bottom margins for blocks are 0 if auto.
- let margin_top = MaybeAuto::from_style(style.Margin.get().margin_top,
- remaining_width).specified_or_zero();
- let margin_bottom = MaybeAuto::from_style(style.Margin.get().margin_bottom,
- remaining_width).specified_or_zero();
-
- let (width, margin_left, margin_right) = if self.is_float() {
- self.compute_float_margins(box_, remaining_width)
- } else {
- self.compute_block_margins(box_, remaining_width, available_width)
- };
-
- box_.margin.set(SideOffsets2D::new(margin_top,
- margin_right,
- margin_bottom,
- margin_left));
+ self.calculate_widths_and_margins(containing_block_width, ctx);
- let screen_size = ctx.screen_size;
- let (x, w) = box_.get_x_coord_and_new_width_if_fixed(screen_size.width,
- screen_size.height,
- width,
- box_.offset(),
- self.is_fixed);
-
- x_offset = x;
- remaining_width = w;
-
- // The associated box is the border box of this flow.
- let mut position_ref = box_.border_box.borrow_mut();
- if self.is_fixed {
- position_ref.get().origin.x = x_offset + box_.margin.get().left;
- x_offset = x_offset + box_.padding.get().left;
- } else {
- position_ref.get().origin.x = box_.margin.get().left;
- }
+ for box_ in self.box_.iter() {
+ // Move in from the left border edge
+ left_content_edge = box_.border_box.get().origin.x
+ + box_.padding.get().left + box_.border.get().left;
let padding_and_borders = box_.padding.get().left + box_.padding.get().right +
box_.border.get().left + box_.border.get().right;
- position_ref.get().size.width = remaining_width + padding_and_borders;
+ content_width = box_.border_box.get().size.width - padding_and_borders;
}
if self.is_float() {
- self.base.position.size.width = remaining_width;
+ self.base.position.size.width = content_width;
}
let has_inorder_children = if self.is_float() {
@@ -782,14 +1560,40 @@ impl Flow for BlockFlow {
self.base.flags_info.flags.inorder() || self.base.num_floats > 0
};
+ let kid_abs_cb_x_offset;
+ if self.is_positioned() {
+ match self.box_ {
+ Some(ref box_) => {
+ // Pass yourself as a new Containing Block
+ // The static x offset for any immediate kid flows will be the
+ // left padding
+ kid_abs_cb_x_offset = box_.padding.get().left;
+ }
+ None => fail!("BlockFlow: no principal box found"),
+ }
+ } else {
+ // 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.base.absolute_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();
for kid in self.base.child_iter() {
assert!(kid.is_block_flow() || kid.is_inline_flow());
+ if kid.is_block_flow() {
+ let kid_block = kid.as_block();
+ 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);
- child_base.position.origin.x = x_offset;
- child_base.position.size.width = remaining_width;
+ // Left margin edge of kid flow is at our left content edge
+ child_base.position.origin.x = left_content_edge;
+ // Width of kid flow is our content width
+ child_base.position.size.width = content_width;
child_base.flags_info.flags.set_inorder(has_inorder_children);
if !child_base.flags_info.flags.inorder() {
@@ -805,6 +1609,10 @@ impl Flow for BlockFlow {
}
}
+ /// This is called on kid flows by a parent.
+ ///
+ /// Hence, we can assume that assign_height has already been called on the
+ /// kid (because of the bottom-up traversal).
fn assign_height_inorder(&mut self, ctx: &mut LayoutContext) {
if self.is_float() {
debug!("assign_height_inorder_float: assigning height for float");
@@ -816,7 +1624,7 @@ impl Flow for BlockFlow {
}
fn assign_height(&mut self, ctx: &mut LayoutContext) {
- //assign height for box
+ // Assign height for box if it is an image box.
for box_ in self.box_.iter() {
box_.assign_height();
}
@@ -828,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;
}
@@ -837,6 +1645,10 @@ impl Flow for BlockFlow {
}
// CSS Section 8.3.1 - Collapsing Margins
+ // `self`: the Flow whose margins we want to collapse.
+ // `collapsing`: value to be set by this function. This tells us how much
+ // of the top margin has collapsed with a previous margin.
+ // `collapsible`: Potential collapsible margin at the bottom of this flow's box.
fn collapse_margins(&mut self,
top_margin_collapsible: bool,
first_in_flow: &mut bool,
@@ -877,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<Au> {
+ 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<Au> {
+ 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 7a230bf09c1..7b8062e5dfc 100644
--- a/src/components/main/layout/box_.rs
+++ b/src/components/main/layout/box_.rs
@@ -605,6 +605,22 @@ impl Box {
specified(padding, content_box_width)
}
+ pub fn padding_box_size(&self) -> Size2D<Au> {
+ 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_vert(&self) -> Au {
+ self.border.get().top + self.border.get().bottom + self.padding.get().top
+ + self.padding.get().bottom
+ }
+
pub fn noncontent_width(&self) -> Au {
self.noncontent_left() + self.noncontent_right()
}
@@ -613,6 +629,7 @@ impl Box {
self.noncontent_top() + self.noncontent_bottom()
}
+ // Return offset from original position because of `position: relative`.
pub fn relative_position(&self, container_block_size: &Size2D<Au>) -> Point2D<Au> {
fn left_right(style: &ComputedValues, block_width: Au) -> Au {
// TODO(ksh8281) : consider RTL(right-to-left) culture
@@ -651,6 +668,7 @@ impl Box {
rel_pos.y = rel_pos.y + top_bottom(self.style(), container_block_size.height);
}
+ // Go over the ancestor boxes and add all relative offsets (if any).
let info = self.inline_info.borrow();
match info.get() {
&Some(ref info) => {
@@ -977,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.
///
@@ -990,15 +1008,16 @@ impl Box {
&self,
builder: &DisplayListBuilder,
dirty: &Rect<Au>,
- offset: Point2D<Au>,
+ flow_origin: Point2D<Au>,
flow: &Flow,
index: uint,
lists: &RefCell<DisplayListCollection<E>>) {
+ // 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;
@@ -1011,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) => {
@@ -1122,6 +1146,7 @@ impl Box {
// FIXME(pcwalton): This is a bit of an abuse of the logging infrastructure. We
// should have a real `SERVO_DEBUG` system.
debug!("{:?}", {
+ // This prints a debug border around the border of this box.
let debug_border = SideOffsets2D::new_all_same(Au::from_px(1));
lists.with_mut(|lists| {
@@ -1208,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.
@@ -1432,8 +1453,10 @@ impl Box {
}
}
- /// Assigns the appropriate width to this box.
- pub fn assign_width(&self,container_width: Au) {
+ /// Assigns replaced width for this box only if it is replaced content.
+ ///
+ /// CSS 2.1 § 10.3.2.
+ pub fn assign_replaced_width_if_necessary(&self,container_width: Au) {
match self.specific {
GenericBox | IframeBox(_) => {
}
@@ -1469,7 +1492,8 @@ impl Box {
image_box_info.computed_width.set(Some(width));
}
ScannedTextBox(_) => {
- // Scanned text boxes will have already had their content_widths assigned by this point.
+ // Scanned text boxes will have already had their
+ // content_widths assigned by this point.
let mut position = self.border_box.borrow_mut();
position.get().size.width = position.get().size.width + self.noncontent_width() +
self.noncontent_inline_left() + self.noncontent_inline_right();
@@ -1478,6 +1502,7 @@ impl Box {
}
}
+ /// Assign height for image and scanned text boxes.
pub fn assign_height(&self) {
match self.specific {
GenericBox | IframeBox(_) => {
@@ -1514,6 +1539,8 @@ impl Box {
ScannedTextBox(_) => {
// Scanned text boxes will have already had their widths assigned by this point
let mut position = self.border_box.borrow_mut();
+ // Scanned text boxes' content heights are calculated by the
+ // text run scanner during Flow construction.
position.get().size.height
= position.get().size.height + self.noncontent_height()
}
diff --git a/src/components/main/layout/construct.rs b/src/components/main/layout/construct.rs
index d91d538464f..745ee21c173 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<T> {
+pub trait OptVector<T> {
/// 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,29 +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, is_fixed: bool) -> ~Flow {
- let mut flow = ~BlockFlow::from_node(self, node, is_fixed) 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)
}
@@ -433,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.
@@ -475,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))
@@ -533,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 {
@@ -612,6 +666,8 @@ impl<'a> FlowConstructor<'a> {
boxes: ~[
Box::new(self, node)
],
+ abs_descendants: Descendants::new(),
+ fixed_descendants: Descendants::new(),
});
ConstructionItemConstructionResult(construction_item)
}
@@ -636,7 +692,7 @@ impl<'a> PostorderNodeMutTraversal for FlowConstructor<'a> {
#[inline(always)]
fn process(&mut self, node: &ThreadSafeLayoutNode) -> bool {
// Get the `display` property for this node, and determine whether this node is floated.
- let (display, float, position) = match node.type_id() {
+ let (display, float, positioning) = match node.type_id() {
ElementNodeTypeId(_) => {
let style = node.style().get();
(style.Box.get().display, style.Box.get().float, style.Box.get().position)
@@ -652,7 +708,7 @@ impl<'a> PostorderNodeMutTraversal for FlowConstructor<'a> {
debug!("building flow for node: {:?} {:?}", display, float);
// Switch on display and floatedness.
- match (display, float, position) {
+ match (display, float, positioning) {
// `display: none` contributes no flow construction result. Nuke the flow construction
// results of children.
(display::none, _, _) => {
@@ -662,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);
@@ -673,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::fixed) => {
- let flow = self.build_flow_for_block(node, true);
- node.set_flow_construction_result(FlowConstructionResult(flow))
- }
(_, float::none, _) => {
- let flow = self.build_flow_for_block(node, false);
- 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))
}
}
@@ -779,7 +834,7 @@ trait ObjectElement {
/// Returns true if this node has object data that is correct uri.
fn has_object_data(&self) -> bool;
- /// Returns the "data" attribute value parsed as a URL
+ /// Returns the "data" attribute value parsed as a URL
fn get_object_data(&self, base_url: &Url) -> Option<Url>;
}
@@ -793,7 +848,7 @@ impl<'ln> ObjectElement for ThreadSafeLayoutNode<'ln> {
match self.get_type_and_data() {
(None, Some(uri)) => is_image_data(uri),
_ => false
- }
+ }
}
fn get_object_data(&self, base_url: &Url) -> Option<Url> {
@@ -854,4 +909,3 @@ fn strip_ignorable_whitespace_from_end(opt_boxes: &mut Option<~[Box]>) {
*opt_boxes = None
}
}
-
diff --git a/src/components/main/layout/flow.rs b/src/components/main/layout/flow.rs
index b476e8b07b7..d8abda6d7e8 100644
--- a/src/components/main/layout/flow.rs
+++ b/src/components/main/layout/flow.rs
@@ -16,7 +16,7 @@
///
/// * `BlockFlow`: A flow that establishes a block context. It has several child flows, each of
/// which are positioned according to block formatting context rules (CSS block boxes). Block
-/// flows also contain a single `GenericBox` to represent their rendered borders, padding, etc.
+/// flows also contain a single box to represent their rendered borders, padding, etc.
/// The BlockFlow at the root of the tree has special behavior: it stretches to the boundaries of
/// the viewport.
///
@@ -26,9 +26,10 @@
/// similar methods.
use css::node_style::StyledNode;
-use layout::block::BlockFlow;
+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<Au> {
+ 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<Au> {
+ fail!("this is not the CB-generating flow you're looking for")
+ }
+
/// Returns a debugging string describing this flow.
fn debug_str(&self) -> ~str {
~"???"
@@ -208,6 +269,7 @@ pub trait MutableFlowUtils {
self,
builder: &DisplayListBuilder,
container_block_size: &Size2D<Au>,
+ absolute_cb_abs_position: Point2D<Au>,
dirty: &Rect<Au>,
index: uint,
mut list: &RefCell<DisplayListCollection<E>>)
@@ -230,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);
}
@@ -483,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<Rawlink>,
+ /// Static y offsets of all descendants from the start of this flow box.
+ static_y_offsets: SmallVec0<Au>,
+}
+
+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, Rawlink>, VecMutIterator<'a, Au>>;
+
/// Data common to all flows.
pub struct BaseFlow {
restyle_damage: RestyleDamage,
@@ -498,8 +625,9 @@ pub struct BaseFlow {
min_width: Au,
pref_width: Au,
- /// The position of the upper left corner of the border box of this flow, relative to the
- /// containing block.
+ /// The upper left corner of the box representing this flow, relative to
+ /// the box representing its parent flow.
+ /// For absolute flows, this represents the position wrt to its Containing Block.
position: Rect<Au>,
/// The amount of overflow of this flow, relative to the containing block. Must include all the
@@ -514,12 +642,32 @@ pub struct BaseFlow {
/// The floats next to this flow.
floats: Floats,
- /// The number of floated descendants of this flow (including this flow, if it's floated).
+ /// For normal flows, this is the number of floated descendants that are
+ /// not contained within any other floated descendant of this flow. For
+ /// floats, it is 1.
+ /// It is used to allocate float data if necessary and to
+ /// decide whether to do an in-order traversal for assign_height.
num_floats: uint,
/// The position of this flow in page coordinates, computed during display list construction.
abs_position: Point2D<Au>,
+ /// 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
@@ -576,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,
@@ -707,15 +860,56 @@ impl<'a> MutableFlowUtils for &'a mut Flow {
f(mut_base(self).children.back_mut())
}
+ /// Calculate and set overflow for current flow.
+ ///
+ /// CSS Section 11.1
+ /// 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.
+ /// 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`.
@@ -723,17 +917,29 @@ impl<'a> MutableFlowUtils for &'a mut Flow {
/// For InlineFlow, add display items for all its boxes onto list`.
/// For BlockFlow, add a ClipDisplayItemClass for itself and its children,
/// plus any other display items like border.
+ ///
+ /// `container_block_size`: Size of the Containing Block for the current
+ /// flow. This is used for relative positioning (which resolves percentage
+ /// values for 'top', etc. after all Containing Block heights have been computed.)
+ /// `absolute_cb_abs_position`: Absolute position of the Containing Block
+ /// for the flow if it is absolutely positioned.
fn build_display_lists<E:ExtraDisplayListData>(
self,
builder: &DisplayListBuilder,
container_block_size: &Size2D<Au>,
+ absolute_cb_abs_position: Point2D<Au>,
dirty: &Rect<Au>,
mut index: uint,
lists: &RefCell<DisplayListCollection<E>>)
-> bool {
debug!("Flow: building display list");
index = match self.class() {
- BlockFlowClass => self.as_block().build_display_list_block(builder, container_block_size, dirty, index, lists),
+ BlockFlowClass => self.as_block().build_display_list_block(builder,
+ container_block_size,
+ absolute_cb_abs_position,
+ dirty,
+ index,
+ lists),
InlineFlowClass => self.as_inline().build_display_list_inline(builder, container_block_size, dirty, index, lists),
};
@@ -742,24 +948,68 @@ 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 container_block_size = match self.class() {
- BlockFlowClass => {
- if self.as_block().box_.is_some() {
- self.as_block().box_.get_ref().border_box.get().size
+ let container_block_size;
+ let abs_cb_position;
+ // 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 block.is_positioned() {
+ block.base.abs_position + block.generated_cb_position()
} else {
- base(self).position.size
+ absolute_cb_abs_position
+ };
+ }
+ None => fail!("Flow: block container should have a box_")
+ }
+
+ 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);
}
- },
- _ => {
- base(self).position.size
+ None => fail!("empty Rawlink to a descendant")
}
- };
+ }
- for kid in child_iter(self) {
- kid.build_display_lists(builder, &container_block_size, dirty, 0u, &child_lists);
+ 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());
@@ -822,10 +1072,54 @@ 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;
self_borrowed.destroy();
}
}
-
diff --git a/src/components/main/layout/flow_list.rs b/src/components/main/layout/flow_list.rs
index 232b718fba8..330bf78c2a8 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/inline.rs b/src/components/main/layout/inline.rs
index 4292ab32274..6abfd6639e8 100644
--- a/src/components/main/layout/inline.rs
+++ b/src/components/main/layout/inline.rs
@@ -657,18 +657,13 @@ impl Flow for InlineFlow {
{
let this = &mut *self;
for box_ in this.boxes.iter() {
- box_.assign_width(self.base.position.size.width);
+ box_.assign_replaced_width_if_necessary(self.base.position.size.width);
}
}
- // FIXME(ksh8281) avoid copy
- let flags_info = self.base.flags_info.clone();
- for kid in self.base.child_iter() {
- let child_base = flow::mut_base(kid);
- child_base.position.size.width = self.base.position.size.width;
- child_base.flags_info.flags.set_inorder(self.base.flags_info.flags.inorder());
- child_base.flags_info.propagate_text_alignment_from_parent(&flags_info)
- }
+ assert!(self.base.children.len() == 0,
+ "InlineFlow: should not have children flows in the current layout implementation.");
+
// There are no child contexts, so stop here.
// TODO(Issue #225): once there are 'inline-block' elements, this won't be
@@ -685,6 +680,9 @@ impl Flow for InlineFlow {
self.assign_height(ctx);
}
+ /// Calculate and set the height of this Flow.
+ ///
+ /// CSS Section 10.6.1
fn assign_height(&mut self, _: &mut LayoutContext) {
debug!("assign_height_inline: assigning height for flow");
@@ -901,4 +899,3 @@ impl Flow for InlineFlow {
~"InlineFlow: " + self.boxes.map(|s| s.debug_str()).connect(", ")
}
}
-
diff --git a/src/components/main/layout/layout_task.rs b/src/components/main/layout/layout_task.rs
index b8e8b022c9a..0fdacec78ad 100644
--- a/src/components/main/layout/layout_task.rs
+++ b/src/components/main/layout/layout_task.rs
@@ -23,6 +23,7 @@ use layout::wrapper::{LayoutNode, TLayoutNode, ThreadSafeLayoutNode};
use extra::url::Url;
use extra::arc::{Arc, MutexArc};
+use geom::point::Point2D;
use geom::rect::Rect;
use geom::size::Size2D;
use gfx::display_list::{ClipDisplayItemClass, DisplayItem, DisplayItemIterator};
@@ -211,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
}
@@ -276,7 +281,7 @@ impl LayoutTask {
chan: LayoutChan,
constellation_chan: ConstellationChan,
script_chan: ScriptChan,
- render_chan: RenderChan<OpaqueNode>,
+ render_chan: RenderChan<OpaqueNode>,
image_cache_task: ImageCacheTask,
opts: &Opts,
profiler_chan: ProfilerChan)
@@ -402,7 +407,7 @@ impl LayoutTask {
/// crash.
fn exit_now(&mut self) {
let (response_port, response_chan) = Chan::new();
-
+
match self.parallel_traversal {
None => {}
Some(ref mut traversal) => traversal.shutdown(),
@@ -426,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();
@@ -614,6 +629,7 @@ impl LayoutTask {
if data.goal == ReflowForDisplay {
profile(time::LayoutDispListBuildCategory, self.profiler_chan.clone(), || {
let root_size = flow::base(layout_root).position.size;
+ let root_abs_position = Point2D(Au::new(0), Au::new(0));
let mut display_list_collection = DisplayListCollection::new();
display_list_collection.add_list(DisplayList::<OpaqueNode>::new());
let display_list_collection = ~RefCell::new(display_list_collection);
@@ -621,14 +637,16 @@ impl LayoutTask {
let display_list_builder = DisplayListBuilder {
ctx: &layout_ctx,
};
- layout_root.build_display_lists(&display_list_builder, &root_size, &dirty, 0u, display_list_collection);
+ layout_root.build_display_lists(&display_list_builder, &root_size,
+ root_abs_position,
+ &dirty, 0u, display_list_collection);
let display_list_collection = Arc::new(display_list_collection.unwrap());
let mut color = color::rgba(255.0, 255.0, 255.0, 255.0);
for child in node.traverse_preorder() {
- if child.type_id() == ElementNodeTypeId(HTMLHtmlElementTypeId) ||
+ if child.type_id() == ElementNodeTypeId(HTMLHtmlElementTypeId) ||
child.type_id() == ElementNodeTypeId(HTMLBodyElementTypeId) {
let element_bg_color = {
let thread_safe_child = ThreadSafeLayoutNode::new(&child);
@@ -768,7 +786,7 @@ impl LayoutTask {
Au::from_frac_px(point.y as f64));
let resp = hit_test(x,y,display_list.list);
if resp.is_some() {
- reply_chan.send(Ok(resp.unwrap()));
+ reply_chan.send(Ok(resp.unwrap()));
return
}
}
@@ -842,4 +860,3 @@ impl LayoutTask {
util::replace(layout_data_ref.get(), None));
}
}
-
diff --git a/src/components/main/layout/parallel.rs b/src/components/main/layout/parallel.rs
index 5e4da051dbc..f03341ed6e0 100644
--- a/src/components/main/layout/parallel.rs
+++ b/src/components/main/layout/parallel.rs
@@ -119,6 +119,18 @@ impl FlowParallelInfo {
/// A parallel bottom-up flow traversal.
trait ParallelPostorderFlowTraversal : PostorderFlowTraversal {
+ /// Process current flow and potentially traverse its ancestors.
+ ///
+ /// If we are the last child that finished processing, recursively process
+ /// our parent. Else, stop.
+ /// Also, stop at the root (obviously :P).
+ ///
+ /// Thus, if we start with all the leaves of a tree, we end up traversing
+ /// the whole tree bottom-up because each parent will be processed exactly
+ /// once (by the last child that finishes processing).
+ ///
+ /// The only communication between siblings is that they both
+ /// fetch-and-subtract the parent's children count.
fn run_parallel(&mut self,
mut unsafe_flow: UnsafeFlow,
_: &mut WorkerProxy<*mut LayoutContext,PaddedUnsafeFlow>) {
@@ -144,8 +156,9 @@ trait ParallelPostorderFlowTraversal : PostorderFlowTraversal {
break
}
- // No, we're not at the root yet. Then are we the last sibling of our parent? If
- // so, we can continue on with our parent; otherwise, we've gotta wait.
+ // No, we're not at the root yet. Then are we the last child
+ // of our parent to finish processing? If so, we can continue
+ // on with our parent; otherwise, we've gotta wait.
let parent: &mut ~Flow = cast::transmute(&unsafe_parent);
let parent_base = flow::mut_base(*parent);
if parent_base.parallel.children_count.fetch_sub(1, SeqCst) == 1 {
@@ -426,4 +439,3 @@ pub fn traverse_flow_tree_preorder(root: &mut ~Flow,
queue.data = ptr::mut_null()
}
-
diff --git a/src/test/ref/basic.list b/src/test/ref/basic.list
index c6713e02f90..f30a45531e2 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 00000000000..aa9a3f11b7b
--- /dev/null
+++ b/src/test/ref/overflow_position_abs_inside_normal_a.html
@@ -0,0 +1,32 @@
+<html>
+ <title>
+ `overflow: hidden` on #second has no effect on #abs because its CB is #first.
+ </title>
+ <head>
+ <style>
+ #first {
+ position: relative;
+ }
+ #second {
+ height: 100px;
+ width: 100px;
+ background: red;
+ overflow: hidden;
+ }
+ #abs {
+ position: absolute;
+ height: 200px;
+ width: 200px;
+ background: green;
+ }
+ </style>
+ </head>
+ <body>
+ <div id="first">
+ <div id="second">
+ <div id="abs">
+ </div>
+ </div>
+ </div>
+ </body>
+</html>
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 00000000000..6d0bb81e4be
--- /dev/null
+++ b/src/test/ref/overflow_position_abs_inside_normal_b.html
@@ -0,0 +1,17 @@
+<html>
+ <title>
+ `overflow: hidden` on #second has no effect on #abs because its CB is #first.
+ </title>
+ <head>
+ <style>
+ #first {
+ height: 200px;
+ width: 200px;
+ background: green;
+ }
+ </style>
+ </head>
+ <body>
+ <div id="first"></div>
+ </body>
+</html>
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 00000000000..315b48bf8db
--- /dev/null
+++ b/src/test/ref/overflow_position_abs_simple_a.html
@@ -0,0 +1,24 @@
+<html>
+ <head>
+ <style>
+ #first {
+ height: 100px;
+ width: 100px;
+ overflow: hidden;
+ position: relative;
+ }
+ #second {
+ position: absolute;
+ height: 100px;
+ width: 200px;
+ background: green;
+ }
+ </style>
+ </head>
+ <body>
+ <div id="first">
+ <div id="second">
+ </div>
+ </div>
+ </body>
+</html>
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 00000000000..d899d1f950c
--- /dev/null
+++ b/src/test/ref/overflow_position_abs_simple_b.html
@@ -0,0 +1,15 @@
+<html>
+ <head>
+ <style>
+ #first {
+ height: 100px;
+ width: 100px;
+ background: green;
+ }
+ </style>
+ </head>
+ <body>
+ <div id="first">
+ </div>
+ </body>
+</html>
diff --git a/src/test/ref/overflow_simple_a.html b/src/test/ref/overflow_simple_a.html
new file mode 100644
index 00000000000..9b8b53eff89
--- /dev/null
+++ b/src/test/ref/overflow_simple_a.html
@@ -0,0 +1,22 @@
+<html>
+ <head>
+ <style>
+ #first {
+ height: 100px;
+ width: 100px;
+ overflow: hidden;
+ }
+ #second {
+ height: 100px;
+ width: 200px;
+ background: green;
+ }
+ </style>
+ </head>
+ <body>
+ <div id="first">
+ <div id="second">
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/src/test/ref/overflow_simple_b.html b/src/test/ref/overflow_simple_b.html
new file mode 100644
index 00000000000..d899d1f950c
--- /dev/null
+++ b/src/test/ref/overflow_simple_b.html
@@ -0,0 +1,15 @@
+<html>
+ <head>
+ <style>
+ #first {
+ height: 100px;
+ width: 100px;
+ background: green;
+ }
+ </style>
+ </head>
+ <body>
+ <div id="first">
+ </div>
+ </body>
+</html>
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 00000000000..6eb85991133
--- /dev/null
+++ b/src/test/ref/position_abs_cb_with_non_cb_kid_a.html
@@ -0,0 +1,38 @@
+<html>
+ <title>Absolute Containing Blocks with an absolute child and a non-CB child which contains an absolute flow.</title>
+ <head>
+ <style>
+ .cb {
+ position: relative;
+ width: 90px;
+ height: 90px;
+ border: solid 1px;
+ }
+ #second {
+ width: 30px;
+ height: 30px;
+ }
+ #abs {
+ position: absolute;
+ left: 30px;
+ right: 30px;
+ height: 20px;
+ background: green;
+ }
+ </style>
+ </head>
+ <body>
+ <div id="first-cb" class="cb">
+ <div id="second">
+ </div>
+ <div id="abs">
+ </div>
+ <div id="non-cb-2">
+ <div id="second">
+ </div>
+ <div id="abs">
+ </div>
+ </div>
+ </div>
+ </body>
+</html>
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 00000000000..fb7007bf644
--- /dev/null
+++ b/src/test/ref/position_abs_cb_with_non_cb_kid_b.html
@@ -0,0 +1,32 @@
+<html>
+ <head>
+ <style>
+ #first {
+ width: 90px;
+ height: 90px;
+ border: solid 1px;
+ }
+ .row {
+ width: 90px;
+ height: 30px;
+ }
+ .center {
+ margin-left: 30px;
+ height: 20px;
+ width: 30px;
+ background: green;
+ }
+ #row2 {
+ height: 10px;
+ }
+ </style>
+ </head>
+ <body>
+ <div id="first">
+ <div class="row"></div>
+ <div class="center"></div>
+ <div class="row" id="row2"></div>
+ <div class="center"></div>
+ </div>
+ </body>
+</html>
diff --git a/src/test/ref/position_abs_height_width_a.html b/src/test/ref/position_abs_height_width_a.html
new file mode 100644
index 00000000000..667929c20bd
--- /dev/null
+++ b/src/test/ref/position_abs_height_width_a.html
@@ -0,0 +1,26 @@
+<html>
+ <head>
+ <style>
+ #first {
+ position: relative;
+ width: 90px;
+ height: 90px;
+ border: solid 1px;
+ }
+ #abs {
+ position: absolute;
+ left: 30px;
+ top: 30px;
+ right: 30px;
+ bottom: 30px;
+ background: green;
+ }
+ </style>
+ </head>
+ <body>
+ <div id="first">
+ <div id="abs">
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/src/test/ref/position_abs_height_width_b.html b/src/test/ref/position_abs_height_width_b.html
new file mode 100644
index 00000000000..ab53620a552
--- /dev/null
+++ b/src/test/ref/position_abs_height_width_b.html
@@ -0,0 +1,29 @@
+<html>
+ <head>
+ <style>
+ #first {
+ width: 90px;
+ height: 90px;
+ border: solid 1px;
+ }
+ .row {
+ width: 90px;
+ height: 30px;
+ }
+ .center {
+ margin-left: 30px;
+ height: 30px;
+ width: 30px;
+ background: green;
+ }
+ </style>
+ </head>
+ <body>
+ <div id="first">
+ <div class="row"></div>
+ <div class="center">
+ </div>
+ <div class="row"></div>
+ </div>
+ </body>
+</html>
diff --git a/src/test/ref/position_abs_left_a.html b/src/test/ref/position_abs_left_a.html
new file mode 100644
index 00000000000..502f152e029
--- /dev/null
+++ b/src/test/ref/position_abs_left_a.html
@@ -0,0 +1,30 @@
+<html>
+ <head>
+ <style>
+ body {
+ position: relative;
+ }
+ div {
+ width: 100px;
+ height: 100px;
+ }
+ #first {
+ background: red;
+ }
+ #abs {
+ position: absolute;
+ left: auto;
+ top: 0px;
+ background: green;
+ }
+ </style>
+ </head>
+ <body>
+ <div id="first">
+
+ </div>
+ <div id="abs">
+
+ </div>
+ </body>
+</html>
diff --git a/src/test/ref/position_abs_left_b.html b/src/test/ref/position_abs_left_b.html
new file mode 100644
index 00000000000..f1d4fcd65a8
--- /dev/null
+++ b/src/test/ref/position_abs_left_b.html
@@ -0,0 +1,18 @@
+<html>
+ <head>
+ <style>
+ div {
+ width: 100px;
+ height: 100px;
+ }
+ #first {
+ background: green;
+ }
+ </style>
+ </head>
+ <body>
+ <div id="first">
+
+ </div>
+ </body>
+</html>
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 00000000000..de14db70dc3
--- /dev/null
+++ b/src/test/ref/position_abs_static_y_a.html
@@ -0,0 +1,31 @@
+<html>
+ <head>
+ <style>
+ #first {
+ position: relative;
+ width: 90px;
+ height: 90px;
+ border: solid 1px;
+ }
+ #second {
+ width: 30px;
+ height: 30px;
+ }
+ #abs {
+ position: absolute;
+ left: 30px;
+ right: 30px;
+ height: 20px;
+ background: green;
+ }
+ </style>
+ </head>
+ <body>
+ <div id="first">
+ <div id="second">
+ </div>
+ <div id="abs">
+ </div>
+ </div>
+ </body>
+</html>
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 00000000000..7b149c7433d
--- /dev/null
+++ b/src/test/ref/position_abs_static_y_b.html
@@ -0,0 +1,28 @@
+<html>
+ <head>
+ <style>
+ #first {
+ width: 90px;
+ height: 90px;
+ border: solid 1px;
+ }
+ .row {
+ width: 90px;
+ height: 30px;
+ }
+ .center {
+ margin-left: 30px;
+ height: 20px;
+ width: 30px;
+ background: green;
+ }
+ </style>
+ </head>
+ <body>
+ <div id="first">
+ <div class="row"></div>
+ <div class="center">
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/src/test/ref/position_abs_width_percentage_a.html b/src/test/ref/position_abs_width_percentage_a.html
new file mode 100644
index 00000000000..956f8470f90
--- /dev/null
+++ b/src/test/ref/position_abs_width_percentage_a.html
@@ -0,0 +1,26 @@
+<html>
+ <head>
+ <style>
+ #first {
+ position: relative;
+ width: 100px;
+ height: 100px;
+ border: solid 1px;
+ }
+ #abs {
+ position: absolute;
+ left: 0px;
+ right: 50%;
+ top: 50%;
+ height: 50px;
+ background: green;
+ }
+ </style>
+ </head>
+ <body>
+ <div id="first">
+ <div id="abs">
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/src/test/ref/position_abs_width_percentage_b.html b/src/test/ref/position_abs_width_percentage_b.html
new file mode 100644
index 00000000000..0352d787e69
--- /dev/null
+++ b/src/test/ref/position_abs_width_percentage_b.html
@@ -0,0 +1,27 @@
+<html>
+ <head>
+ <style>
+ #first {
+ width: 100px;
+ height: 100px;
+ border: solid 1px;
+ }
+ .row {
+ width: 100px;
+ height: 50px;
+ }
+ .green_square {
+ height: 50px;
+ width: 50px;
+ background: green;
+ }
+ </style>
+ </head>
+ <body>
+ <div id="first">
+ <div class="row"></div>
+ <div class="green_square">
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/src/test/ref/position_fixed_simple_a.html b/src/test/ref/position_fixed_simple_a.html
new file mode 100644
index 00000000000..83e7cc72a78
--- /dev/null
+++ b/src/test/ref/position_fixed_simple_a.html
@@ -0,0 +1,38 @@
+<html>
+ <head>
+ <style>
+ html, body {
+ margin: 0px;
+ }
+ .box {
+ width: 100px;
+ height: 100px;
+ }
+ #rel-cont {
+ position: relative;
+ }
+ #first {
+ background: red;
+ }
+ #normal {
+ background: blue;
+ }
+ #fixed {
+ position: fixed;
+ left: 0px;
+ top: 0px;
+ background: green;
+ }
+ </style>
+ </head>
+ <body>
+ <div id="first" class="box">
+ </div>
+ <div id="normal" class="box"></div>
+ <!-- This should become empty -->
+ <div id="rel-cont">
+ <div id="fixed" class="box">
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/src/test/ref/position_fixed_simple_b.html b/src/test/ref/position_fixed_simple_b.html
new file mode 100644
index 00000000000..67ddd4208e6
--- /dev/null
+++ b/src/test/ref/position_fixed_simple_b.html
@@ -0,0 +1,24 @@
+<html>
+ <head>
+ <style>
+ html, body {
+ margin: 0px;
+ }
+ .box {
+ width: 100px;
+ height: 100px;
+ }
+ #first {
+ background: green;
+ }
+ #normal {
+ background: blue;
+ }
+ </style>
+ </head>
+ <body>
+ <div id="first" class="box">
+ </div>
+ <div id="normal" class="box"></div>
+ </body>
+</html>
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 00000000000..5d44f77006f
--- /dev/null
+++ b/src/test/ref/position_fixed_static_y_a.html
@@ -0,0 +1,40 @@
+<html>
+ <head>
+ <style>
+ html, body {
+ margin-left: 0px;
+ margin-right: 0px;
+ }
+ body {
+ margin-top: 100px;
+ }
+ #first {
+ position: relative;
+ width: 90px;
+ height: 90px;
+ border: solid 1px;
+ }
+ #second {
+ width: 30px;
+ height: 30px;
+ }
+ #fixed {
+ position: fixed;
+ left: 30px;
+ right: 30px;
+ width: 100px;
+ height: 20px;
+ background: green;
+ }
+ </style>
+ </head>
+ <body>
+ <div id="first">
+ <div id="second">
+ </div>
+ <!-- This should be at its static y position (inside #first, after #second) -->
+ <div id="fixed">
+ </div>
+ </div>
+ </body>
+</html>
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 00000000000..0b9ab5568ef
--- /dev/null
+++ b/src/test/ref/position_fixed_static_y_b.html
@@ -0,0 +1,35 @@
+<html>
+ <head>
+ <style>
+ html, body {
+ margin-left: 0px;
+ margin-right: 0px;
+ }
+ body {
+ margin-top: 100px;
+ }
+ #first {
+ width: 90px;
+ height: 90px;
+ border: solid 1px;
+ }
+ .row {
+ width: 90px;
+ height: 30px;
+ }
+ .center {
+ margin-left: 29px;
+ height: 20px;
+ width: 100px;
+ background: green;
+ }
+ </style>
+ </head>
+ <body>
+ <div id="first">
+ <div class="row"></div>
+ <div class="center">
+ </div>
+ </div>
+ </body>
+</html>