aboutsummaryrefslogtreecommitdiffstats
path: root/src/components/main/layout
diff options
context:
space:
mode:
authorPatrick Walton <pcwalton@mimiga.net>2013-11-09 21:39:39 -0800
committerPatrick Walton <pcwalton@mimiga.net>2013-11-18 11:24:11 -0800
commit155befe10dc56cfb2dfbf0cca7b652293dba9753 (patch)
tree43c3e51689cd42a3fb623eda84740f49dd667f5e /src/components/main/layout
parent37f9427b6c53b90234e82d219217a97c10811243 (diff)
downloadservo-155befe10dc56cfb2dfbf0cca7b652293dba9753.tar.gz
servo-155befe10dc56cfb2dfbf0cca7b652293dba9753.zip
Rewrite flow construction to be incrementalizable and parallelizable.
This replaces flow construction with a strict bottom-up tree traversal, allowing for parallelism. Each step of the traversal creates a flow or a `ConstructionItem`, similar to how Gecko works. {ib} splits are handled by not creating `InlineFlow`s until the containing block is reached. This should be able to be incrementalized by storing the `Flow` from layout to layout, and performing fixups during flow construction and/or wiping containing blocks in a previous pass.
Diffstat (limited to 'src/components/main/layout')
-rw-r--r--src/components/main/layout/block.rs12
-rw-r--r--src/components/main/layout/box.rs2
-rw-r--r--src/components/main/layout/box_builder.rs673
-rw-r--r--src/components/main/layout/construct.rs614
-rw-r--r--src/components/main/layout/extra.rs16
-rw-r--r--src/components/main/layout/float.rs48
-rw-r--r--src/components/main/layout/float_context.rs23
-rw-r--r--src/components/main/layout/flow.rs4
-rw-r--r--src/components/main/layout/inline.rs19
-rw-r--r--src/components/main/layout/layout_task.rs200
-rw-r--r--src/components/main/layout/util.rs60
11 files changed, 880 insertions, 791 deletions
diff --git a/src/components/main/layout/block.rs b/src/components/main/layout/block.rs
index 24565896ff7..bae5add6f09 100644
--- a/src/components/main/layout/block.rs
+++ b/src/components/main/layout/block.rs
@@ -40,6 +40,14 @@ impl BlockFlow {
}
}
+ pub fn from_box(base: FlowData, box: @RenderBox) -> BlockFlow {
+ BlockFlow {
+ base: base,
+ box: Some(box),
+ is_root: false
+ }
+ }
+
pub fn new_root(base: FlowData) -> BlockFlow {
BlockFlow {
base: base,
@@ -499,6 +507,10 @@ impl FlowContext for BlockFlow {
*first_in_flow = false;
}
+ fn mark_as_root(&mut self) {
+ self.is_root = true
+ }
+
fn debug_str(&self) -> ~str {
if self.is_root {
~"BlockFlow(root)"
diff --git a/src/components/main/layout/box.rs b/src/components/main/layout/box.rs
index e2ca7b5d164..7f4514e3c4e 100644
--- a/src/components/main/layout/box.rs
+++ b/src/components/main/layout/box.rs
@@ -225,6 +225,7 @@ pub struct ImageRenderBox {
}
impl ImageRenderBox {
+ #[inline]
pub fn new(base: RenderBoxBase, image_url: Url, local_image_cache: @mut LocalImageCache)
-> ImageRenderBox {
assert!(base.node.is_image_element());
@@ -523,6 +524,7 @@ pub struct UnscannedTextRenderBox {
impl UnscannedTextRenderBox {
/// Creates a new instance of `UnscannedTextRenderBox`.
+ #[inline(always)]
pub fn new(base: RenderBoxBase) -> UnscannedTextRenderBox {
assert!(base.node.is_text());
diff --git a/src/components/main/layout/box_builder.rs b/src/components/main/layout/box_builder.rs
deleted file mode 100644
index ee492c85304..00000000000
--- a/src/components/main/layout/box_builder.rs
+++ /dev/null
@@ -1,673 +0,0 @@
-/* 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/. */
-
-//! Creates CSS boxes from a DOM tree.
-
-use layout::block::BlockFlow;
-use layout::float::FloatFlow;
-use layout::box::{GenericRenderBox, GenericRenderBoxClass, ImageRenderBox, ImageRenderBoxClass};
-use layout::box::{RenderBox, RenderBoxBase, RenderBoxClass, RenderBoxUtils, TextRenderBoxClass};
-use layout::box::{UnscannedTextRenderBox, UnscannedTextRenderBoxClass};
-use layout::context::LayoutContext;
-use layout::float_context::FloatType;
-use layout::flow::{AbsoluteFlow, BlockFlowClass, FloatFlowClass, FlowContext, FlowData};
-use layout::flow::{ImmutableFlowUtils, InlineBlockFlow, InlineBlockFlowClass, InlineFlowClass};
-use layout::flow::{MutableFlowUtils, TableFlow};
-use layout::flow;
-use layout::inline::{InlineFlow};
-use layout::text::TextRunScanner;
-use css::node_style::StyledNode;
-
-use style::computed_values::display;
-use style::computed_values::float;
-use layout::float_context::{FloatLeft, FloatRight};
-use script::dom::node::{AbstractNode, CommentNodeTypeId, DoctypeNodeTypeId};
-use script::dom::node::{ElementNodeTypeId, LayoutView, TextNodeTypeId, DocumentNodeTypeId};
-use script::dom::node::DocumentFragmentNodeTypeId;
-use servo_util::range::Range;
-use servo_util::tree::{TreeNodeRef, TreeNode};
-use std::cast;
-use std::cell::Cell;
-
-enum FlowType {
- AbsoluteFlowType,
- BlockFlowType,
- FloatFlowType(FloatType),
- InlineBlockFlowType,
- InlineFlowType,
- RootFlowType,
- TableFlowType,
-}
-
-pub struct LayoutTreeBuilder {
- next_cid: int,
- next_bid: int,
-}
-
-impl LayoutTreeBuilder {
- pub fn new() -> LayoutTreeBuilder {
- LayoutTreeBuilder {
- next_cid: -1,
- next_bid: -1,
- }
- }
-}
-
-// helper object for building the initial box list and making the
-// mapping between DOM nodes and boxes.
-struct BoxGenerator<'self> {
- flow: &'self mut FlowContext,
- range_stack: @mut ~[uint],
-}
-
-enum InlineSpacerSide {
- LogicalBefore,
- LogicalAfter,
-}
-
-impl<'self> BoxGenerator<'self> {
- /* Debug ids only */
-
- fn new(flow: &'self mut FlowContext) -> BoxGenerator<'self> {
- debug!("Creating box generator for flow: {:s}", flow.debug_str());
- BoxGenerator {
- flow: flow,
- range_stack: @mut ~[]
- }
- }
-
- fn with_clone<R>(&mut self, cb: &fn(BoxGenerator<'self>) -> R) -> R {
- // FIXME(pcwalton): This is a hack; it can be done safely with linearity.
- unsafe {
- let gen = BoxGenerator {
- flow: cast::transmute_copy(&self.flow),
- range_stack: self.range_stack
- };
- cb(gen)
- }
- }
-
- /* Whether "spacer" boxes are needed to stand in for this DOM node */
- fn inline_spacers_needed_for_node(_: AbstractNode<LayoutView>) -> bool {
- return false;
- }
-
- // TODO: implement this, generating spacer
- fn make_inline_spacer_for_node_side(_: &LayoutContext,
- _: AbstractNode<LayoutView>,
- _: InlineSpacerSide)
- -> Option<@RenderBox> {
- None
- }
-
- pub fn push_node(&mut self,
- ctx: &LayoutContext,
- node: AbstractNode<LayoutView>,
- builder: &mut LayoutTreeBuilder) {
- debug!("BoxGenerator[f{:d}]: pushing node: {:s}", flow::base(self.flow).id, node.debug_str());
-
- // TODO: remove this once UA styles work
- let box_type = self.decide_box_type(node);
-
- debug!("BoxGenerator[f{:d}]: point a", flow::base(self.flow).id);
-
- let range_stack = &mut self.range_stack;
- // depending on flow, make a box for this node.
- match self.flow.class() {
- InlineFlowClass => {
- let inline = self.flow.as_inline();
- let node_range_start = inline.boxes.len();
- range_stack.push(node_range_start);
-
- // if a leaf, make a box.
- if node.is_leaf() {
- let new_box = BoxGenerator::make_box(ctx, box_type, node, builder);
- inline.boxes.push(new_box);
- } else if BoxGenerator::inline_spacers_needed_for_node(node) {
- // else, maybe make a spacer for "left" margin, border, padding
- let inline_spacer = BoxGenerator::make_inline_spacer_for_node_side(ctx, node, LogicalBefore);
- for spacer in inline_spacer.iter() {
- inline.boxes.push(*spacer);
- }
- }
- // TODO: cases for inline-block, etc.
- },
- BlockFlowClass => {
- let block = self.flow.as_block();
- debug!("BoxGenerator[f{:d}]: point b", block.base.id);
- let new_box = BoxGenerator::make_box(ctx, box_type, node, builder);
-
- debug!("BoxGenerator[f{:d}]: attaching box[b{:d}] to block flow (node: {:s})",
- block.base.id,
- new_box.base().id(),
- node.debug_str());
-
- assert!(block.box.is_none());
- block.box = Some(new_box);
- }
- FloatFlowClass => {
- let float = self.flow.as_float();
- debug!("BoxGenerator[f{:d}]: point b", float.base.id);
-
- let new_box = BoxGenerator::make_box(ctx, box_type, node, builder);
-
- debug!("BoxGenerator[f{:d}]: attaching box[b{:d}] to float flow (node: {:s})",
- float.base.id,
- new_box.base().id(),
- node.debug_str());
-
- assert!(float.box.is_none() && float.index.is_none());
- float.box = Some(new_box);
- }
- _ => warn!("push_node() not implemented for flow f{:d}", flow::base(self.flow).id),
- }
- }
-
- pub fn pop_node(&mut self, ctx: &LayoutContext, node: AbstractNode<LayoutView>) {
- debug!("BoxGenerator[f{:d}]: popping node: {:s}", flow::base(self.flow).id, node.debug_str());
-
- match self.flow.class() {
- InlineFlowClass => {
- let inline = self.flow.as_inline();
- let inline = &mut *inline;
-
- if BoxGenerator::inline_spacers_needed_for_node(node) {
- // If this non-leaf box generates extra horizontal spacing, add a SpacerBox for
- // it.
- let result = BoxGenerator::make_inline_spacer_for_node_side(ctx, node, LogicalAfter);
- for spacer in result.iter() {
- let boxes = &mut inline.boxes;
- boxes.push(*spacer);
- }
- }
- let mut node_range: Range = Range::new(self.range_stack.pop(), 0);
- node_range.extend_to(inline.boxes.len());
-
- if node_range.length() == 0 {
- warn!("node range length is zero?!")
- }
-
- debug!("BoxGenerator: adding element range={}", node_range);
- inline.elems.add_mapping(node, &node_range);
- },
- BlockFlowClass => assert!(self.range_stack.len() == 0),
- FloatFlowClass => assert!(self.range_stack.len() == 0),
- _ => warn!("pop_node() not implemented for flow {:?}", flow::base(self.flow).id),
- }
- }
-
- /// Disambiguate between different methods here instead of inlining, since each case has very
- /// different complexity.
- fn make_box(layout_ctx: &LayoutContext,
- ty: RenderBoxClass,
- node: AbstractNode<LayoutView>,
- builder: &mut LayoutTreeBuilder)
- -> @RenderBox {
- let base = RenderBoxBase::new(node, builder.next_box_id());
- let result = match ty {
- GenericRenderBoxClass => @GenericRenderBox::new(base) as @RenderBox,
- TextRenderBoxClass | UnscannedTextRenderBoxClass => {
- @UnscannedTextRenderBox::new(base) as @RenderBox
- }
- ImageRenderBoxClass => BoxGenerator::make_image_box(layout_ctx, node, base),
- };
- debug!("BoxGenerator: created box: {:s}", result.debug_str());
- result
- }
-
- fn make_image_box(layout_ctx: &LayoutContext,
- node: AbstractNode<LayoutView>,
- base: RenderBoxBase)
- -> @RenderBox {
- assert!(node.is_image_element());
-
- do node.with_imm_image_element |image_element| {
- if image_element.image.is_some() {
- // FIXME(pcwalton): Don't copy URLs.
- let url = (*image_element.image.get_ref()).clone();
- @ImageRenderBox::new(base.clone(), url, layout_ctx.image_cache) as @RenderBox
- } else {
- info!("Tried to make image box, but couldn't find image. Made generic box \
- instead.");
- @GenericRenderBox::new(base.clone()) as @RenderBox
- }
- }
- }
-
- fn decide_box_type(&self, node: AbstractNode<LayoutView>) -> RenderBoxClass {
- if node.is_text() {
- TextRenderBoxClass
- } else if node.is_image_element() {
- do node.with_imm_image_element |image_element| {
- match image_element.image {
- Some(_) => ImageRenderBoxClass,
- None => GenericRenderBoxClass,
- }
- }
- } else if node.is_element() {
- GenericRenderBoxClass
- } else {
- fail!("Hey, doctypes and comments shouldn't get here! They are display:none!")
- }
- }
-
-}
-
-enum BoxGenResult<'self> {
- NoGenerator,
- ParentGenerator,
- SiblingGenerator,
- NewGenerator(BoxGenerator<'self>),
- /// Start a new generator, but also switch the parent out for the
- /// grandparent, ending the parent generator.
- ReparentingGenerator(BoxGenerator<'self>),
- Mixed(BoxGenerator<'self>, ~BoxGenResult<'self>),
-}
-
-/// Determines whether the result of child box construction needs to reparent
-/// or not. Reparenting is needed when a block flow is a child of an inline;
-/// in that case, we need to let the level up the stack no to end the parent
-/// genertor and continue with the grandparent.
-enum BoxConstructResult<'self> {
- Normal(Option<BoxGenerator<'self>>),
- Reparent(BoxGenerator<'self>),
-}
-
-impl LayoutTreeBuilder {
- /* Debug-only ids */
- pub fn next_flow_id(&mut self) -> int { self.next_cid += 1; self.next_cid }
- pub fn next_box_id(&mut self) -> int { self.next_bid += 1; self.next_bid }
-
- /// Creates necessary box(es) and flow context(s) for the current DOM node,
- /// and recurses on its children.
- pub fn construct_recursively<'a>(
- &mut self,
- layout_ctx: &LayoutContext,
- cur_node: AbstractNode<LayoutView>,
- mut grandparent_generator: Option<BoxGenerator<'a>>,
- mut parent_generator: BoxGenerator<'a>,
- mut prev_sibling_generator: Option<BoxGenerator<'a>>)
- -> BoxConstructResult<'a> {
- debug!("Considering node: {:s}", cur_node.debug_str());
- let box_gen_result = {
- let grandparent_gen_ref = match grandparent_generator {
- Some(ref mut generator) => Some(generator),
- None => None,
- };
- let sibling_gen_ref = match prev_sibling_generator {
- Some(ref mut generator) => Some(generator),
- None => None,
- };
- self.box_generator_for_node(cur_node, grandparent_gen_ref, &mut parent_generator, sibling_gen_ref)
- };
-
- let mut reparent = false;
-
- debug!("result from generator_for_node: {:?}", &box_gen_result);
- // Skip over nodes that don't belong in the flow tree
- let (this_generator, next_generator) = match box_gen_result {
- NoGenerator => return Normal(prev_sibling_generator),
- ParentGenerator => {
- do parent_generator.with_clone |clone| {
- (clone, None)
- }
- }
- SiblingGenerator => (prev_sibling_generator.take_unwrap(), None),
- NewGenerator(gen) => (gen, None),
- ReparentingGenerator(gen) => {
- reparent = true;
- (gen, None)
- }
- Mixed(gen, next_gen) => (gen, Some(match *next_gen {
- ParentGenerator => {
- do parent_generator.with_clone |clone| {
- clone
- }
- }
- SiblingGenerator => prev_sibling_generator.take_unwrap(),
- _ => fail!("Unexpect BoxGenResult")
- }))
- };
-
- let mut this_generator = this_generator;
-
- debug!("point a: {:s}", cur_node.debug_str());
- this_generator.push_node(layout_ctx, cur_node, self);
- debug!("point b: {:s}", cur_node.debug_str());
-
- // recurse on child nodes.
- let prev_gen_cell = Cell::new(Normal(None));
- for child_node in cur_node.children() {
- do parent_generator.with_clone |grandparent_clone| {
- let grandparent_clone_cell = Cell::new(Some(grandparent_clone));
- do this_generator.with_clone |parent_clone| {
- match prev_gen_cell.take() {
- Normal(prev_gen) => {
- let prev_generator = self.construct_recursively(layout_ctx,
- child_node,
- grandparent_clone_cell.take(),
- parent_clone,
- prev_gen);
- prev_gen_cell.put_back(prev_generator);
- }
- Reparent(prev_gen) => {
- let prev_generator = self.construct_recursively(layout_ctx,
- child_node,
- None,
- grandparent_clone_cell.take().unwrap(),
- Some(prev_gen));
- prev_gen_cell.put_back(prev_generator);
- }
- }
- }
- }
- }
-
- this_generator.pop_node(layout_ctx, cur_node);
- self.simplify_children_of_flow(layout_ctx, this_generator.flow);
-
- match next_generator {
- Some(n_gen) => Normal(Some(n_gen)),
- None => {
- if reparent {
- Reparent(this_generator)
- } else {
- Normal(Some(this_generator))
- }
- }
- }
- }
-
- pub fn box_generator_for_node<'a>(&mut self,
- node: AbstractNode<LayoutView>,
- grandparent_generator: Option<&mut BoxGenerator<'a>>,
- parent_generator: &mut BoxGenerator<'a>,
- mut sibling_generator: Option<&mut BoxGenerator<'a>>)
- -> BoxGenResult<'a> {
- let display = match node.type_id() {
- ElementNodeTypeId(_) => match node.style().Box.display {
- display::none => return NoGenerator,
- display::table | display::inline_table | display::table_row_group
- | display::table_header_group | display::table_footer_group
- | display::table_row | display::table_column_group
- | display::table_column | display::table_cell | display::table_caption
- | display::list_item
- => display::block,
- display => display,
- },
- TextNodeTypeId => display::inline,
- DocumentNodeTypeId(_) |
- DoctypeNodeTypeId |
- DocumentFragmentNodeTypeId |
- CommentNodeTypeId => return NoGenerator,
- };
-
- // FIXME(pcwalton): Unsafe.
- let sibling_flow: Option<&mut FlowContext> = sibling_generator.as_mut().map(|gen| {
- unsafe {
- cast::transmute_copy(&gen.flow)
- }
- });
-
- let is_float = if (node.is_element()) {
- match node.style().Box.float {
- float::none => None,
- float::left => Some(FloatLeft),
- float::right => Some(FloatRight)
- }
- } else {
- None
- };
-
- let sibling_flow_class = match sibling_flow {
- None => None,
- Some(flow) => Some(flow.class()),
- };
-
- let new_generator = match (display, parent_generator.flow.class(), sibling_flow_class) {
- // Floats
- (display::block, BlockFlowClass, _) |
- (display::block, FloatFlowClass, _) if is_float.is_some() => {
- self.create_child_generator(node,
- parent_generator,
- FloatFlowType(is_float.unwrap()))
- }
- // If we're placing a float after an inline, append the float to the inline flow,
- // then continue building from the inline flow in case there are more inlines
- // afterward.
- (display::block, _, Some(InlineFlowClass)) if is_float.is_some() => {
- let float_type = FloatFlowType(is_float.unwrap());
- let float_generator = self.create_child_generator(node,
- sibling_generator.unwrap(),
- float_type);
- return Mixed(float_generator, ~SiblingGenerator);
- }
- // This is a catch-all case for when:
- // a) sibling_flow is None
- // b) sibling_flow is a BlockFlow
- (display::block, InlineFlowClass, _) if is_float.is_some() => {
- self.create_child_generator(node,
- parent_generator,
- FloatFlowType(is_float.unwrap()))
- }
-
- (display::block, BlockFlowClass, _) => {
- match (parent_generator.flow.as_block().is_root, node.parent_node()) {
- // If this is the root node, then use the root flow's
- // context. Otherwise, make a child block context.
- (true, Some(parent)) if !parent.is_document() => {
- self.create_child_generator(node, parent_generator, BlockFlowType)
- }
- (true, None) | (true, Some(_)) => return ParentGenerator,
- (false, _) => {
- self.create_child_generator(node, parent_generator, BlockFlowType)
- }
- }
- }
-
- (display::block, FloatFlowClass, _) => {
- self.create_child_generator(node, parent_generator, BlockFlowType)
- }
-
- // Inlines that are children of inlines are part of the same flow
- (display::inline, InlineFlowClass, _) => return ParentGenerator,
- (display::inline_block, InlineFlowClass, _) => return ParentGenerator,
-
- // Inlines that are children of blocks create new flows if their
- // previous sibling was a block.
- (display::inline, BlockFlowClass, Some(BlockFlowClass)) |
- (display::inline_block, BlockFlowClass, Some(BlockFlowClass)) => {
- self.create_child_generator(node, parent_generator, InlineFlowType)
- }
-
- // The first two cases should only be hit when a FloatFlow
- // is the first child of a BlockFlow. Other times, we will
- (display::inline, _, Some(FloatFlowClass)) |
- (display::inline_block, _, Some(FloatFlowClass)) |
- (display::inline, FloatFlowClass, _) |
- (display::inline_block, FloatFlowClass, _) => {
- self.create_child_generator(node, parent_generator, InlineFlowType)
- }
-
- // Inlines whose previous sibling was not a block try to use their
- // sibling's flow context.
- (display::inline, BlockFlowClass, _) |
- (display::inline_block, BlockFlowClass, _) => {
- return match sibling_generator {
- None => NewGenerator(self.create_child_generator(node,
- parent_generator,
- InlineFlowType)),
- Some(*) => SiblingGenerator
- }
- }
-
- // blocks that are children of inlines need to split their parent
- // flows.
- (display::block, InlineFlowClass, _) => {
- match grandparent_generator {
- None => fail!("expected to have a grandparent block flow"),
- Some(grandparent_gen) => {
- assert!(grandparent_gen.flow.is_block_like());
-
- let block_gen = self.create_child_generator(node,
- grandparent_gen,
- BlockFlowType);
- return ReparentingGenerator(block_gen);
- }
- }
- }
-
- _ => return ParentGenerator
- };
-
- NewGenerator(new_generator)
- }
-
- pub fn create_child_generator<'a>(
- &mut self,
- node: AbstractNode<LayoutView>,
- parent_generator: &mut BoxGenerator<'a>,
- ty: FlowType)
- -> BoxGenerator<'a> {
- let new_flow = self.make_flow(ty, node);
- parent_generator.flow.add_new_child(new_flow);
- let flow_ref = flow::last_child(parent_generator.flow).unwrap();
- BoxGenerator::new(*flow_ref)
- }
-
- /// Fix up any irregularities such as:
- ///
- /// * split inlines (CSS 2.1 Section 9.2.1.1)
- /// * elide non-preformatted whitespace-only text boxes and their flows (CSS 2.1 Section
- /// 9.2.2.1).
- ///
- /// The latter can only be done immediately adjacent to, or at the beginning or end of a block
- /// flow. Otherwise, the whitespace might affect whitespace collapsing with adjacent text.
- pub fn simplify_children_of_flow(&self, ctx: &LayoutContext, parent_flow: &mut FlowContext) {
- match parent_flow.class() {
- InlineFlowClass => {
- let mut found_child_inline = false;
- let mut found_child_block = false;
-
- for child_ctx in flow::child_iter(parent_flow) {
- match child_ctx.class() {
- InlineFlowClass | InlineBlockFlowClass => found_child_inline = true,
- BlockFlowClass => found_child_block = true,
- _ => {}
- }
- }
-
- if found_child_block && found_child_inline {
- self.fixup_split_inline(parent_flow)
- }
- }
- BlockFlowClass | FloatFlowClass => {
- // check first/last child for whitespace-ness
- let mut do_remove = false;
- let p_id = flow::base(parent_flow).id;
- do parent_flow.with_first_child |mut first_child| {
- for first_flow in first_child.mut_iter() {
- if first_flow.starts_inline_flow() {
- // FIXME: workaround for rust#6393
- {
- let first_inline_flow = first_flow.as_inline();
- let boxes = &first_inline_flow.boxes;
- if boxes.len() == 1 {
- let first_box = boxes[0]; // FIXME(pcwalton): Rust bug
- if first_box.is_whitespace_only() {
- debug!("LayoutTreeBuilder: pruning whitespace-only first \
- child flow f{:d} from parent f{:d}",
- first_inline_flow.base.id,
- p_id);
- do_remove = true;
- }
- }
- }
- }
- }
- }
- if (do_remove) {
- parent_flow.remove_first();
- }
-
-
- do_remove = false;
- let p_id = flow::base(parent_flow).id;
- do parent_flow.with_last_child |mut last_child| {
- for last_flow in last_child.mut_iter() {
- if last_flow.starts_inline_flow() {
- // FIXME: workaround for rust#6393
- {
- let last_inline_flow = last_flow.as_inline();
- let boxes = &last_inline_flow.boxes;
- if boxes.len() == 1 && boxes.last().is_whitespace_only() {
- let last_box = boxes.last(); // FIXME(pcwalton): Rust bug
- if last_box.is_whitespace_only() {
- debug!("LayoutTreeBuilder: pruning whitespace-only last \
- child flow f{:d} from parent f{:d}",
- last_inline_flow.base.id,
- p_id);
- do_remove = true;
- }
- }
- }
- }
- }
- }
- if (do_remove) {
- parent_flow.remove_last();
- }
-
- // Issue 543: We only need to do this if there are inline child
- // flows, but there's not a quick way to check at the moment.
- for child_flow in flow::child_iter(parent_flow) {
- match child_flow.class() {
- InlineFlowClass | InlineBlockFlowClass => {
- let mut scanner = TextRunScanner::new();
- scanner.scan_for_runs(ctx, *child_flow);
- }
- _ => {}
- }
- }
- }
- _ => {}
- }
- }
-
- pub fn fixup_split_inline(&self, _: &mut FlowContext) {
- // TODO: finish me.
- fail!(~"TODO: handle case where an inline is split by a block")
- }
-
- /// Entry point for box creation. Should only be called on the root DOM element.
- pub fn construct_trees(&mut self, layout_ctx: &LayoutContext, root: AbstractNode<LayoutView>)
- -> Result<~FlowContext:, ()> {
- debug!("Constructing flow tree for DOM: ");
- debug!("{:?}", root.dump());
-
- let mut new_flow = self.make_flow(RootFlowType, root);
- {
- let new_generator = BoxGenerator::new(new_flow);
- self.construct_recursively(layout_ctx, root, None, new_generator, None);
- }
- return Ok(new_flow)
- }
-
- /// Creates a flow of the given type for the supplied node.
- pub fn make_flow(&mut self, flow_type: FlowType, node: AbstractNode<LayoutView>)
- -> ~FlowContext: {
- let info = FlowData::new(self.next_flow_id(), node);
- let result = match flow_type {
- AbsoluteFlowType => ~AbsoluteFlow::new(info) as ~FlowContext:,
- BlockFlowType => ~BlockFlow::new(info) as ~FlowContext:,
- FloatFlowType(f_type) => ~FloatFlow::new(info, f_type) as ~FlowContext:,
- InlineBlockFlowType => ~InlineBlockFlow::new(info) as ~FlowContext:,
- InlineFlowType => ~InlineFlow::new(info) as ~FlowContext:,
- RootFlowType => ~BlockFlow::new_root(info) as ~FlowContext:,
- TableFlowType => ~TableFlow::new(info) as ~FlowContext:,
- };
- debug!("LayoutTreeBuilder: created flow: {:s}", result.debug_str());
- result
- }
-}
diff --git a/src/components/main/layout/construct.rs b/src/components/main/layout/construct.rs
new file mode 100644
index 00000000000..a190783f8d5
--- /dev/null
+++ b/src/components/main/layout/construct.rs
@@ -0,0 +1,614 @@
+/* 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/. */
+
+//! Creates flows and boxes from a DOM tree via a bottom-up, incremental traversal of the DOM.
+//!
+//! Each step of the traversal considers the node and existing flow, if there is one. If a node is
+//! not dirty and an existing flow exists, then the traversal reuses that flow. Otherwise, it
+//! proceeds to construct either a flow or a `ConstructionItem`. A construction item is a piece of
+//! intermediate data that goes with a DOM node and hasn't found its "home" yet—maybe it's a render
+//! box, maybe it's an absolute or fixed position thing that hasn't found its containing block yet.
+//! Construction items bubble up the tree from children to parents until they find their homes.
+//!
+//! TODO(pcwalton): There is no incremental reflow yet. This scheme requires that nodes either have
+//! weak references to flows or that there be some mechanism to efficiently (O(1) time) "blow
+//! apart" a flow tree and have the flows migrate "home" to their respective DOM nodes while we
+//! perform flow tree construction. The precise mechanism for this will take some experimentation
+//! to get right.
+//!
+//! TODO(pcwalton): This scheme should be amenable to parallelization, but, of course, that's not
+//! yet implemented.
+
+use css::node_style::StyledNode;
+use layout::block::BlockFlow;
+use layout::box::{GenericRenderBox, ImageRenderBox, RenderBox, RenderBoxBase};
+use layout::box::{UnscannedTextRenderBox};
+use layout::context::LayoutContext;
+use layout::float::FloatFlow;
+use layout::float_context::FloatType;
+use layout::flow::{FlowContext, FlowData, MutableFlowUtils};
+use layout::inline::InlineFlow;
+use layout::text::TextRunScanner;
+use layout::util::LayoutDataAccess;
+
+use script::dom::element::HTMLImageElementTypeId;
+use script::dom::node::{AbstractNode, CommentNodeTypeId, DoctypeNodeTypeId};
+use script::dom::node::{DocumentFragmentNodeTypeId, DocumentNodeTypeId, ElementNodeTypeId};
+use script::dom::node::{LayoutView, PostorderNodeTraversal, TextNodeTypeId};
+use servo_util::slot::Slot;
+use servo_util::tree::TreeNodeRef;
+use std::util;
+use style::computed_values::{display, float};
+
+/// The results of flow construction for a DOM node.
+pub enum ConstructionResult {
+ /// This node contributes nothing at all (`display: none`). Alternately, this is what newly
+ /// 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(~FlowContext:),
+
+ /// 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.
+ ConstructionItemConstructionResult(ConstructionItem),
+}
+
+/// Represents the output of flow construction for a DOM node that has not yet resulted in a
+/// complete flow. Construction items bubble up the tree until they find a `FlowContext` to be
+/// attached to.
+enum ConstructionItem {
+ /// Inline boxes and associated {ib} splits that have not yet found flows.
+ InlineBoxesConstructionItem(InlineBoxesConstructionResult),
+}
+
+/// Represents inline boxes and {ib} splits that are bubbling up from an inline.
+struct InlineBoxesConstructionResult {
+ /// Any {ib} splits that we're bubbling up.
+ ///
+ /// TODO(pcwalton): Small vector optimization.
+ splits: Option<~[InlineBlockSplit]>,
+
+ /// Any render boxes that succeed the {ib} splits.
+ boxes: ~[@RenderBox],
+}
+
+/// Represents an {ib} split that has not yet found the containing block that it belongs to. This
+/// is somewhat tricky. An example may be helpful. For this DOM fragment:
+///
+/// <span>
+/// A
+/// <div>B</div>
+/// C
+/// </span>
+///
+/// The resulting `ConstructionItem` for the outer `span` will be:
+///
+/// InlineBoxesConstructionItem(Some(~[
+/// InlineBlockSplit {
+/// predecessor_boxes: ~[
+/// A
+/// ],
+/// block: ~BlockFlow {
+/// B
+/// },
+/// }),~[
+/// C
+/// ])
+struct InlineBlockSplit {
+ /// The inline render boxes that precede the flow.
+ ///
+ /// TODO(pcwalton): Small vector optimization.
+ predecessor_boxes: ~[@RenderBox],
+
+ /// The flow that caused this {ib} split.
+ flow: ~FlowContext:,
+}
+
+/// Methods on optional vectors.
+///
+/// TODO(pcwalton): I think this will no longer be necessary once Rust #8981 lands.
+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];
+
+ /// Pushes a value onto this vector.
+ fn push(&mut self, value: T);
+
+ /// Pushes a vector onto this vector, consuming the original.
+ fn push_all_move(&mut self, values: ~[T]);
+
+ /// Pushes an optional vector onto this vector, consuming the original.
+ fn push_opt_vec_move(&mut self, values: Self);
+
+ /// Returns the length of this optional vector.
+ fn len(&self) -> uint;
+}
+
+impl<T> OptVector<T> for Option<~[T]> {
+ #[inline]
+ fn to_vec(self) -> ~[T] {
+ match self {
+ None => ~[],
+ Some(vector) => vector,
+ }
+ }
+
+ #[inline]
+ fn push(&mut self, value: T) {
+ match *self {
+ None => *self = Some(~[value]),
+ Some(ref mut vector) => vector.push(value),
+ }
+ }
+
+ #[inline]
+ fn push_all_move(&mut self, values: ~[T]) {
+ match *self {
+ None => *self = Some(values),
+ Some(ref mut vector) => vector.push_all_move(values),
+ }
+ }
+
+ #[inline]
+ fn push_opt_vec_move(&mut self, values: Option<~[T]>) {
+ match values {
+ None => {}
+ Some(values) => self.push_all_move(values),
+ }
+ }
+
+ #[inline]
+ fn len(&self) -> uint {
+ match *self {
+ None => 0,
+ Some(ref vector) => vector.len(),
+ }
+ }
+}
+
+/// An object that knows how to create flows.
+pub struct FlowConstructor<'self> {
+ /// The layout context.
+ ///
+ /// FIXME(pcwalton): Why does this contain `@`??? That destroys parallelism!!!
+ layout_context: &'self LayoutContext,
+
+ /// The next flow ID to assign.
+ ///
+ /// FIXME(pcwalton): This is going to have to be atomic; can't we do something better?
+ next_flow_id: Slot<int>,
+
+ /// The next box ID to assign.
+ ///
+ /// FIXME(pcwalton): This is going to have to be atomic; can't we do something better?
+ next_box_id: Slot<int>,
+}
+
+impl<'self> FlowConstructor<'self> {
+ /// Creates a new flow constructor.
+ pub fn init<'a>(layout_context: &'a LayoutContext) -> FlowConstructor<'a> {
+ FlowConstructor {
+ layout_context: layout_context,
+ next_flow_id: Slot::init(0),
+ next_box_id: Slot::init(0),
+ }
+ }
+
+ /// Returns the next flow ID and bumps the internal counter.
+ fn next_flow_id(&self) -> int {
+ let id = self.next_flow_id.get();
+ self.next_flow_id.set(id + 1);
+ id
+ }
+
+ /// Returns the next render box ID and bumps the internal counter.
+ fn next_box_id(&self) -> int {
+ let id = self.next_box_id.get();
+ self.next_box_id.set(id + 1);
+ id
+ }
+
+ /// Builds a `RenderBox` for the given image. This is out of line to guide inlining.
+ fn build_box_for_image(&self, base: RenderBoxBase, node: AbstractNode<LayoutView>)
+ -> @RenderBox {
+ // FIXME(pcwalton): Don't copy URLs.
+ let url = node.with_imm_image_element(|image_element| {
+ image_element.image.as_ref().map(|url| (*url).clone())
+ });
+
+ match url {
+ None => @GenericRenderBox::new(base) as @RenderBox,
+ Some(url) => {
+ // FIXME(pcwalton): The fact that image render boxes store the cache in the
+ // box makes little sense to me.
+ @ImageRenderBox::new(base, url, self.layout_context.image_cache) as @RenderBox
+ }
+ }
+ }
+
+ /// Builds a `RenderBox` for the given node.
+ fn build_box_for_node(&self, node: AbstractNode<LayoutView>) -> @RenderBox {
+ let base = RenderBoxBase::new(node, self.next_box_id());
+ match node.type_id() {
+ ElementNodeTypeId(HTMLImageElementTypeId) => self.build_box_for_image(base, node),
+ TextNodeTypeId => @UnscannedTextRenderBox::new(base) as @RenderBox,
+ _ => @GenericRenderBox::new(base) as @RenderBox,
+ }
+ }
+
+ /// Creates an inline flow from a set of inline boxes and adds it as a child of the given flow.
+ ///
+ /// `#[inline(always)]` because this is performance critical and LLVM will not inline it
+ /// otherwise.
+ #[inline(always)]
+ fn flush_inline_boxes_to_flow(&self,
+ boxes: ~[@RenderBox],
+ flow: &mut ~FlowContext:,
+ node: AbstractNode<LayoutView>) {
+ if boxes.len() > 0 {
+ let inline_base = FlowData::new(self.next_flow_id(), node);
+ let mut inline_flow = ~InlineFlow::from_boxes(inline_base, boxes) as ~FlowContext:;
+ TextRunScanner::new().scan_for_runs(self.layout_context, inline_flow);
+ flow.add_new_child(inline_flow)
+ }
+ }
+
+ /// Creates an inline flow from a set of inline boxes, if present, and adds it as a child of
+ /// the given flow.
+ fn flush_inline_boxes_to_flow_if_necessary(&self,
+ opt_boxes: &mut Option<~[@RenderBox]>,
+ flow: &mut ~FlowContext:,
+ node: AbstractNode<LayoutView>) {
+ let opt_boxes = util::replace(opt_boxes, None);
+ if opt_boxes.len() > 0 {
+ self.flush_inline_boxes_to_flow(opt_boxes.to_vec(), flow, node)
+ }
+ }
+
+ /// 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(&self,
+ flow: &mut ~FlowContext:,
+ node: AbstractNode<LayoutView>) {
+ // 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;
+ for kid in node.children() {
+ match kid.swap_out_construction_result() {
+ NoConstructionResult => {}
+ FlowConstructionResult(kid_flow) => {
+ // Strip ignorable whitespace from the start of this flow per CSS 2.1 §
+ // 9.2.1.1.
+ if first_box {
+ strip_ignorable_whitespace_from_start(&mut opt_boxes_for_inline_flow);
+ first_box = false
+ }
+
+ // Flush any inline boxes that we were gathering up. This allows us to handle
+ // {ib} splits.
+ self.flush_inline_boxes_to_flow_if_necessary(&mut opt_boxes_for_inline_flow,
+ flow,
+ node);
+ flow.add_new_child(kid_flow);
+ }
+ ConstructionItemConstructionResult(InlineBoxesConstructionItem(
+ InlineBoxesConstructionResult {
+ splits: opt_splits,
+ boxes: boxes
+ })) => {
+ // Add any {ib} splits.
+ match opt_splits {
+ None => {}
+ Some(splits) => {
+ for split in splits.move_iter() {
+ // Pull apart the {ib} split object and push its predecessor boxes
+ // onto the list.
+ let InlineBlockSplit {
+ predecessor_boxes: predecessor_boxes,
+ flow: kid_flow
+ } = split;
+ opt_boxes_for_inline_flow.push_all_move(predecessor_boxes);
+
+ // If this is the first box in flow, then strip ignorable
+ // whitespace per CSS 2.1 § 9.2.1.1.
+ if first_box {
+ strip_ignorable_whitespace_from_start(
+ &mut opt_boxes_for_inline_flow);
+ first_box = false
+ }
+
+ // Flush any inline boxes that we were gathering up.
+ self.flush_inline_boxes_to_flow_if_necessary(
+ &mut opt_boxes_for_inline_flow,
+ flow,
+ node);
+
+ // Push the flow generated by the {ib} split onto our list of
+ // flows.
+ flow.add_new_child(kid_flow);
+ }
+ }
+ }
+
+ // Add the boxes to the list we're maintaining.
+ opt_boxes_for_inline_flow.push_all_move(boxes)
+ }
+ }
+ }
+
+ // Perform a final flush of any inline boxes that we were gathering up to handle {ib}
+ // 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,
+ node);
+ }
+
+ /// 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(&self, node: AbstractNode<LayoutView>) -> ~FlowContext: {
+ let base = FlowData::new(self.next_flow_id(), node);
+ let box = self.build_box_for_node(node);
+ let mut flow = ~BlockFlow::from_box(base, box) as ~FlowContext:;
+ self.build_children_of_block_flow(&mut flow, node);
+ flow
+ }
+
+ /// Builds the flow for a node with `float: {left|right}`. This yields a `FloatFlow` with a
+ /// `BlockFlow` underneath it.
+ fn build_flow_for_floated_block(&self, node: AbstractNode<LayoutView>, float_type: FloatType)
+ -> ~FlowContext: {
+ let base = FlowData::new(self.next_flow_id(), node);
+ let box = self.build_box_for_node(node);
+ let mut flow = ~FloatFlow::from_box(base, float_type, box) as ~FlowContext:;
+ self.build_children_of_block_flow(&mut flow, node);
+ flow
+ }
+
+ /// Concatenates the boxes of kids, adding in our own borders/padding/margins if necessary.
+ /// Returns the `InlineBoxesConstructionResult`, if any. There will be no
+ /// `InlineBoxesConstructionResult` if this node consisted entirely of ignorable whitespace.
+ fn build_boxes_for_nonreplaced_inline_content(&self, node: AbstractNode<LayoutView>)
+ -> ConstructionResult {
+ let mut opt_inline_block_splits = None;
+ let mut opt_box_accumulator = None;
+
+ // Concatenate all the render boxes of our kids, creating {ib} splits as necessary.
+ for kid in node.children() {
+ match kid.swap_out_construction_result() {
+ NoConstructionResult => {}
+ FlowConstructionResult(flow) => {
+ // {ib} split. Flush the accumulator to our new split and make a new
+ // accumulator to hold any subsequent `RenderBox`es 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)
+ }
+ ConstructionItemConstructionResult(InlineBoxesConstructionItem(
+ InlineBoxesConstructionResult {
+ splits: opt_splits,
+ boxes: boxes
+ })) => {
+ // Bubble up {ib} splits.
+ match opt_splits {
+ None => {}
+ Some(splits) => {
+ for split in splits.move_iter() {
+ let InlineBlockSplit {
+ predecessor_boxes: boxes,
+ flow: kid_flow
+ } = split;
+ opt_box_accumulator.push_all_move(boxes);
+
+ let split = InlineBlockSplit {
+ predecessor_boxes: util::replace(&mut opt_box_accumulator,
+ None).to_vec(),
+ flow: kid_flow,
+ };
+ opt_inline_block_splits.push(split)
+ }
+ }
+ }
+
+ // Push residual boxes.
+ opt_box_accumulator.push_all_move(boxes)
+ }
+ }
+ }
+
+ // TODO(pcwalton): Add in our own borders/padding/margins if necessary.
+
+ // Finally, make a new construction result.
+ if opt_inline_block_splits.len() > 0 || opt_box_accumulator.len() > 0 {
+ let construction_item = InlineBoxesConstructionItem(InlineBoxesConstructionResult {
+ splits: opt_inline_block_splits,
+ boxes: opt_box_accumulator.to_vec(),
+ });
+ ConstructionItemConstructionResult(construction_item)
+ } else {
+ NoConstructionResult
+ }
+ }
+
+ /// Creates an `InlineBoxesConstructionResult` for replaced content. Replaced content doesn't
+ /// render its children, so this just nukes a child's boxes and creates a `RenderBox`.
+ fn build_boxes_for_replaced_inline_content(&self, node: AbstractNode<LayoutView>)
+ -> ConstructionResult {
+ for kid in node.children() {
+ kid.set_flow_construction_result(NoConstructionResult)
+ }
+
+ let construction_item = InlineBoxesConstructionItem(InlineBoxesConstructionResult {
+ splits: None,
+ boxes: ~[
+ self.build_box_for_node(node)
+ ],
+ });
+ ConstructionItemConstructionResult(construction_item)
+ }
+
+ /// Builds one or more render boxes for a node with `display: inline`. This yields an
+ /// `InlineBoxesConstructionResult`.
+ fn build_boxes_for_inline(&self, node: AbstractNode<LayoutView>) -> ConstructionResult {
+ // Is this node replaced content?
+ if !node.is_replaced_content() {
+ // Go to a path that concatenates our kids' boxes.
+ self.build_boxes_for_nonreplaced_inline_content(node)
+ } else {
+ // Otherwise, just nuke our kids' boxes, create our `RenderBox` if any, and be done
+ // with it.
+ self.build_boxes_for_replaced_inline_content(node)
+ }
+ }
+}
+
+impl<'self> PostorderNodeTraversal for FlowConstructor<'self> {
+ // `#[inline(always)]` because this is always called from the traversal function and for some
+ // reason LLVM's inlining heuristics go awry here.
+ #[inline(always)]
+ fn process(&self, node: AbstractNode<LayoutView>) -> bool {
+ // Get the `display` property for this node, and determine whether this node is floated.
+ let (display, float) = match node.type_id() {
+ ElementNodeTypeId(_) => (node.style().Box.display, node.style().Box.float),
+ TextNodeTypeId => (display::inline, float::none),
+ CommentNodeTypeId |
+ DoctypeNodeTypeId |
+ DocumentFragmentNodeTypeId |
+ DocumentNodeTypeId(_) => (display::none, float::none),
+ };
+
+ // Switch on display and floatedness.
+ match (display, float) {
+ // `display: none` contributes no flow construction result. Nuke the flow construction
+ // results of children.
+ (display::none, _) => {
+ for child in node.children() {
+ child.set_flow_construction_result(NoConstructionResult)
+ }
+ }
+
+ // Inline items contribute inline render box construction results.
+ (display::inline, float::none) => {
+ let construction_result = self.build_boxes_for_inline(node);
+ node.set_flow_construction_result(construction_result)
+ }
+
+ // Block flows that are not floated contribute block flow construction results.
+ //
+ // TODO(pcwalton): Make this only trigger for blocks and handle the other `display`
+ // properties separately.
+ (_, float::none) => {
+ let flow = self.build_flow_for_block(node);
+ node.set_flow_construction_result(FlowConstructionResult(flow))
+ }
+
+ // Floated flows contribute float flow construction results.
+ (_, float_value) => {
+ let float_type = FloatType::from_property(float_value);
+ let flow = self.build_flow_for_floated_block(node, float_type);
+ node.set_flow_construction_result(FlowConstructionResult(flow))
+ }
+ }
+
+ true
+ }
+}
+
+/// A utility trait with some useful methods for node queries.
+trait NodeUtils {
+ /// Returns true if this node doesn't render its kids and false otherwise.
+ fn is_replaced_content(self) -> bool;
+
+ /// Sets the construction result of a flow.
+ fn set_flow_construction_result(self, result: ConstructionResult);
+
+ /// Replaces the flow construction result in a node with `NoConstructionResult` and returns the
+ /// old value.
+ fn swap_out_construction_result(self) -> ConstructionResult;
+
+ /// Returns true if this node consists entirely of ignorable whitespace and false otherwise.
+ /// Ignorable whitespace is defined as whitespace that would be removed per CSS 2.1 § 16.6.1.
+ fn is_ignorable_whitespace(self) -> bool;
+}
+
+impl NodeUtils for AbstractNode<LayoutView> {
+ fn is_replaced_content(self) -> bool {
+ match self.type_id() {
+ TextNodeTypeId |
+ CommentNodeTypeId |
+ DoctypeNodeTypeId |
+ DocumentFragmentNodeTypeId |
+ DocumentNodeTypeId(_) |
+ ElementNodeTypeId(HTMLImageElementTypeId) => true,
+ ElementNodeTypeId(_) => false,
+ }
+ }
+
+ #[inline(always)]
+ fn set_flow_construction_result(self, result: ConstructionResult) {
+ match *self.mutate_layout_data().ptr {
+ Some(ref mut layout_data) => layout_data.flow_construction_result = result,
+ None => fail!("no layout data"),
+ }
+ }
+
+ #[inline(always)]
+ fn swap_out_construction_result(self) -> ConstructionResult {
+ match *self.mutate_layout_data().ptr {
+ Some(ref mut layout_data) => {
+ util::replace(&mut layout_data.flow_construction_result, NoConstructionResult)
+ }
+ None => fail!("no layout data"),
+ }
+ }
+
+ fn is_ignorable_whitespace(self) -> bool {
+ self.is_text() && self.with_imm_text(|text| text.element.data.is_whitespace())
+ }
+}
+
+/// Strips ignorable whitespace from the start of a list of boxes.
+fn strip_ignorable_whitespace_from_start(opt_boxes: &mut Option<~[@RenderBox]>) {
+ match util::replace(opt_boxes, None) {
+ None => return,
+ Some(boxes) => {
+ // FIXME(pcwalton): This is slow because vector shift is broken. :(
+ let mut found_nonwhitespace = false;
+ let mut result = ~[];
+ for box in boxes.move_iter() {
+ if !found_nonwhitespace && box.is_whitespace_only() {
+ continue
+ }
+
+ found_nonwhitespace = true;
+ result.push(box)
+ }
+
+ *opt_boxes = Some(result)
+ }
+ }
+}
+
+/// Strips ignorable whitespace from the end of a list of boxes.
+fn strip_ignorable_whitespace_from_end(opt_boxes: &mut Option<~[@RenderBox]>) {
+ match *opt_boxes {
+ None => {}
+ Some(ref mut boxes) => {
+ while boxes.len() > 0 && boxes.last().is_whitespace_only() {
+ let _ = boxes.pop();
+ }
+ }
+ }
+ if opt_boxes.len() == 0 {
+ *opt_boxes = None
+ }
+}
+
diff --git a/src/components/main/layout/extra.rs b/src/components/main/layout/extra.rs
index 0bb1cbb4cd2..2683d8d0b34 100644
--- a/src/components/main/layout/extra.rs
+++ b/src/components/main/layout/extra.rs
@@ -8,7 +8,6 @@ use layout::util::{DisplayBoxes, LayoutData, LayoutDataAccess};
use script::dom::node::{AbstractNode, LayoutView};
use servo_util::tree::TreeNodeRef;
-use std::cast;
/// Functionality useful for querying the layout-specific data on DOM nodes.
pub trait LayoutAuxMethods {
@@ -18,20 +17,17 @@ pub trait LayoutAuxMethods {
impl LayoutAuxMethods for AbstractNode<LayoutView> {
/// Resets layout data and styles for the node.
- ///
- /// FIXME(pcwalton): Do this as part of box building instead of in a traversal.
fn initialize_layout_data(self) {
- unsafe {
- let node = cast::transmute_mut(self.node());
- if node.layout_data.is_none() {
- node.layout_data = Some(~LayoutData::new() as ~Any)
- } else {
- self.layout_data().boxes.set(DisplayBoxes::init());
- }
+ let layout_data_handle = self.mutate_layout_data();
+ match *layout_data_handle.ptr {
+ None => *layout_data_handle.ptr = Some(~LayoutData::new()),
+ Some(ref mut layout_data) => layout_data.boxes = DisplayBoxes::init(),
}
}
/// Resets layout data and styles for a Node tree.
+ ///
+ /// FIXME(pcwalton): Do this as part of box building instead of in a traversal.
fn initialize_style_for_subtree(self) {
for n in self.traverse_preorder() {
n.initialize_layout_data();
diff --git a/src/components/main/layout/float.rs b/src/components/main/layout/float.rs
index 3559ca4e842..be7ac50524b 100644
--- a/src/components/main/layout/float.rs
+++ b/src/components/main/layout/float.rs
@@ -53,6 +53,18 @@ impl FloatFlow {
}
}
+ pub fn from_box(base: FlowData, float_type: FloatType, box: @RenderBox) -> FloatFlow {
+ FloatFlow {
+ base: base,
+ containing_width: Au(0),
+ box: Some(box),
+ index: None,
+ float_type: float_type,
+ rel_pos: Point2D(Au(0), Au(0)),
+ floated_children: 0,
+ }
+ }
+
pub fn teardown(&mut self) {
for box in self.box.iter() {
box.teardown();
@@ -252,7 +264,7 @@ impl FlowContext for FloatFlow {
}
fn assign_height(&mut self, ctx: &mut LayoutContext) {
- debug!("assign_height_float: assigning height for float {}", self.base.id);
+ // Now that we've determined our height, propagate that out.
let has_inorder_children = self.base.num_floats > 0;
if has_inorder_children {
let mut float_ctx = FloatContext::new(self.floated_children);
@@ -262,7 +274,7 @@ impl FlowContext for FloatFlow {
float_ctx = flow::mut_base(*kid).floats_out.clone();
}
}
-
+ debug!("assign_height_float: assigning height for float {}", self.base.id);
let mut cur_y = Au(0);
let mut top_offset = Au(0);
@@ -281,26 +293,25 @@ impl FlowContext for FloatFlow {
let mut height = cur_y - top_offset;
let mut noncontent_height;
- for box in self.box.iter() {
- let base = box.base();
- let mut position_ref = base.position.mutate();
- let position = &mut position_ref.ptr;
+ let box = self.box.as_ref().unwrap();
+ let base = box.base();
+ let mut position_ref = base.position.mutate();
+ let position = &mut position_ref.ptr;
- // The associated box is the border box of this flow.
- position.origin.y = base.margin.top;
+ // The associated box is the border box of this flow.
+ position.origin.y = base.margin.top;
- noncontent_height = base.padding.top + base.padding.bottom + base.border.top +
- base.border.bottom;
-
- //TODO(eatkinson): compute heights properly using the 'height' property.
- let height_prop = MaybeAuto::from_style(base.style().Box.height,
- Au::new(0)).specified_or_zero();
+ noncontent_height = base.padding.top + base.padding.bottom + base.border.top +
+ base.border.bottom;
+
+ //TODO(eatkinson): compute heights properly using the 'height' property.
+ let height_prop = MaybeAuto::from_style(base.style().Box.height,
+ Au::new(0)).specified_or_zero();
- height = geometry::max(height, height_prop) + noncontent_height;
- debug!("assign_height_float -- height: {}", height);
+ height = geometry::max(height, height_prop) + noncontent_height;
+ debug!("assign_height_float -- height: {}", height);
- position.size.height = height;
- }
+ position.size.height = height;
}
@@ -314,6 +325,7 @@ impl FlowContext for FloatFlow {
// Margins between a floated box and any other box do not collapse.
*collapsing = Au::new(0);
}
+
fn debug_str(&self) -> ~str {
~"FloatFlow"
}
diff --git a/src/components/main/layout/float_context.rs b/src/components/main/layout/float_context.rs
index 45e142b24b9..7a92554401c 100644
--- a/src/components/main/layout/float_context.rs
+++ b/src/components/main/layout/float_context.rs
@@ -3,12 +3,13 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use geom::point::Point2D;
-use geom::size::Size2D;
use geom::rect::Rect;
+use geom::size::Size2D;
use servo_util::geometry::{Au, max, min};
+use std::i32::max_value;
use std::util::replace;
use std::vec;
-use std::i32::max_value;
+use style::computed_values::float;
#[deriving(Clone)]
pub enum FloatType {
@@ -16,6 +17,16 @@ pub enum FloatType {
FloatRight
}
+impl FloatType {
+ pub fn from_property(property: float::T) -> FloatType {
+ match property {
+ float::none => fail!("can't create a float type from an unfloated property"),
+ float::left => FloatLeft,
+ float::right => FloatRight,
+ }
+ }
+}
+
pub enum ClearType {
ClearLeft,
ClearRight,
@@ -31,13 +42,13 @@ struct FloatContextBase {
}
#[deriving(Clone)]
-struct FloatData{
+struct FloatData {
bounds: Rect<Au>,
f_type: FloatType
}
/// All information necessary to place a float
-pub struct PlacementInfo{
+pub struct PlacementInfo {
width: Au, // The dimensions of the float
height: Au,
ceiling: Au, // The minimum top of the float, as determined by earlier elements
@@ -126,7 +137,7 @@ impl FloatContext {
}
}
-impl FloatContextBase{
+impl FloatContextBase {
fn new(num_floats: uint) -> FloatContextBase {
debug!("Creating float context of size {}", num_floats);
FloatContextBase {
@@ -335,7 +346,7 @@ impl FloatContextBase{
match maybe_location {
// If there are no floats blocking us, return the current location
- // TODO(eatknson): integrate with overflow
+ // TODO(eatkinson): integrate with overflow
None => return match info.f_type {
FloatLeft => Rect(Point2D(Au(0), float_y),
Size2D(info.max_width, Au(max_value))),
diff --git a/src/components/main/layout/flow.rs b/src/components/main/layout/flow.rs
index afa0f80b258..27d61f9dd84 100644
--- a/src/components/main/layout/flow.rs
+++ b/src/components/main/layout/flow.rs
@@ -111,6 +111,9 @@ pub trait FlowContext {
fail!("collapse_margins not yet implemented")
}
+ /// Marks this flow as the root flow. The default implementation is a no-op.
+ fn mark_as_root(&mut self) {}
+
/// Returns a debugging string describing this flow.
fn debug_str(&self) -> ~str {
~"???"
@@ -360,6 +363,7 @@ impl Iterator<@RenderBox> for BoxIterator {
}
impl FlowData {
+ #[inline]
pub fn new(id: int, node: AbstractNode<LayoutView>) -> FlowData {
FlowData {
node: node,
diff --git a/src/components/main/layout/inline.rs b/src/components/main/layout/inline.rs
index 39fda3b7961..2a0c412fb32 100644
--- a/src/components/main/layout/inline.rs
+++ b/src/components/main/layout/inline.rs
@@ -196,14 +196,14 @@ impl LineboxScanner {
debug!("LineboxScanner: Trying to place first box of line {}", self.lines.len());
let first_box_size = first_box.base().position.get().size;
- debug!("LineboxScanner: box size: {}", first_box_size);
- let splitable = first_box.can_split();
+ let splittable = first_box.can_split();
+ debug!("LineboxScanner: box size: {}, splittable: {}", first_box_size, splittable);
let line_is_empty: bool = self.pending_line.range.length() == 0;
- // Initally, pretend a splitable box has 0 width.
+ // Initally, pretend a splittable box has 0 width.
// We will move it later if it has nonzero width
// and that causes problems.
- let placement_width = if splitable {
+ let placement_width = if splittable {
Au::new(0)
} else {
first_box_size.width
@@ -231,7 +231,7 @@ impl LineboxScanner {
// If not, but we can't split the box, then we'll place
// the line here and it will overflow.
- if !splitable {
+ if !splittable {
debug!("LineboxScanner: case=line doesn't fit, but is unsplittable");
return (line_bounds, first_box_size.width);
}
@@ -467,6 +467,15 @@ impl InlineFlow {
}
}
+ pub fn from_boxes(base: FlowData, boxes: ~[@RenderBox]) -> InlineFlow {
+ InlineFlow {
+ base: base,
+ boxes: boxes,
+ lines: ~[],
+ elems: ElementMapping::new(),
+ }
+ }
+
pub fn teardown(&mut self) {
for box in self.boxes.iter() {
box.teardown();
diff --git a/src/components/main/layout/layout_task.rs b/src/components/main/layout/layout_task.rs
index f2bbfe55b04..4542b01323d 100644
--- a/src/components/main/layout/layout_task.rs
+++ b/src/components/main/layout/layout_task.rs
@@ -7,66 +7,87 @@
use css::matching::MatchMethods;
use css::select::new_stylist;
-use layout::extra::LayoutAuxMethods;
-use layout::box_builder::LayoutTreeBuilder;
+use layout::construct::{FlowConstructionResult, FlowConstructor, NoConstructionResult};
use layout::context::LayoutContext;
use layout::display_list_builder::{DisplayListBuilder};
+use layout::extra::LayoutAuxMethods;
use layout::flow::{FlowContext, ImmutableFlowUtils, MutableFlowUtils, PreorderFlowTraversal};
use layout::flow::{PostorderFlowTraversal};
use layout::flow;
use layout::incremental::{RestyleDamage, BubbleWidths};
-use layout::util::LayoutDataAccess;
+use layout::util::{LayoutData, LayoutDataAccess};
-use std::cast::transmute;
-use std::cell::Cell;
-use std::comm::Port;
-use std::task;
use extra::arc::{Arc, RWArc};
use geom::point::Point2D;
use geom::rect::Rect;
use geom::size::Size2D;
use gfx::display_list::DisplayList;
use gfx::font_context::FontContext;
-use servo_util::geometry::Au;
use gfx::opts::Opts;
use gfx::render_task::{RenderMsg, RenderChan, RenderLayer};
use gfx::render_task;
-use style::Stylist;
-use style::Stylesheet;
-use style::AuthorOrigin;
use script::dom::event::ReflowEvent;
-use script::dom::node::{AbstractNode, LayoutView};
+use script::dom::node::{AbstractNode, LayoutDataRef, LayoutView};
use script::layout_interface::{AddStylesheetMsg, ContentBoxQuery};
+use script::layout_interface::{ContentBoxesQuery, ContentBoxesResponse, ExitNowMsg, LayoutQuery};
use script::layout_interface::{HitTestQuery, ContentBoxResponse, HitTestResponse};
-use script::layout_interface::{ContentBoxesQuery, ContentBoxesResponse, ExitMsg, LayoutQuery};
-use script::layout_interface::{MatchSelectorsDocumentDamage, Msg};
-use script::layout_interface::{QueryMsg, Reflow, ReflowDocumentDamage};
+use script::layout_interface::{MatchSelectorsDocumentDamage, Msg, PrepareToExitMsg};
+use script::layout_interface::{QueryMsg, ReapLayoutDataMsg, Reflow, ReflowDocumentDamage};
use script::layout_interface::{ReflowForDisplay, ReflowMsg};
use script::script_task::{ReflowCompleteMsg, ScriptChan, SendEventMsg};
use servo_msg::constellation_msg::{ConstellationChan, PipelineId};
use servo_net::image_cache_task::{ImageCacheTask, ImageResponseMsg};
use servo_net::local_image_cache::{ImageResponder, LocalImageCache};
-use servo_util::tree::TreeNodeRef;
+use servo_util::geometry::Au;
+use servo_util::range::Range;
use servo_util::time::{ProfilerChan, profile};
use servo_util::time;
-use servo_util::range::Range;
-use extra::url::Url;
+use servo_util::tree::TreeNodeRef;
+use std::cast::transmute;
+use std::cast;
+use std::cell::Cell;
+use std::comm::Port;
+use std::task;
+use std::util;
+use style::AuthorOrigin;
+use style::Stylesheet;
+use style::Stylist;
+/// Information needed by the layout task.
struct LayoutTask {
+ /// The ID of the pipeline that we belong to.
id: PipelineId,
+
+ /// The port on which we receive messages.
port: Port<Msg>,
+
+ /// The channel on which messages can be sent to the constellation.
constellation_chan: ConstellationChan,
+
+ /// The channel on which messages can be sent to the script task.
script_chan: ScriptChan,
+
+ /// The channel on which messages can be sent to the painting task.
render_chan: RenderChan<AbstractNode<()>>,
+
+ /// The channel on which messages can be sent to the image cache.
image_cache_task: ImageCacheTask,
+
+ /// The local image cache.
local_image_cache: @mut LocalImageCache,
+
+ /// The local font context.
font_ctx: @mut FontContext,
- doc_url: Option<Url>,
+
+ /// The size of the viewport.
screen_size: Option<Size2D<Au>>,
+ /// A cached display list.
display_list: Option<Arc<DisplayList<AbstractNode<()>>>>,
stylist: RWArc<Stylist>,
+
+ /// The channel on which messages can be sent to the profiler.
profiler_chan: ProfilerChan,
}
@@ -195,6 +216,7 @@ impl ImageResponder for LayoutImageResponder {
}
impl LayoutTask {
+ /// Spawns a new layout task.
pub fn create(id: PipelineId,
port: Port<Msg>,
constellation_chan: ConstellationChan,
@@ -217,6 +239,7 @@ impl LayoutTask {
});
}
+ /// Creates a new `LayoutTask` structure.
fn new(id: PipelineId,
port: Port<Msg>,
constellation_chan: ConstellationChan,
@@ -237,7 +260,6 @@ impl LayoutTask {
image_cache_task: image_cache_task.clone(),
local_image_cache: @mut LocalImageCache(image_cache_task),
font_ctx: fctx,
- doc_url: None,
screen_size: None,
display_list: None,
@@ -247,6 +269,7 @@ impl LayoutTask {
}
}
+ /// Starts listening on the port.
fn start(&mut self) {
while self.handle_request() {
// Loop indefinitely.
@@ -266,6 +289,7 @@ impl LayoutTask {
}
}
+ /// Receives and dispatches messages from the port.
fn handle_request(&mut self) -> bool {
match self.port.recv() {
AddStylesheetMsg(sheet) => self.handle_add_stylesheet(sheet),
@@ -282,11 +306,17 @@ impl LayoutTask {
self.handle_query(query.take());
}
}
- ExitMsg => {
- debug!("layout: ExitMsg received");
- let (response_port, response_chan) = stream();
- self.render_chan.send(render_task::ExitMsg(response_chan));
- response_port.recv();
+ ReapLayoutDataMsg(dead_layout_data) => {
+ unsafe {
+ self.handle_reap_layout_data(dead_layout_data)
+ }
+ }
+ PrepareToExitMsg(response_chan) => {
+ self.prepare_to_exit(response_chan)
+ }
+ ExitNowMsg => {
+ debug!("layout: ExitNowMsg received");
+ self.exit_now();
return false
}
}
@@ -294,6 +324,32 @@ impl LayoutTask {
true
}
+ /// Enters a quiescent state in which no new messages except for `ReapLayoutDataMsg` will be
+ /// processed until an `ExitNowMsg` is received. A pong is immediately sent on the given
+ /// response channel.
+ fn prepare_to_exit(&mut self, response_chan: Chan<()>) {
+ response_chan.send(());
+ match self.port.recv() {
+ ReapLayoutDataMsg(dead_layout_data) => {
+ unsafe {
+ self.handle_reap_layout_data(dead_layout_data)
+ }
+ }
+ ExitNowMsg => self.exit_now(),
+ _ => {
+ fail!("layout: message that wasn't `ExitNowMsg` received after `PrepareToExitMsg`")
+ }
+ }
+ }
+
+ /// Shuts down the layout task now. If there are any DOM nodes left, layout will now (safely)
+ /// crash.
+ fn exit_now(&mut self) {
+ let (response_port, response_chan) = stream();
+ self.render_chan.send(render_task::ExitMsg(response_chan));
+ response_port.recv()
+ }
+
fn handle_add_stylesheet(&mut self, sheet: Stylesheet) {
let sheet = Cell::new(sheet);
do self.stylist.write |stylist| {
@@ -301,6 +357,31 @@ impl LayoutTask {
}
}
+ /// Builds the flow tree.
+ ///
+ /// This corresponds to the various `nsCSSFrameConstructor` methods in Gecko or
+ /// `createRendererIfNeeded` in WebKit. Note, however that in WebKit `createRendererIfNeeded`
+ /// is intertwined with selector matching, making it difficult to compare directly. It is
+ /// marked `#[inline(never)]` to aid benchmarking in sampling profilers.
+ #[inline(never)]
+ fn construct_flow_tree(&self, layout_context: &LayoutContext, node: AbstractNode<LayoutView>)
+ -> ~FlowContext: {
+ node.traverse_postorder(&FlowConstructor::init(layout_context));
+
+ let result = match *node.mutate_layout_data().ptr {
+ Some(ref mut layout_data) => {
+ util::replace(&mut layout_data.flow_construction_result, NoConstructionResult)
+ }
+ None => fail!("no layout data for root node"),
+ };
+ let mut flow = match result {
+ FlowConstructionResult(flow) => flow,
+ _ => fail!("Flow construction didn't result in a flow at the root of the tree!"),
+ };
+ flow.mark_as_root();
+ flow
+ }
+
/// Performs layout constraint solving.
///
/// This corresponds to `Reflow()` in Gecko and `layout()` in WebKit/Blink and should be
@@ -331,17 +412,14 @@ impl LayoutTask {
transmute(&data.document_root)
};
- // FIXME: Bad copy!
- let doc_url = data.url.clone();
-
- debug!("layout: received layout request for: {:s}", doc_url.to_str());
+ debug!("layout: received layout request for: {:s}", data.url.to_str());
debug!("layout: damage is {:?}", data.damage);
debug!("layout: parsed Node tree");
debug!("{:?}", node.dump());
+
// Reset the image cache.
self.local_image_cache.next_round(self.make_on_image_available_cb());
- self.doc_url = Some(doc_url);
let screen_size = Size2D(Au::from_px(data.window_size.width as int),
Au::from_px(data.window_size.height as int));
let resized = self.screen_size != Some(screen_size);
@@ -370,16 +448,9 @@ impl LayoutTask {
}
// Construct the flow tree.
- let mut layout_root: ~FlowContext: = do profile(time::LayoutTreeBuilderCategory,
- self.profiler_chan.clone()) {
- let mut builder = LayoutTreeBuilder::new();
- let layout_root: ~FlowContext: = match builder.construct_trees(&layout_ctx, *node) {
- Ok(root) => root,
- Err(*) => fail!(~"Root flow should always exist")
- };
-
- layout_root
- };
+ let mut layout_root = profile(time::LayoutTreeBuilderCategory,
+ self.profiler_chan.clone(),
+ || self.construct_flow_tree(&layout_ctx, *node));
// Propagate damage.
layout_root.traverse_preorder(&mut PropagateDamageTraversal {
@@ -421,22 +492,26 @@ impl LayoutTask {
};
// FIXME(pcwalton): Why are we cloning the display list here?!
- let layout_data = node.layout_data();
- let boxes = layout_data.boxes.mutate();
- boxes.ptr.display_list = Some(display_list.clone());
-
- if boxes.ptr.range.is_none() {
- debug!("Creating initial range for node");
- boxes.ptr.range = Some(Range::new(i,1));
- } else {
- debug!("Appending item to range");
- unsafe {
- let old_node: AbstractNode<()> = transmute(node);
- assert!(old_node == display_list.get().list[i-1].base().extra,
- "Non-contiguous arrangement of display items");
- }
+ match *node.mutate_layout_data().ptr {
+ Some(ref mut layout_data) => {
+ let boxes = &mut layout_data.boxes;
+ boxes.display_list = Some(display_list.clone());
+
+ if boxes.range.is_none() {
+ debug!("Creating initial range for node");
+ boxes.range = Some(Range::new(i,1));
+ } else {
+ debug!("Appending item to range");
+ unsafe {
+ let old_node: AbstractNode<()> = transmute(node);
+ assert!(old_node == display_list.get().list[i-1].base().extra,
+ "Non-contiguous arrangement of display items");
+ }
- boxes.ptr.range.unwrap().extend_by(1);
+ boxes.range.unwrap().extend_by(1);
+ }
+ }
+ None => fail!("no layout data"),
}
}
@@ -472,8 +547,8 @@ impl LayoutTask {
fn box_for_node(node: AbstractNode<LayoutView>) -> Option<Rect<Au>> {
// FIXME(pcwalton): Why are we cloning the display list here?!
- let boxes = node.layout_data().boxes.borrow();
- let boxes = boxes.ptr;
+ let layout_data = node.borrow_layout_data();
+ let boxes = &layout_data.ptr.as_ref().unwrap().boxes;
match (boxes.display_list.clone(), boxes.range) {
(Some(display_list), Some(range)) => {
let mut rect: Option<Rect<Au>> = None;
@@ -516,8 +591,8 @@ impl LayoutTask {
fn boxes_for_node(node: AbstractNode<LayoutView>, mut box_accumulator: ~[Rect<Au>])
-> ~[Rect<Au>] {
- let boxes = node.layout_data().boxes.borrow();
- let boxes = boxes.ptr;
+ let layout_data = node.borrow_layout_data();
+ let boxes = &layout_data.ptr.as_ref().unwrap().boxes;
match (boxes.display_list.clone(), boxes.range) {
(Some(display_list), Some(range)) => {
for i in range.eachi() {
@@ -589,5 +664,12 @@ impl LayoutTask {
script_chan: self.script_chan.clone(),
} as @ImageResponder
}
+
+ /// Handles a message to destroy layout data. Layout data must be destroyed on *this* task
+ /// because it contains local managed pointers.
+ unsafe fn handle_reap_layout_data(&self, layout_data: LayoutDataRef) {
+ let ptr: &mut Option<~LayoutData> = cast::transmute(layout_data.borrow_unchecked());
+ *ptr = None
+ }
}
diff --git a/src/components/main/layout/util.rs b/src/components/main/layout/util.rs
index eba5270489b..e6409bb6dbc 100644
--- a/src/components/main/layout/util.rs
+++ b/src/components/main/layout/util.rs
@@ -3,14 +3,15 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use layout::box::{RenderBox, RenderBoxUtils};
+use layout::construct::{ConstructionResult, NoConstructionResult};
use extra::arc::Arc;
use gfx::display_list::DisplayList;
use script::dom::node::{AbstractNode, LayoutView};
use servo_util::range::Range;
-use servo_util::slot::Slot;
+use servo_util::slot::{MutSlotRef, SlotRef};
use servo_util::tree::TreeNodeRef;
-use std::any::AnyRefExt;
+use std::cast;
use std::iter::Enumerate;
use std::vec::VecIterator;
use style::{ComputedValues, PropertyDeclaration};
@@ -140,47 +141,66 @@ impl ElementMapping {
/// Data that layout associates with a node.
pub struct LayoutData {
/// The results of CSS matching for this node.
- applicable_declarations: Slot<~[Arc<~[PropertyDeclaration]>]>,
+ applicable_declarations: ~[Arc<~[PropertyDeclaration]>],
/// The results of CSS styling for this node.
- style: Slot<Option<ComputedValues>>,
+ style: Option<ComputedValues>,
/// Description of how to account for recent style changes.
- restyle_damage: Slot<Option<int>>,
+ restyle_damage: Option<int>,
/// The boxes assosiated with this flow.
/// Used for getBoundingClientRect and friends.
- boxes: Slot<DisplayBoxes>,
+ boxes: DisplayBoxes,
+
+ /// The current results of flow construction for this node. This is either a flow or a
+ /// `ConstructionItem`. See comments in `construct.rs` for more details.
+ flow_construction_result: ConstructionResult,
}
impl LayoutData {
/// Creates new layout data.
pub fn new() -> LayoutData {
LayoutData {
- applicable_declarations: Slot::init(~[]),
- style: Slot::init(None),
- restyle_damage: Slot::init(None),
- boxes: Slot::init(DisplayBoxes::init()),
+ applicable_declarations: ~[],
+ style: None,
+ restyle_damage: None,
+ boxes: DisplayBoxes::init(),
+ flow_construction_result: NoConstructionResult,
}
}
}
-// This serves as a static assertion that layout data remains sendable. If this is not done, then
-// we can have memory unsafety, which usually manifests as shutdown crashes.
-fn assert_is_sendable<T:Send>(_: T) {}
-fn assert_layout_data_is_sendable() {
- assert_is_sendable(LayoutData::new())
-}
-
/// A trait that allows access to the layout data of a DOM node.
pub trait LayoutDataAccess {
- fn layout_data<'a>(&'a self) -> &'a LayoutData;
+ /// Borrows the layout data without checks.
+ ///
+ /// FIXME(pcwalton): Make safe.
+ unsafe fn borrow_layout_data_unchecked<'a>(&'a self) -> &'a Option<~LayoutData>;
+ /// Borrows the layout data immutably. Fails on a conflicting borrow.
+ fn borrow_layout_data<'a>(&'a self) -> SlotRef<'a,Option<~LayoutData>>;
+ /// Borrows the layout data mutably. Fails on a conflicting borrow.
+ fn mutate_layout_data<'a>(&'a self) -> MutSlotRef<'a,Option<~LayoutData>>;
}
impl LayoutDataAccess for AbstractNode<LayoutView> {
#[inline(always)]
- fn layout_data<'a>(&'a self) -> &'a LayoutData {
- self.node().layout_data.as_ref().unwrap().as_ref().unwrap()
+ unsafe fn borrow_layout_data_unchecked<'a>(&'a self) -> &'a Option<~LayoutData> {
+ cast::transmute(self.node().layout_data.borrow_unchecked())
+ }
+
+ #[inline(always)]
+ fn borrow_layout_data<'a>(&'a self) -> SlotRef<'a,Option<~LayoutData>> {
+ unsafe {
+ cast::transmute(self.node().layout_data.borrow())
+ }
+ }
+
+ #[inline(always)]
+ fn mutate_layout_data<'a>(&'a self) -> MutSlotRef<'a,Option<~LayoutData>> {
+ unsafe {
+ cast::transmute(self.node().layout_data.mutate())
+ }
}
}