aboutsummaryrefslogtreecommitdiffstats
path: root/src/components/layout/flow.rs
diff options
context:
space:
mode:
authorJosh Matthews <josh@joshmatthews.net>2014-06-28 08:12:34 -0400
committerJosh Matthews <josh@joshmatthews.net>2014-06-28 08:12:34 -0400
commit23968efbd187de0bf19e766fd3fea8eebe432172 (patch)
tree59886fd2bcfe79870dd0932d7b093630e41355a1 /src/components/layout/flow.rs
parent9f915e9e42217c09c7ed15b14b66fc8f84e38490 (diff)
downloadservo-23968efbd187de0bf19e766fd3fea8eebe432172.tar.gz
servo-23968efbd187de0bf19e766fd3fea8eebe432172.zip
Split layout code into a separate crate.
Diffstat (limited to 'src/components/layout/flow.rs')
-rw-r--r--src/components/layout/flow.rs1086
1 files changed, 1086 insertions, 0 deletions
diff --git a/src/components/layout/flow.rs b/src/components/layout/flow.rs
new file mode 100644
index 00000000000..71924bf13b0
--- /dev/null
+++ b/src/components/layout/flow.rs
@@ -0,0 +1,1086 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+//! Servo's experimental layout system builds a tree of `Flow` and `Fragment` objects and solves
+//! layout constraints to obtain positions and display attributes of tree nodes. Positions are
+//! computed in several tree traversals driven by the fundamental data dependencies required by
+/// inline and block layout.
+///
+/// Flows are interior nodes in the layout tree and correspond closely to *flow contexts* in the
+/// CSS specification. Flows are responsible for positioning their child flow contexts and fragments.
+/// Flows have purpose-specific fields, such as auxiliary line structs, out-of-flow child
+/// lists, and so on.
+///
+/// Currently, the important types of flows are:
+///
+/// * `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 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.
+///
+/// * `InlineFlow`: A flow that establishes an inline context. It has a flat list of child
+/// fragments/flows that are subject to inline layout and line breaking and structs to represent
+/// line breaks and mapping to CSS boxes, for the purpose of handling `getClientRects()` and
+/// similar methods.
+
+use css::node_style::StyledNode;
+use block::BlockFlow;
+use context::LayoutContext;
+use floats::Floats;
+use flow_list::{FlowList, Link, FlowListIterator, MutFlowListIterator};
+use flow_ref::FlowRef;
+use fragment::{Fragment, TableRowFragment, TableCellFragment};
+use incremental::RestyleDamage;
+use inline::InlineFlow;
+use model::{CollapsibleMargins, IntrinsicWidths, MarginCollapseInfo};
+use parallel::FlowParallelInfo;
+use table_wrapper::TableWrapperFlow;
+use table::TableFlow;
+use table_colgroup::TableColGroupFlow;
+use table_rowgroup::TableRowGroupFlow;
+use table_row::TableRowFlow;
+use table_caption::TableCaptionFlow;
+use table_cell::TableCellFlow;
+use wrapper::ThreadSafeLayoutNode;
+
+use collections::dlist::DList;
+use geom::point::Point2D;
+use geom::rect::Rect;
+use geom::size::Size2D;
+use gfx::display_list::DisplayList;
+use gfx::render_task::RenderLayer;
+use servo_msg::compositor_msg::LayerId;
+use servo_util::geometry::Au;
+use std::mem;
+use std::fmt;
+use std::iter::Zip;
+use std::num::Zero;
+use std::sync::atomics::{AtomicUint, Relaxed, SeqCst};
+use std::slice::MutItems;
+use style::computed_values::{clear, position, text_align};
+
+/// Virtual methods that make up a float context.
+///
+/// Note that virtual methods have a cost; we should not overuse them in Servo. Consider adding
+/// methods to `ImmutableFlowUtils` or `MutableFlowUtils` before adding more methods here.
+pub trait Flow: fmt::Show + ToStr + Share {
+ // RTTI
+ //
+ // TODO(pcwalton): Use Rust's RTTI, once that works.
+
+ /// Returns the class of flow that this is.
+ fn class(&self) -> FlowClass;
+
+ /// If this is a block flow, returns the underlying object. Fails otherwise.
+ fn as_block<'a>(&'a mut self) -> &'a mut BlockFlow {
+ debug!("called as_block() on a flow of type {}", self.class());
+ fail!("called as_block() on a non-block flow")
+ }
+
+ /// If this is an inline flow, returns the underlying object, borrowed immutably. Fails
+ /// otherwise.
+ fn as_immutable_inline<'a>(&'a self) -> &'a InlineFlow {
+ fail!("called as_immutable_inline() on a non-inline flow")
+ }
+
+ /// If this is an inline flow, returns the underlying object. Fails otherwise.
+ fn as_inline<'a>(&'a mut self) -> &'a mut InlineFlow {
+ fail!("called as_inline() on a non-inline flow")
+ }
+
+ /// If this is a table wrapper flow, returns the underlying object. Fails otherwise.
+ fn as_table_wrapper<'a>(&'a mut self) -> &'a mut TableWrapperFlow {
+ fail!("called as_table_wrapper() on a non-tablewrapper flow")
+ }
+
+ /// If this is a table flow, returns the underlying object. Fails otherwise.
+ fn as_table<'a>(&'a mut self) -> &'a mut TableFlow {
+ fail!("called as_table() on a non-table flow")
+ }
+
+ /// If this is a table colgroup flow, returns the underlying object. Fails otherwise.
+ fn as_table_colgroup<'a>(&'a mut self) -> &'a mut TableColGroupFlow {
+ fail!("called as_table_colgroup() on a non-tablecolgroup flow")
+ }
+
+ /// If this is a table rowgroup flow, returns the underlying object. Fails otherwise.
+ fn as_table_rowgroup<'a>(&'a mut self) -> &'a mut TableRowGroupFlow {
+ fail!("called as_table_rowgroup() on a non-tablerowgroup flow")
+ }
+
+ /// If this is a table row flow, returns the underlying object. Fails otherwise.
+ fn as_table_row<'a>(&'a mut self) -> &'a mut TableRowFlow {
+ fail!("called as_table_row() on a non-tablerow flow")
+ }
+
+ /// If this is a table cell flow, returns the underlying object. Fails otherwise.
+ fn as_table_caption<'a>(&'a mut self) -> &'a mut TableCaptionFlow {
+ fail!("called as_table_caption() on a non-tablecaption flow")
+ }
+
+ /// If this is a table cell flow, returns the underlying object. Fails otherwise.
+ fn as_table_cell<'a>(&'a mut self) -> &'a mut TableCellFlow {
+ fail!("called as_table_cell() on a non-tablecell flow")
+ }
+
+ /// If this is a table row or table rowgroup or table flow, returns column widths.
+ /// Fails otherwise.
+ fn col_widths<'a>(&'a mut self) -> &'a mut Vec<Au> {
+ fail!("called col_widths() on an other flow than table-row/table-rowgroup/table")
+ }
+
+ /// If this is a table row flow or table rowgroup flow or table flow, returns column min widths.
+ /// Fails otherwise.
+ fn col_min_widths<'a>(&'a self) -> &'a Vec<Au> {
+ fail!("called col_min_widths() on an other flow than table-row/table-rowgroup/table")
+ }
+
+ /// If this is a table row flow or table rowgroup flow or table flow, returns column min widths.
+ /// Fails otherwise.
+ fn col_pref_widths<'a>(&'a self) -> &'a Vec<Au> {
+ fail!("called col_pref_widths() on an other flow than table-row/table-rowgroup/table")
+ }
+
+ // Main methods
+
+ /// Pass 1 of reflow: computes minimum and preferred widths.
+ ///
+ /// Recursively (bottom-up) determine the flow's minimum and preferred widths. When called on
+ /// this flow, all child flows have had their minimum and preferred widths set. This function
+ /// must decide minimum/preferred widths based on its children's widths and the dimensions of
+ /// any boxes it is responsible for flowing.
+ fn bubble_widths(&mut self, _ctx: &mut LayoutContext) {
+ fail!("bubble_widths not yet implemented")
+ }
+
+ /// Pass 2 of reflow: computes width.
+ fn assign_widths(&mut self, _ctx: &mut LayoutContext) {
+ fail!("assign_widths not yet implemented")
+ }
+
+ /// Pass 3a of reflow: computes height.
+ fn assign_height(&mut self, _ctx: &mut LayoutContext) {
+ fail!("assign_height not yet implemented")
+ }
+
+ /// Assigns heights in-order; or, if this is a float, places the float. The default
+ /// implementation simply assigns heights if this flow is impacted by floats. Returns true if
+ /// this child was impacted by floats or false otherwise.
+ fn assign_height_for_inorder_child_if_necessary(&mut self, layout_context: &mut LayoutContext)
+ -> bool {
+ let impacted = base(&*self).flags.impacted_by_floats();
+ if impacted {
+ self.assign_height(layout_context);
+ }
+ impacted
+ }
+
+ /// Phase 4 of reflow: computes absolute positions.
+ fn compute_absolute_position(&mut self) {
+ // The default implementation is a no-op.
+ }
+
+ /// Returns the direction that this flow clears floats in, if any.
+ fn float_clearance(&self) -> clear::T {
+ clear::none
+ }
+
+ /// Returns true if this float is a block formatting context and false otherwise. The default
+ /// implementation returns false.
+ fn is_block_formatting_context(&self, _only_impactable_by_floats: bool) -> bool {
+ false
+ }
+
+ fn compute_collapsible_top_margin(&mut self,
+ _layout_context: &mut LayoutContext,
+ _margin_collapse_info: &mut MarginCollapseInfo) {
+ // The default implementation is a no-op.
+ }
+
+ /// 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
+ }
+
+ /// Returns true if this is an absolute containing block.
+ fn is_absolute_containing_block(&self) -> bool {
+ false
+ }
+
+ /// Return the dimensions of the containing block generated by this flow for absolutely-
+ /// positioned descendants. For block flows, this is the padding box.
+ fn generated_containing_block_rect(&self) -> Rect<Au> {
+ fail!("generated_containing_block_position not yet implemented for this flow")
+ }
+
+ /// Returns a layer ID for the given fragment.
+ fn layer_id(&self, fragment_id: uint) -> LayerId {
+ unsafe {
+ let pointer: uint = mem::transmute(self);
+ LayerId(pointer, fragment_id)
+ }
+ }
+}
+
+// Base access
+
+#[inline(always)]
+pub fn base<'a>(this: &'a Flow) -> &'a BaseFlow {
+ unsafe {
+ let (_, ptr): (uint, &BaseFlow) = mem::transmute(this);
+ ptr
+ }
+}
+
+/// Iterates over the children of this immutable flow.
+pub fn imm_child_iter<'a>(flow: &'a Flow) -> FlowListIterator<'a> {
+ base(flow).children.iter()
+}
+
+#[inline(always)]
+pub fn mut_base<'a>(this: &'a mut Flow) -> &'a mut BaseFlow {
+ unsafe {
+ let (_, ptr): (uint, &mut BaseFlow) = mem::transmute(this);
+ ptr
+ }
+}
+
+/// Iterates over the children of this flow.
+pub fn child_iter<'a>(flow: &'a mut Flow) -> MutFlowListIterator<'a> {
+ mut_base(flow).children.mut_iter()
+}
+
+pub trait ImmutableFlowUtils {
+ // Convenience functions
+
+ /// Returns true if this flow is a block or a float flow.
+ fn is_block_like(self) -> bool;
+
+ /// Returns true if this flow is a table flow.
+ fn is_table(self) -> bool;
+
+ /// Returns true if this flow is a table caption flow.
+ fn is_table_caption(self) -> bool;
+
+ /// Returns true if this flow is a proper table child.
+ fn is_proper_table_child(self) -> bool;
+
+ /// Returns true if this flow is a table row flow.
+ fn is_table_row(self) -> bool;
+
+ /// Returns true if this flow is a table cell flow.
+ fn is_table_cell(self) -> bool;
+
+ /// Returns true if this flow is a table colgroup flow.
+ fn is_table_colgroup(self) -> bool;
+
+ /// Returns true if this flow is a table rowgroup flow.
+ fn is_table_rowgroup(self) -> bool;
+
+ /// Returns true if this flow is one of table-related flows.
+ fn is_table_kind(self) -> bool;
+
+ /// Returns true if anonymous flow is needed between this flow and child flow.
+ fn need_anonymous_flow(self, child: &Flow) -> bool;
+
+ /// Generates missing child flow of this flow.
+ fn generate_missing_child_flow(self, node: &ThreadSafeLayoutNode) -> FlowRef;
+
+ /// Returns true if this flow has no children.
+ fn is_leaf(self) -> bool;
+
+ /// Returns the number of children that this flow possesses.
+ fn child_count(self) -> uint;
+
+ /// Return true if this flow is a Block Container.
+ fn is_block_container(self) -> bool;
+
+ /// Returns true if this flow is a block flow.
+ fn is_block_flow(self) -> bool;
+
+ /// Returns true if this flow is an inline flow.
+ fn is_inline_flow(self) -> bool;
+
+ /// Dumps the flow tree for debugging.
+ fn dump(self);
+
+ /// Dumps the flow tree for debugging, with a prefix to indicate that we're at the given level.
+ fn dump_with_level(self, level: uint);
+}
+
+pub trait MutableFlowUtils {
+ // Traversals
+
+ /// Traverses the tree in preorder.
+ fn traverse_preorder<T:PreorderFlowTraversal>(self, traversal: &mut T) -> bool;
+
+ /// Traverses the tree in postorder.
+ fn traverse_postorder<T:PostorderFlowTraversal>(self, traversal: &mut T) -> bool;
+
+ // Mutators
+
+ /// Computes the overflow region for this flow.
+ fn store_overflow(self, _: &mut LayoutContext);
+
+ /// Builds the display lists for this flow.
+ fn build_display_list(self, layout_context: &LayoutContext);
+}
+
+pub trait MutableOwnedFlowUtils {
+ /// 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);
+}
+
+#[deriving(PartialEq, Show)]
+pub enum FlowClass {
+ BlockFlowClass,
+ InlineFlowClass,
+ TableWrapperFlowClass,
+ TableFlowClass,
+ TableColGroupFlowClass,
+ TableRowGroupFlowClass,
+ TableRowFlowClass,
+ TableCaptionFlowClass,
+ TableCellFlowClass,
+}
+
+/// A top-down traversal.
+pub trait PreorderFlowTraversal {
+ /// The operation to perform. Return true to continue or false to stop.
+ fn process(&mut self, flow: &mut Flow) -> bool;
+
+ /// Returns true if this node should be pruned. If this returns true, we skip the operation
+ /// entirely and do not process any descendant nodes. This is called *before* child nodes are
+ /// visited. The default implementation never prunes any nodes.
+ fn should_prune(&mut self, _flow: &mut Flow) -> bool {
+ false
+ }
+}
+
+/// A bottom-up traversal, with a optional in-order pass.
+pub trait PostorderFlowTraversal {
+ /// The operation to perform. Return true to continue or false to stop.
+ fn process(&mut self, flow: &mut Flow) -> bool;
+
+ /// Returns false if this node must be processed in-order. If this returns false, we skip the
+ /// operation for this node, but continue processing the ancestors. This is called *after*
+ /// child nodes are visited.
+ fn should_process(&mut self, _flow: &mut Flow) -> bool {
+ true
+ }
+
+ /// Returns true if this node should be pruned. If this returns true, we skip the operation
+ /// entirely and do not process any descendant nodes. This is called *before* child nodes are
+ /// visited. The default implementation never prunes any nodes.
+ fn should_prune(&mut self, _flow: &mut Flow) -> bool {
+ false
+ }
+}
+
+/// Flags used in flows, tightly packed to save space.
+#[deriving(Clone)]
+pub struct FlowFlags(pub u8);
+
+/// The bitmask of flags that represent the `has_left_floated_descendants` and
+/// `has_right_floated_descendants` fields.
+///
+/// NB: If you update this field, you must update the bitfields below.
+static HAS_FLOATED_DESCENDANTS_BITMASK: u8 = 0b0000_0011;
+
+// Whether this flow has descendants that float left in the same block formatting context.
+bitfield!(FlowFlags, has_left_floated_descendants, set_has_left_floated_descendants, 0b0000_0001)
+
+// Whether this flow has descendants that float right in the same block formatting context.
+bitfield!(FlowFlags, has_right_floated_descendants, set_has_right_floated_descendants, 0b0000_0010)
+
+// Whether this flow is impacted by floats to the left in the same block formatting context (i.e.
+// its height depends on some prior flows with `float: left`).
+bitfield!(FlowFlags, impacted_by_left_floats, set_impacted_by_left_floats, 0b0000_0100)
+
+// Whether this flow is impacted by floats to the right in the same block formatting context (i.e.
+// its height depends on some prior flows with `float: right`).
+bitfield!(FlowFlags, impacted_by_right_floats, set_impacted_by_right_floats, 0b0000_1000)
+
+/// The bitmask of flags that represent the text alignment field.
+///
+/// NB: If you update this field, you must update the bitfields below.
+static TEXT_ALIGN_BITMASK: u8 = 0b0011_0000;
+
+/// The number of bits we must shift off to handle the text alignment field.
+///
+/// NB: If you update this field, you must update the bitfields below.
+static TEXT_ALIGN_SHIFT: u8 = 4;
+
+// Whether this flow contains a flow that has its own layer within the same absolute containing
+// block.
+bitfield!(FlowFlags,
+ layers_needed_for_descendants,
+ set_layers_needed_for_descendants,
+ 0b0100_0000)
+
+// Whether this flow must have its own layer. Even if this flag is not set, it might get its own
+// layer if it's deemed to be likely to overlap flows with their own layer.
+bitfield!(FlowFlags, needs_layer, set_needs_layer, 0b1000_0000)
+
+impl FlowFlags {
+ /// Creates a new set of flow flags.
+ pub fn new() -> FlowFlags {
+ FlowFlags(0)
+ }
+
+ /// Propagates text alignment flags from an appropriate parent flow per CSS 2.1.
+ ///
+ /// FIXME(#2265, pcwalton): It would be cleaner and faster to make this a derived CSS property
+ /// `-servo-text-align-in-effect`.
+ pub fn propagate_text_alignment_from_parent(&mut self, parent_flags: FlowFlags) {
+ self.set_text_align_override(parent_flags);
+ }
+
+ #[inline]
+ pub fn text_align(self) -> text_align::T {
+ let FlowFlags(ff) = self;
+ FromPrimitive::from_u8((ff & TEXT_ALIGN_BITMASK) >> TEXT_ALIGN_SHIFT).unwrap()
+ }
+
+ #[inline]
+ pub fn set_text_align(&mut self, value: text_align::T) {
+ let FlowFlags(ff) = *self;
+ *self = FlowFlags((ff & !TEXT_ALIGN_BITMASK) | ((value as u8) << TEXT_ALIGN_SHIFT))
+ }
+
+ #[inline]
+ pub fn set_text_align_override(&mut self, parent: FlowFlags) {
+ let FlowFlags(ff) = *self;
+ let FlowFlags(pff) = parent;
+ *self = FlowFlags(ff | (pff & TEXT_ALIGN_BITMASK))
+ }
+
+ #[inline]
+ pub fn union_floated_descendants_flags(&mut self, other: FlowFlags) {
+ let FlowFlags(my_flags) = *self;
+ let FlowFlags(other_flags) = other;
+ *self = FlowFlags(my_flags | (other_flags & HAS_FLOATED_DESCENDANTS_BITMASK))
+ }
+
+ #[inline]
+ pub fn impacted_by_floats(&self) -> bool {
+ self.impacted_by_left_floats() || self.impacted_by_right_floats()
+ }
+}
+
+/// The Descendants of a flow.
+///
+/// Also, details about their position wrt this flow.
+pub struct Descendants {
+ /// Links to every descendant. This must be private because it is unsafe to leak `FlowRef`s to
+ /// layout.
+ descendant_links: Vec<FlowRef>,
+
+ /// Static y offsets of all descendants from the start of this flow box.
+ pub static_y_offsets: Vec<Au>,
+}
+
+impl Descendants {
+ pub fn new() -> Descendants {
+ Descendants {
+ descendant_links: Vec::new(),
+ static_y_offsets: Vec::new(),
+ }
+ }
+
+ pub fn len(&self) -> uint {
+ self.descendant_links.len()
+ }
+
+ pub fn push(&mut self, given_descendant: FlowRef) {
+ 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, 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> {
+ DescendantIter {
+ iter: 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> {
+ let descendant_iter = DescendantIter {
+ iter: self.descendant_links.mut_slice_from(0).mut_iter(),
+ };
+ descendant_iter.zip(self.static_y_offsets.mut_slice_from(0).mut_iter())
+ }
+}
+
+pub type AbsDescendants = Descendants;
+
+pub struct DescendantIter<'a> {
+ iter: MutItems<'a, FlowRef>,
+}
+
+impl<'a> Iterator<&'a mut Flow> for DescendantIter<'a> {
+ fn next(&mut self) -> Option<&'a mut Flow> {
+ match self.iter.next() {
+ None => None,
+ Some(ref mut flow) => {
+ unsafe {
+ let result: &'a mut Flow = mem::transmute(flow.get_mut());
+ Some(result)
+ }
+ }
+ }
+ }
+}
+
+pub type DescendantOffsetIter<'a> = Zip<DescendantIter<'a>, MutItems<'a, Au>>;
+
+/// Information needed to compute absolute (i.e. viewport-relative) flow positions (not to be
+/// confused with absolutely-positioned flows).
+pub struct AbsolutePositionInfo {
+ /// The size of the containing block for relatively-positioned descendants.
+ pub relative_containing_block_size: Size2D<Au>,
+ /// The position of the absolute containing block.
+ pub absolute_containing_block_position: Point2D<Au>,
+ /// Whether the absolute containing block forces positioned descendants to be layerized.
+ ///
+ /// FIXME(pcwalton): Move into `FlowFlags`.
+ pub layers_needed_for_positioned_flows: bool,
+}
+
+impl AbsolutePositionInfo {
+ pub fn new() -> AbsolutePositionInfo {
+ // FIXME(pcwalton): The initial relative containing block size should be equal to the size
+ // of the root layer.
+ AbsolutePositionInfo {
+ relative_containing_block_size: Size2D::zero(),
+ absolute_containing_block_position: Zero::zero(),
+ layers_needed_for_positioned_flows: false,
+ }
+ }
+}
+
+/// Data common to all flows.
+pub struct BaseFlow {
+ /// NB: Must be the first element.
+ ///
+ /// The necessity of this will disappear once we have dynamically-sized types.
+ ref_count: AtomicUint,
+
+ pub restyle_damage: RestyleDamage,
+
+ /// The children of this flow.
+ pub children: FlowList,
+ pub next_sibling: Link,
+ pub prev_sibling: Link,
+
+ /* layout computations */
+ // TODO: min/pref and position are used during disjoint phases of
+ // layout; maybe combine into a single enum to save space.
+ pub intrinsic_widths: IntrinsicWidths,
+
+ /// 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 with respect to its *containing block*.
+ ///
+ /// This does not include margins in the block flow direction, because those can collapse. So
+ /// for the block direction (usually vertical), this represents the *border box*. For the
+ /// inline direction (usually horizontal), this represents the *margin box*.
+ pub position: Rect<Au>,
+
+ /// The amount of overflow of this flow, relative to the containing block. Must include all the
+ /// pixels of all the display list items for correct invalidation.
+ pub overflow: Rect<Au>,
+
+ /// Data used during parallel traversals.
+ ///
+ /// TODO(pcwalton): Group with other transient data to save space.
+ pub parallel: FlowParallelInfo,
+
+ /// The floats next to this flow.
+ pub floats: Floats,
+
+ /// The collapsible margins for this flow, if any.
+ pub collapsible_margins: CollapsibleMargins,
+
+ /// The position of this flow in page coordinates, computed during display list construction.
+ pub abs_position: Point2D<Au>,
+
+ /// Details about descendants with position 'absolute' or 'fixed' for which we are the
+ /// containing block. This is in tree order. This includes any direct children.
+ pub abs_descendants: AbsDescendants,
+
+ /// Offset wrt the nearest positioned ancestor - aka the Containing Block
+ /// for any absolutely positioned elements.
+ pub absolute_static_x_offset: Au,
+
+ /// Offset wrt the Initial Containing Block.
+ pub fixed_static_x_offset: Au,
+
+ /// Reference to the Containing Block, if this flow is absolutely positioned.
+ pub absolute_cb: ContainingBlockLink,
+
+ /// Information needed to compute absolute (i.e. viewport-relative) flow positions (not to be
+ /// confused with absolutely-positioned flows).
+ ///
+ /// FIXME(pcwalton): Merge with `absolute_static_x_offset` and `fixed_static_x_offset` above?
+ pub absolute_position_info: AbsolutePositionInfo,
+
+ /// The unflattened display items for this flow.
+ pub display_list: DisplayList,
+
+ /// Any layers that we're bubbling up, in a linked list.
+ pub layers: DList<RenderLayer>,
+
+ /// Various flags for flows, tightly packed to save space.
+ pub flags: FlowFlags,
+}
+
+#[unsafe_destructor]
+impl Drop for BaseFlow {
+ fn drop(&mut self) {
+ if self.ref_count.load(SeqCst) != 0 {
+ fail!("Flow destroyed before its ref count hit zero—this is unsafe!")
+ }
+ }
+}
+
+impl BaseFlow {
+ #[inline]
+ pub fn new(node: ThreadSafeLayoutNode) -> BaseFlow {
+ BaseFlow {
+ ref_count: AtomicUint::new(1),
+
+ restyle_damage: node.restyle_damage(),
+
+ children: FlowList::new(),
+ next_sibling: None,
+ prev_sibling: None,
+
+ intrinsic_widths: IntrinsicWidths::new(),
+ position: Rect::zero(),
+ overflow: Rect::zero(),
+
+ parallel: FlowParallelInfo::new(),
+
+ floats: Floats::new(),
+ collapsible_margins: CollapsibleMargins::new(),
+ abs_position: Point2D(Au::new(0), Au::new(0)),
+ abs_descendants: Descendants::new(),
+ absolute_static_x_offset: Au::new(0),
+ fixed_static_x_offset: Au::new(0),
+ absolute_cb: ContainingBlockLink::new(),
+ display_list: DisplayList::new(),
+ layers: DList::new(),
+ absolute_position_info: AbsolutePositionInfo::new(),
+
+ flags: FlowFlags::new(),
+ }
+ }
+
+ pub fn child_iter<'a>(&'a mut self) -> MutFlowListIterator<'a> {
+ self.children.mut_iter()
+ }
+
+ pub unsafe fn ref_count<'a>(&'a self) -> &'a AtomicUint {
+ &self.ref_count
+ }
+}
+
+impl<'a> ImmutableFlowUtils for &'a Flow {
+ /// Returns true if this flow is a block or a float flow.
+ fn is_block_like(self) -> bool {
+ match self.class() {
+ BlockFlowClass => true,
+ _ => false,
+ }
+ }
+
+ /// Returns true if this flow is a proper table child.
+ /// 'Proper table child' is defined as table-row flow, table-rowgroup flow,
+ /// table-column-group flow, or table-caption flow.
+ fn is_proper_table_child(self) -> bool {
+ match self.class() {
+ TableRowFlowClass | TableRowGroupFlowClass |
+ TableColGroupFlowClass | TableCaptionFlowClass => true,
+ _ => false,
+ }
+ }
+
+ /// Returns true if this flow is a table row flow.
+ fn is_table_row(self) -> bool {
+ match self.class() {
+ TableRowFlowClass => true,
+ _ => false,
+ }
+ }
+
+ /// Returns true if this flow is a table cell flow.
+ fn is_table_cell(self) -> bool {
+ match self.class() {
+ TableCellFlowClass => true,
+ _ => false,
+ }
+ }
+
+ /// Returns true if this flow is a table colgroup flow.
+ fn is_table_colgroup(self) -> bool {
+ match self.class() {
+ TableColGroupFlowClass => true,
+ _ => false,
+ }
+ }
+
+ /// Returns true if this flow is a table flow.
+ fn is_table(self) -> bool {
+ match self.class() {
+ TableFlowClass => true,
+ _ => false,
+ }
+ }
+
+ /// Returns true if this flow is a table caption flow.
+ fn is_table_caption(self) -> bool {
+ match self.class() {
+ TableCaptionFlowClass => true,
+ _ => false,
+ }
+ }
+
+ /// Returns true if this flow is a table rowgroup flow.
+ fn is_table_rowgroup(self) -> bool {
+ match self.class() {
+ TableRowGroupFlowClass => true,
+ _ => false,
+ }
+ }
+
+ /// Returns true if this flow is one of table-related flows.
+ fn is_table_kind(self) -> bool {
+ match self.class() {
+ TableWrapperFlowClass | TableFlowClass |
+ TableColGroupFlowClass | TableRowGroupFlowClass |
+ TableRowFlowClass | TableCaptionFlowClass | TableCellFlowClass => true,
+ _ => false,
+ }
+ }
+
+ /// Returns true if anonymous flow is needed between this flow and child flow.
+ /// Spec: http://www.w3.org/TR/CSS21/tables.html#anonymous-boxes
+ fn need_anonymous_flow(self, child: &Flow) -> bool {
+ match self.class() {
+ TableFlowClass => !child.is_proper_table_child(),
+ TableRowGroupFlowClass => !child.is_table_row(),
+ TableRowFlowClass => !child.is_table_cell(),
+ _ => false
+ }
+ }
+
+ /// Generates missing child flow of this flow.
+ fn generate_missing_child_flow(self, node: &ThreadSafeLayoutNode) -> FlowRef {
+ let flow = match self.class() {
+ TableFlowClass | TableRowGroupFlowClass => {
+ let fragment = Fragment::new_anonymous_table_fragment(node, TableRowFragment);
+ box TableRowFlow::from_node_and_fragment(node, fragment) as Box<Flow>
+ },
+ TableRowFlowClass => {
+ let fragment = Fragment::new_anonymous_table_fragment(node, TableCellFragment);
+ box TableCellFlow::from_node_and_fragment(node, fragment) as Box<Flow>
+ },
+ _ => {
+ fail!("no need to generate a missing child")
+ }
+ };
+ FlowRef::new(flow)
+ }
+
+ /// Returns true if this flow has no children.
+ fn is_leaf(self) -> bool {
+ base(self).children.len() == 0
+ }
+
+ /// Returns the number of children that this flow possesses.
+ fn child_count(self) -> uint {
+ base(self).children.len()
+ }
+
+ /// Return true if this flow is a Block Container.
+ ///
+ /// Except for table fragments and replaced elements, block-level fragments (`BlockFlow`) are
+ /// also block container fragments.
+ /// Non-replaced inline blocks and non-replaced table cells are also block
+ /// containers.
+ fn is_block_container(self) -> bool {
+ match self.class() {
+ // TODO: Change this when inline-blocks are supported.
+ BlockFlowClass | TableCaptionFlowClass | TableCellFlowClass => {
+ // FIXME: Actually check the type of the node
+ self.child_count() != 0
+ }
+ _ => false,
+ }
+ }
+
+ /// Returns true if this flow is a block flow.
+ fn is_block_flow(self) -> bool {
+ match self.class() {
+ BlockFlowClass => true,
+ _ => false,
+ }
+ }
+
+ /// Returns true if this flow is an inline flow.
+ fn is_inline_flow(self) -> bool {
+ match self.class() {
+ InlineFlowClass => true,
+ _ => false,
+ }
+ }
+
+ /// Dumps the flow tree for debugging.
+ fn dump(self) {
+ self.dump_with_level(0)
+ }
+
+ /// Dumps the flow tree for debugging, with a prefix to indicate that we're at the given level.
+ fn dump_with_level(self, level: uint) {
+ let mut indent = String::new();
+ for _ in range(0, level) {
+ indent.push_str("| ")
+ }
+ debug!("{}+ {}", indent, self.to_str());
+ for kid in imm_child_iter(self) {
+ kid.dump_with_level(level + 1)
+ }
+ }
+}
+
+impl<'a> MutableFlowUtils for &'a mut Flow {
+ /// Traverses the tree in preorder.
+ fn traverse_preorder<T:PreorderFlowTraversal>(self, traversal: &mut T) -> bool {
+ if traversal.should_prune(self) {
+ return true
+ }
+
+ if !traversal.process(self) {
+ return false
+ }
+
+ for kid in child_iter(self) {
+ if !kid.traverse_preorder(traversal) {
+ return false
+ }
+ }
+
+ true
+ }
+
+ /// Traverses the tree in postorder.
+ fn traverse_postorder<T:PostorderFlowTraversal>(self, traversal: &mut T) -> bool {
+ if traversal.should_prune(self) {
+ return true
+ }
+
+ for kid in child_iter(self) {
+ if !kid.traverse_postorder(traversal) {
+ return false
+ }
+ }
+
+ if !traversal.should_process(self) {
+ return true
+ }
+
+ traversal.process(self)
+ }
+
+ /// 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;
+
+ 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)
+ }
+
+ // FIXME(#2004, pcwalton): This is wrong for `position: fixed`.
+ for descendant_link in mut_base(self).abs_descendants.iter() {
+ let mut kid_overflow = base(descendant_link).overflow;
+ kid_overflow = kid_overflow.translate(&my_position.origin);
+ overflow = overflow.union(&kid_overflow)
+ }
+ }
+ mut_base(self).overflow = overflow;
+ }
+
+ /// Push display items for current flow and its descendants onto the appropriate display lists
+ /// of the given stacking context.
+ ///
+ /// Arguments:
+ ///
+ /// * `builder`: The display list builder, which contains information used during the entire
+ /// display list building pass.
+ ///
+ /// * `info`: Per-flow display list building information.
+ fn build_display_list(self, layout_context: &LayoutContext) {
+ debug!("Flow: building display list");
+ match self.class() {
+ BlockFlowClass => self.as_block().build_display_list_block(layout_context),
+ InlineFlowClass => self.as_inline().build_display_list_inline(layout_context),
+ TableWrapperFlowClass => {
+ self.as_table_wrapper().build_display_list_table_wrapper(layout_context)
+ }
+ TableFlowClass => self.as_table().build_display_list_table(layout_context),
+ TableRowGroupFlowClass => {
+ self.as_table_rowgroup().build_display_list_table_rowgroup(layout_context)
+ }
+ TableRowFlowClass => self.as_table_row().build_display_list_table_row(layout_context),
+ TableCaptionFlowClass => {
+ self.as_table_caption().build_display_list_table_caption(layout_context)
+ }
+ TableCellFlowClass => {
+ self.as_table_cell().build_display_list_table_cell(layout_context)
+ }
+ TableColGroupFlowClass => {
+ // Nothing to do here, as column groups don't render.
+ }
+ }
+ }
+}
+
+impl MutableOwnedFlowUtils for FlowRef {
+ /// Set absolute descendants for this flow.
+ ///
+ /// Set yourself as the Containing Block for all the absolute descendants.
+ ///
+ /// This is called during flow construction, so nothing else can be accessing the descendant
+ /// flows. This is enforced by the fact that we have a mutable `FlowRef`, which only flow
+ /// construction is allowed to possess.
+ fn set_abs_descendants(&mut self, abs_descendants: AbsDescendants) {
+ let this = self.clone();
+
+ let block = self.get_mut().as_block();
+ block.base.abs_descendants = abs_descendants;
+ block.base
+ .parallel
+ .children_and_absolute_descendant_count
+ .fetch_add(block.base.abs_descendants.len() as int, Relaxed);
+
+ for descendant_link in block.base.abs_descendants.iter() {
+ let base = mut_base(descendant_link);
+ base.absolute_cb.set(this.clone());
+ }
+ }
+}
+
+/// A link to a flow's containing block.
+///
+/// This cannot safely be a `Flow` pointer because this is a pointer *up* the tree, not *down* the
+/// tree. A pointer up the tree is unsafe during layout because it can be used to access a node
+/// with an immutable reference while that same node is being laid out, causing possible iterator
+/// invalidation and use-after-free.
+///
+/// FIXME(pcwalton): I think this would be better with a borrow flag instead of `unsafe`.
+pub struct ContainingBlockLink {
+ /// The pointer up to the containing block.
+ link: Option<FlowRef>,
+}
+
+impl ContainingBlockLink {
+ fn new() -> ContainingBlockLink {
+ ContainingBlockLink {
+ link: None,
+ }
+ }
+
+ fn set(&mut self, link: FlowRef) {
+ self.link = Some(link)
+ }
+
+ pub unsafe fn get<'a>(&'a mut self) -> &'a mut Option<FlowRef> {
+ &mut self.link
+ }
+
+ #[inline]
+ pub fn generated_containing_block_rect(&mut self) -> Rect<Au> {
+ match self.link {
+ None => fail!("haven't done it"),
+ Some(ref mut link) => link.get_mut().generated_containing_block_rect(),
+ }
+ }
+}
+