diff options
author | Patrick Walton <pcwalton@mimiga.net> | 2013-05-06 15:41:47 -0700 |
---|---|---|
committer | Patrick Walton <pcwalton@mimiga.net> | 2013-05-06 15:42:42 -0700 |
commit | 1d7a3f916d56220206d4f1b086a334917c009eb8 (patch) | |
tree | c846c6f477af3f660d8dcafa5b3efde660ad65fc /src | |
parent | 5749ffcf7afeefcbeaf38f1a29dabde7c7da9626 (diff) | |
download | servo-1d7a3f916d56220206d4f1b086a334917c009eb8.tar.gz servo-1d7a3f916d56220206d4f1b086a334917c009eb8.zip |
servo: Refactor the `Flow` type to save memory and allow upcasting and downcasting more naturally
Diffstat (limited to 'src')
-rw-r--r-- | src/servo/dom/node.rs | 2 | ||||
-rw-r--r-- | src/servo/layout/block.rs | 88 | ||||
-rw-r--r-- | src/servo/layout/box.rs | 10 | ||||
-rw-r--r-- | src/servo/layout/box_builder.rs | 286 | ||||
-rw-r--r-- | src/servo/layout/display_list_builder.rs | 41 | ||||
-rw-r--r-- | src/servo/layout/flow.rs | 317 | ||||
-rw-r--r-- | src/servo/layout/inline.rs | 278 | ||||
-rw-r--r-- | src/servo/layout/layout_task.rs | 13 | ||||
-rw-r--r-- | src/servo/layout/root.rs | 67 | ||||
-rw-r--r-- | src/servo/layout/traverse.rs | 12 |
10 files changed, 603 insertions, 511 deletions
diff --git a/src/servo/dom/node.rs b/src/servo/dom/node.rs index 446dc036d62..d8745ae00fb 100644 --- a/src/servo/dom/node.rs +++ b/src/servo/dom/node.rs @@ -71,7 +71,7 @@ pub enum NodeTypeId { pub struct LayoutData { style: Option<CompleteSelectResults>, - flow: Option<@mut FlowContext>, + flow: Option<FlowContext>, } impl LayoutData { diff --git a/src/servo/layout/block.rs b/src/servo/layout/block.rs index 7b1fb67faa2..6d4cda5b31d 100644 --- a/src/servo/layout/block.rs +++ b/src/servo/layout/block.rs @@ -2,12 +2,12 @@ * 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/. */ -// Block layout. +//! CSS block layout. use layout::box::{RenderBox}; use layout::context::LayoutContext; use layout::display_list_builder::{DisplayListBuilder, FlowDisplayListBuilderMethods}; -use layout::flow::{BlockFlow, FlowContext, InlineBlockFlow, RootFlow}; +use layout::flow::{BlockFlow, FlowContext, FlowData, InlineBlockFlow, RootFlow}; use layout::inline::InlineLayout; use au = gfx::geometry; @@ -18,23 +18,33 @@ use gfx::display_list::DisplayList; use gfx::geometry::Au; pub struct BlockFlowData { + /// Data common to all flows. + common: FlowData, + + /// The associated render box. box: Option<@mut RenderBox> } -pub fn BlockFlowData() -> BlockFlowData { - BlockFlowData { - box: None +impl BlockFlowData { + pub fn new(common: FlowData) -> BlockFlowData { + BlockFlowData { + common: common, + box: None, + } } } +/// NB: These are part of FlowContext, not part of BlockFlowData, because the root flow calls these +/// as well. It is not clear to me whether this needs to be the case, or whether `RootFlow` can be +/// merged into this. pub trait BlockLayout { fn starts_block_flow(&self) -> bool; - fn with_block_box(@mut self, &fn(box: &@mut RenderBox) -> ()) -> (); + fn with_block_box(&self, &fn(box: &@mut RenderBox) -> ()) -> (); - fn bubble_widths_block(@mut self, ctx: &LayoutContext); - fn assign_widths_block(@mut self, ctx: &LayoutContext); - fn assign_height_block(@mut self, ctx: &LayoutContext); - fn build_display_list_block(@mut self, + fn bubble_widths_block(&self, ctx: &LayoutContext); + fn assign_widths_block(&self, ctx: &LayoutContext); + fn assign_height_block(&self, ctx: &LayoutContext); + fn build_display_list_block(&self, a: &DisplayListBuilder, b: &Rect<Au>, c: &Point2D<Au>, @@ -49,17 +59,21 @@ impl BlockLayout for FlowContext { } } - /* Get the current flow's corresponding block box, if it exists, and do something with it. - This works on both BlockFlow and RootFlow, since they are mostly the same. */ - fn with_block_box(@mut self, cb: &fn(box: &@mut RenderBox) -> ()) -> () { + /// Get the current flow's corresponding block box, if it exists, and do something with it. + /// This works on both BlockFlow and RootFlow, since they are mostly the same. + fn with_block_box(&self, callback: &fn(box: &@mut RenderBox) -> ()) -> () { match *self { BlockFlow(*) => { let box = self.block().box; - for box.each |b| { cb(b); } + for box.each |b| { + callback(b); + } }, RootFlow(*) => { let mut box = self.root().box; - for box.each |b| { cb(b); } + for box.each |b| { + callback(b); + } }, _ => fail!(fmt!("Tried to do something with_block_box(), but this is a %?", self)) } @@ -74,7 +88,7 @@ impl BlockLayout for FlowContext { /* TODO: floats */ /* TODO: absolute contexts */ /* TODO: inline-blocks */ - fn bubble_widths_block(@mut self, ctx: &LayoutContext) { + fn bubble_widths_block(&self, ctx: &LayoutContext) { assert!(self.starts_block_flow()); let mut min_width = Au(0); @@ -84,8 +98,10 @@ impl BlockLayout for FlowContext { for self.each_child |child_ctx| { assert!(child_ctx.starts_block_flow() || child_ctx.starts_inline_flow()); - min_width = au::max(min_width, child_ctx.d().min_width); - pref_width = au::max(pref_width, child_ctx.d().pref_width); + do child_ctx.with_common_info |child_info| { + min_width = au::max(min_width, child_info.min_width); + pref_width = au::max(pref_width, child_info.pref_width); + } } /* if not an anonymous block context, add in block box's widths. @@ -95,8 +111,10 @@ impl BlockLayout for FlowContext { pref_width = pref_width.add(&box.get_pref_width(ctx)); } - self.d().min_width = min_width; - self.d().pref_width = pref_width; + do self.with_common_info |info| { + info.min_width = min_width; + info.pref_width = pref_width; + } } /* Recursively (top-down) determines the actual width of child @@ -106,10 +124,10 @@ impl BlockLayout for FlowContext { Dual boxes consume some width first, and the remainder is assigned to all child (block) contexts. */ - fn assign_widths_block(@mut self, _ctx: &LayoutContext) { + fn assign_widths_block(&self, _ctx: &LayoutContext) { assert!(self.starts_block_flow()); - let mut remaining_width = self.d().position.size.width; + let mut remaining_width = self.with_common_info(|info| info.position.size.width); let mut _right_used = Au(0); let mut left_used = Au(0); @@ -123,22 +141,28 @@ impl BlockLayout for FlowContext { for self.each_child |child_ctx| { assert!(child_ctx.starts_block_flow() || child_ctx.starts_inline_flow()); - child_ctx.d().position.origin.x = left_used; - child_ctx.d().position.size.width = remaining_width; + do child_ctx.with_common_info |child_info| { + child_info.position.origin.x = left_used; + child_info.position.size.width = remaining_width; + } } } - fn assign_height_block(@mut self, _ctx: &LayoutContext) { + fn assign_height_block(&self, _ctx: &LayoutContext) { assert!(self.starts_block_flow()); let mut cur_y = Au(0); for self.each_child |child_ctx| { - child_ctx.d().position.origin.y = cur_y; - cur_y += child_ctx.d().position.size.height; + do child_ctx.with_common_info |child_info| { + child_info.position.origin.y = cur_y; + cur_y += child_info.position.size.height; + } } - self.d().position.size.height = cur_y; + do self.with_common_info |info| { + info.position.size.height = cur_y; + } let _used_top = Au(0); let _used_bot = Au(0); @@ -150,9 +174,11 @@ impl BlockLayout for FlowContext { } } - fn build_display_list_block(@mut self, builder: &DisplayListBuilder, dirty: &Rect<Au>, - offset: &Point2D<Au>, list: &Cell<DisplayList>) { - + fn build_display_list_block(&self, + builder: &DisplayListBuilder, + dirty: &Rect<Au>, + offset: &Point2D<Au>, + list: &Cell<DisplayList>) { assert!(self.starts_block_flow()); // add box that starts block context diff --git a/src/servo/layout/box.rs b/src/servo/layout/box.rs index a7a56109525..b6b46f7f5d3 100644 --- a/src/servo/layout/box.rs +++ b/src/servo/layout/box.rs @@ -71,13 +71,13 @@ padding, backgrounds. It is analogous to a CSS nonreplaced content box. */ pub struct RenderBoxData { /* originating DOM node */ - node : AbstractNode, + node: AbstractNode, /* reference to containing flow context, which this box participates in */ - ctx : @mut FlowContext, + ctx: FlowContext, /* position of this box relative to owning flow */ - position : Rect<Au>, - font_size : Length, + position: Rect<Au>, + font_size: Length, /* TODO (Issue #87): debug only */ id: int } @@ -103,7 +103,7 @@ pub enum SplitBoxResult { SplitDidNotFit(Option<@mut RenderBox>, Option<@mut RenderBox>) } -pub fn RenderBoxData(node: AbstractNode, ctx: @mut FlowContext, id: int) -> RenderBoxData { +pub fn RenderBoxData(node: AbstractNode, ctx: FlowContext, id: int) -> RenderBoxData { RenderBoxData { node : node, ctx : ctx, diff --git a/src/servo/layout/box_builder.rs b/src/servo/layout/box_builder.rs index f127efa03e7..d0fb0deb55d 100644 --- a/src/servo/layout/box_builder.rs +++ b/src/servo/layout/box_builder.rs @@ -2,7 +2,7 @@ * 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. */ +//! Creates CSS boxes from a DOM tree. use dom::element::*; use dom::node::{AbstractNode, CommentNodeTypeId, DoctypeNodeTypeId}; @@ -16,12 +16,12 @@ use layout::inline::{InlineFlowData, InlineLayout}; use layout::root::RootFlowData; use gfx::image::holder::ImageHolder; -use servo_util::range::Range; use newcss::values::{CSSDisplay, CSSDisplayBlock, CSSDisplayInline, CSSDisplayInlineBlock}; use newcss::values::{CSSDisplayNone}; +use servo_util::range::Range; pub struct LayoutTreeBuilder { - root_flow: Option<@mut FlowContext>, + root_flow: Option<FlowContext>, next_bid: int, next_cid: int } @@ -39,7 +39,7 @@ pub impl LayoutTreeBuilder { // helper object for building the initial box list and making the // mapping between DOM nodes and boxes. struct BoxGenerator { - flow: @mut FlowContext, + flow: FlowContext, range_stack: ~[uint], } @@ -58,7 +58,9 @@ priv fn simulate_UA_display_rules(node: AbstractNode) -> CSSDisplay { };*/ let resolved = CSSDisplayInline; - if (resolved == CSSDisplayNone) { return resolved; } + if resolved == CSSDisplayNone { + return resolved; + } match node.type_id() { DoctypeNodeTypeId | CommentNodeTypeId => CSSDisplayNone, @@ -81,7 +83,7 @@ priv fn simulate_UA_display_rules(node: AbstractNode) -> CSSDisplay { } impl BoxGenerator { - fn new(flow: @mut FlowContext) -> BoxGenerator { + fn new(flow: FlowContext) -> BoxGenerator { debug!("Creating box generator for flow: %s", flow.debug_str()); BoxGenerator { flow: flow, @@ -99,103 +101,110 @@ impl BoxGenerator { _: &LayoutContext, _: AbstractNode, _: InlineSpacerSide) - -> Option<@mut RenderBox> { + -> Option<@mut RenderBox> { None } - pub fn push_node(@mut self, ctx: &LayoutContext, builder: &mut LayoutTreeBuilder, node: AbstractNode) { - debug!("BoxGenerator[f%d]: pushing node: %s", self.flow.d().id, node.debug_str()); + pub fn push_node(&mut self, + ctx: &LayoutContext, + builder: &mut LayoutTreeBuilder, + node: AbstractNode) { + debug!("BoxGenerator[f%d]: pushing node: %s", self.flow.id(), node.debug_str()); // first, determine the box type, based on node characteristics let simulated_display = simulate_UA_display_rules(node); // TODO: remove this once UA styles work let box_type = builder.decide_box_type(node, simulated_display); - debug!("BoxGenerator[f%d]: point a", self.flow.d().id); + debug!("BoxGenerator[f%d]: point a", self.flow.id()); // depending on flow, make a box for this node. match self.flow { - @InlineFlow(*) => { - let node_range_start = match self.flow { - @InlineFlow(*) => { - let inline_flow = self.flow.inline(); - inline_flow.boxes.len() - } - _ => 0 - }; + InlineFlow(inline) => { + let mut inline = &mut *inline; + let node_range_start = inline.boxes.len(); self.range_stack.push(node_range_start); // if a leaf, make a box. if node.is_leaf() { let new_box = builder.make_box(ctx, box_type, node, self.flow); - let boxes = &mut self.flow.inline().boxes; - boxes.push(new_box); + inline.boxes.push(new_box); } else if self.inline_spacers_needed_for_node(node) { // else, maybe make a spacer for "left" margin, border, padding for self.make_inline_spacer_for_node_side(ctx, node, LogicalBefore).each |spacer: &@mut RenderBox| { - let boxes = &mut self.flow.inline().boxes; - boxes.push(*spacer); + inline.boxes.push(*spacer); } } // TODO: cases for inline-block, etc. }, - @BlockFlow(*) => { - debug!("BoxGenerator[f%d]: point b", self.flow.d().id); - let new_box = builder.make_box(ctx, box_type, node, self.flow); - debug!("BoxGenerator[f%d]: attaching box[b%d] to block flow (node: %s)", - self.flow.d().id, new_box.d().id, node.debug_str()); - - assert!(self.flow.block().box.is_none()); - //XXXjdm We segfault when returning without this temporary. - let block = self.flow.block(); - block.box = Some(new_box); + BlockFlow(*) => { + do self.flow.with_common_info |flow_info| { + debug!("BoxGenerator[f%d]: point b", flow_info.id); + let new_box = builder.make_box(ctx, box_type, node, self.flow); + debug!("BoxGenerator[f%d]: attaching box[b%d] to block flow (node: %s)", + flow_info.id, + new_box.d().id, + node.debug_str()); + + assert!(self.flow.block().box.is_none()); + //XXXjdm We segfault when returning without this temporary. + let block = self.flow.block(); + block.box = Some(new_box); + } }, - @RootFlow(*) => { - debug!("BoxGenerator[f%d]: point c", self.flow.d().id); - let new_box = builder.make_box(ctx, box_type, node, self.flow); - debug!("BoxGenerator[f%d]: (node is: %s)", self.flow.d().id, node.debug_str()); - debug!("BoxGenerator[f%d]: attaching box[b%d] to root flow (node: %s)", - self.flow.d().id, new_box.d().id, node.debug_str()); - - assert!(self.flow.root().box.is_none()); - //XXXjdm We segfault when returning without this temporary. - let root = self.flow.root(); - root.box = Some(new_box); + RootFlow(*) => { + do self.flow.with_common_info |info| { + debug!("BoxGenerator[f%d]: point c", info.id); + let new_box = builder.make_box(ctx, box_type, node, self.flow); + debug!("BoxGenerator[f%d]: (node is: %s)", info.id, node.debug_str()); + debug!("BoxGenerator[f%d]: attaching box[b%d] to root flow (node: %s)", + info.id, + new_box.d().id, + node.debug_str()); + + assert!(self.flow.root().box.is_none()); + //XXXjdm We segfault when returning without this temporary. + let root = self.flow.root(); + root.box = Some(new_box); + } }, - _ => { warn!("push_node() not implemented for flow f%d", self.flow.d().id) } + _ => { + do self.flow.with_common_info |flow_info| { + warn!("push_node() not implemented for flow f%d", flow_info.id) + } + } } } - pub fn pop_node(&mut self, ctx: &LayoutContext, _builder: &LayoutTreeBuilder, node: AbstractNode) { - debug!("BoxGenerator[f%d]: popping node: %s", self.flow.d().id, node.debug_str()); + pub fn pop_node(&mut self, + ctx: &LayoutContext, + _builder: &LayoutTreeBuilder, + node: AbstractNode) { + debug!("BoxGenerator[f%d]: popping node: %s", self.flow.id(), node.debug_str()); match self.flow { - @InlineFlow(*) => { + InlineFlow(inline) => { + let inline = &mut *inline; + if self.inline_spacers_needed_for_node(node) { - // if this non-leaf box generates extra horizontal - // spacing, add a SpacerBox for it. - for self.make_inline_spacer_for_node_side(ctx, node, LogicalAfter).each |spacer: &@mut RenderBox| { + // If this non-leaf box generates extra horizontal spacing, add a SpacerBox for + // it. + let result = self.make_inline_spacer_for_node_side(ctx, node, LogicalAfter); + for result.each |spacer| { let boxes = &mut self.flow.inline().boxes; boxes.push(*spacer); } } let mut node_range: Range = Range::new(self.range_stack.pop(), 0); - let inline_flow = self.flow.inline(); // FIXME: borrow checker workaround - node_range.extend_to(inline_flow.boxes.len()); + node_range.extend_to(inline.boxes.len()); assert!(node_range.length() > 0); debug!("BoxGenerator: adding element range=%?", node_range); - let elems = &mut inline_flow.elems; - elems.add_mapping(node, &node_range); - }, - @BlockFlow(*) | @RootFlow(*) => { - assert!(self.range_stack.len() == 0); + inline.elems.add_mapping(node, &node_range); }, - _ => { - let d = self.flow.d(); // FIXME: borrow checker workaround - warn!("pop_node() not implemented for flow %?", d.id) - } + BlockFlow(*) | RootFlow(*) => assert!(self.range_stack.len() == 0), + _ => warn!("pop_node() not implemented for flow %?", self.flow.id()), } } } @@ -207,7 +216,11 @@ struct BuilderContext { impl BuilderContext { fn new(collector: @mut BoxGenerator) -> BuilderContext { - debug!("Creating new BuilderContext for flow: %s", collector.flow.debug_str()); + { + let collector = &mut *collector; + debug!("Creating new BuilderContext for flow: %s", collector.flow.debug_str()); + } + BuilderContext { default_collector: collector, inline_collector: None, @@ -219,11 +232,16 @@ impl BuilderContext { copy self } - priv fn attach_child_flow(&self, child: @mut FlowContext) { - let d = self.default_collector.flow.d(); // FIXME: borrow checker workaround - let cd = child.d(); // FIXME: borrow checker workaround - debug!("BuilderContext: Adding child flow f%? of f%?", d.id, cd.id); - self.default_collector.flow.add_child(child); + priv fn attach_child_flow(&self, child: FlowContext) { + let default_collector = &mut *self.default_collector; + do default_collector.flow.with_common_info |flow_info| { + do child.with_common_info |child_flow_info| { + debug!("BuilderContext: Adding child flow f%? of f%?", + flow_info.id, + child_flow_info.id); + default_collector.flow.add_child(child); + } + } } priv fn create_child_flow_of_type(&self, @@ -236,7 +254,8 @@ impl BuilderContext { BuilderContext::new(@mut BoxGenerator::new(new_flow)) } - priv fn make_inline_collector(&mut self, builder: &mut LayoutTreeBuilder, node: AbstractNode) -> BuilderContext { + priv fn make_inline_collector(&mut self, builder: &mut LayoutTreeBuilder, node: AbstractNode) + -> BuilderContext { debug!("BuilderContext: making new inline collector flow"); let new_flow = builder.make_flow(Flow_Inline, node); let new_generator = @mut BoxGenerator::new(new_flow); @@ -247,7 +266,8 @@ impl BuilderContext { BuilderContext::new(new_generator) } - priv fn get_inline_collector(&mut self, builder: &mut LayoutTreeBuilder, node: AbstractNode) -> BuilderContext { + priv fn get_inline_collector(&mut self, builder: &mut LayoutTreeBuilder, node: AbstractNode) + -> BuilderContext { match copy self.inline_collector { Some(collector) => BuilderContext::new(collector), None => self.make_inline_collector(builder, node) @@ -261,10 +281,8 @@ impl BuilderContext { // returns a context for the current node, or None if the document subtree rooted // by the node should not generate a layout tree. For example, nodes with style 'display:none' // should just not generate any flows or boxes. - fn containing_context_for_node(&mut self, - node: AbstractNode, - builder: &mut LayoutTreeBuilder) - -> Option<BuilderContext> { + fn containing_context_for_node(&mut self, node: AbstractNode, builder: &mut LayoutTreeBuilder) + -> Option<BuilderContext> { // TODO: remove this once UA styles work // TODO: handle interactions with 'float', 'position' (CSS 2.1, Section 9.7) let simulated_display = match simulate_UA_display_rules(node) { @@ -273,7 +291,7 @@ impl BuilderContext { }; let containing_context = match (simulated_display, self.default_collector.flow) { - (CSSDisplayBlock, @RootFlow(*)) => { + (CSSDisplayBlock, RootFlow(*)) => { // If this is the root node, then use the root flow's // context. Otherwise, make a child block context. match node.parent_node() { @@ -281,14 +299,14 @@ impl BuilderContext { None => { self.clone() }, } }, - (CSSDisplayBlock, @BlockFlow(*)) => { + (CSSDisplayBlock, BlockFlow(*)) => { self.clear_inline_collector(); self.create_child_flow_of_type(Flow_Block, builder, node) }, - (CSSDisplayInline, @InlineFlow(*)) => self.clone(), - (CSSDisplayInlineBlock, @InlineFlow(*)) => self.clone(), - (CSSDisplayInline, @BlockFlow(*)) => self.get_inline_collector(builder, node), - (CSSDisplayInlineBlock, @BlockFlow(*)) => self.get_inline_collector(builder, node), + (CSSDisplayInline, InlineFlow(*)) => self.clone(), + (CSSDisplayInlineBlock, InlineFlow(*)) => self.clone(), + (CSSDisplayInline, BlockFlow(*)) => self.get_inline_collector(builder, node), + (CSSDisplayInlineBlock, BlockFlow(*)) => self.get_inline_collector(builder, node), _ => self.clone() }; @@ -330,10 +348,12 @@ pub impl LayoutTreeBuilder { // eventually be elided or split, but the mapping between // nodes and FlowContexts should not change during layout. let flow = &mut this_ctx.default_collector.flow; - for flow.each_child |child_flow: @mut FlowContext| { - let node = child_flow.d().node; - assert!(node.has_layout_data()); - node.layout_data().flow = Some(child_flow); + for flow.each_child |child_flow| { + do child_flow.with_common_info |child_flow_info| { + let node = child_flow_info.node; + assert!(node.has_layout_data()); + node.layout_data().flow = Some(child_flow); + } } } @@ -347,14 +367,14 @@ pub impl LayoutTreeBuilder { // beginning or end of a block flow. Otherwise, the whitespace // might affect whitespace collapsing with adjacent text. fn simplify_children_of_flow(&self, _: &LayoutContext, parent_ctx: &BuilderContext) { - match *parent_ctx.default_collector.flow { + match parent_ctx.default_collector.flow { InlineFlow(*) => { let mut found_child_inline = false; let mut found_child_block = false; let flow = &mut parent_ctx.default_collector.flow; - for flow.each_child |child_ctx: @mut FlowContext| { - match *child_ctx { + for flow.each_child |child_ctx| { + match child_ctx { InlineFlow(*) | InlineBlockFlow(*) => found_child_inline = true, BlockFlow(*) => found_child_block = true, _ => {} @@ -370,34 +390,32 @@ pub impl LayoutTreeBuilder { // of its RenderBox or FlowContext children, and possibly keep alive other junk let parent_flow = parent_ctx.default_collector.flow; - // FIXME: Workaround for the borrow check. - let (first_child, last_child) = { - let parent_flow: &mut FlowContext = parent_flow; - let parent_flow_data = parent_flow.d(); - (parent_flow_data.first_child, parent_flow_data.last_child) - }; + let (first_child, last_child) = + do parent_flow.with_common_info |parent_flow_info| { + (parent_flow_info.first_child, parent_flow_info.last_child) + }; // check first/last child for whitespace-ness - for first_child.each |first_flow: &@mut FlowContext| { + for first_child.each |first_flow| { if first_flow.starts_inline_flow() { let boxes = &mut first_flow.inline().boxes; if boxes.len() == 1 && boxes[0].is_whitespace_only() { debug!("LayoutTreeBuilder: pruning whitespace-only first child flow \ f%d from parent f%d", - first_flow.d().id, - parent_flow.d().id); + first_flow.id(), + parent_flow.id()); parent_flow.remove_child(*first_flow); } } } - for last_child.each |last_flow: &@mut FlowContext| { + for last_child.each |last_flow| { if last_flow.starts_inline_flow() { let boxes = &mut last_flow.inline().boxes; if boxes.len() == 1 && boxes.last().is_whitespace_only() { debug!("LayoutTreeBuilder: pruning whitespace-only last child flow \ f%d from parent f%d", - last_flow.d().id, - parent_flow.d().id); + last_flow.id(), + parent_flow.id()); parent_flow.remove_child(*last_flow); } } @@ -407,15 +425,14 @@ pub impl LayoutTreeBuilder { } } - fn fixup_split_inline(&self, _: @mut FlowContext) { + fn fixup_split_inline(&self, _: 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 root DOM element. */ + /// Entry point for box creation. Should only be called on the root DOM element. fn construct_trees(&mut self, layout_ctx: &LayoutContext, root: AbstractNode) - -> Result<@mut FlowContext, ()> { + -> Result<FlowContext, ()> { let new_flow = self.make_flow(Flow_Root, root); let new_generator = @mut BoxGenerator::new(new_flow); let mut root_ctx = BuilderContext::new(new_generator); @@ -425,53 +442,46 @@ pub impl LayoutTreeBuilder { return Ok(new_flow) } - fn make_flow(&mut self, ty: FlowContextType, node: AbstractNode) -> @mut FlowContext { - let data = FlowData::new(self.next_flow_id(), node); - let ret = match ty { - Flow_Absolute => @mut AbsoluteFlow(data), - Flow_Block => @mut BlockFlow(data, BlockFlowData()), - Flow_Float => @mut FloatFlow(data), - Flow_InlineBlock => @mut InlineBlockFlow(data), - Flow_Inline => @mut InlineFlow(data, InlineFlowData()), - Flow_Root => @mut RootFlow(data, RootFlowData()), - Flow_Table => @mut TableFlow(data) + /// Creates a flow of the given type for the supplied node. + fn make_flow(&mut self, ty: FlowContextType, node: AbstractNode) -> FlowContext { + let info = FlowData::new(self.next_flow_id(), node); + let result = match ty { + Flow_Absolute => AbsoluteFlow(@mut info), + Flow_Block => BlockFlow(@mut BlockFlowData::new(info)), + Flow_Float => FloatFlow(@mut info), + Flow_InlineBlock => InlineBlockFlow(@mut info), + Flow_Inline => InlineFlow(@mut InlineFlowData::new(info)), + Flow_Root => RootFlow(@mut RootFlowData::new(info)), + Flow_Table => TableFlow(@mut info), }; - debug!("LayoutTreeBuilder: created flow: %s", ret.debug_str()); - ret + debug!("LayoutTreeBuilder: created flow: %s", result.debug_str()); + result } - /** - disambiguate between different methods here instead of inlining, since each - case has very different complexity - */ + /// Disambiguate between different methods here instead of inlining, since each case has very + /// different complexity. fn make_box(&mut self, layout_ctx: &LayoutContext, ty: RenderBoxType, node: AbstractNode, - ctx: @mut FlowContext) - -> @mut RenderBox { - let ret = match ty { + ctx: FlowContext) + -> @mut RenderBox { + let result = match ty { RenderBox_Generic => self.make_generic_box(layout_ctx, node, ctx), RenderBox_Text => self.make_text_box(layout_ctx, node, ctx), RenderBox_Image => self.make_image_box(layout_ctx, node, ctx), }; - debug!("LayoutTreeBuilder: created box: %s", ret.debug_str()); - ret + debug!("LayoutTreeBuilder: created box: %s", result.debug_str()); + result } - fn make_generic_box(&mut self, - _: &LayoutContext, - node: AbstractNode, - ctx: @mut FlowContext) - -> @mut RenderBox { + fn make_generic_box(&mut self, _: &LayoutContext, node: AbstractNode, ctx: FlowContext) + -> @mut RenderBox { @mut GenericBox(RenderBoxData(copy node, ctx, self.next_box_id())) } - fn make_image_box(&mut self, - layout_ctx: &LayoutContext, - node: AbstractNode, - ctx: @mut FlowContext) - -> @mut RenderBox { + fn make_image_box(&mut self, layout_ctx: &LayoutContext, node: AbstractNode, ctx: FlowContext) + -> @mut RenderBox { if !node.is_image_element() { fail!(~"WAT error: why couldn't we make an image box?"); } @@ -482,17 +492,15 @@ pub impl LayoutTreeBuilder { layout_ctx.image_cache); @mut ImageBox(RenderBoxData(node, ctx, self.next_box_id()), holder) } else { - info!("Tried to make image box, but couldn't find image. Made generic box instead."); + info!("Tried to make image box, but couldn't find image. Made generic box \ + instead."); self.make_generic_box(layout_ctx, node, ctx) } } } - fn make_text_box(&mut self, - _: &LayoutContext, - node: AbstractNode, - ctx: @mut FlowContext) - -> @mut RenderBox { + fn make_text_box(&mut self, _: &LayoutContext, node: AbstractNode, ctx: FlowContext) + -> @mut RenderBox { if !node.is_text() { fail!(~"WAT error: why couldn't we make a text box?"); } diff --git a/src/servo/layout/display_list_builder.rs b/src/servo/layout/display_list_builder.rs index e106347e3f7..85eed2db274 100644 --- a/src/servo/layout/display_list_builder.rs +++ b/src/servo/layout/display_list_builder.rs @@ -29,17 +29,17 @@ pub struct DisplayListBuilder<'self> { } pub trait FlowDisplayListBuilderMethods { - fn build_display_list(@mut self, a: &DisplayListBuilder, b: &Rect<Au>, c: &Cell<DisplayList>); - fn build_display_list_for_child(@mut self, + fn build_display_list(&self, a: &DisplayListBuilder, b: &Rect<Au>, c: &Cell<DisplayList>); + fn build_display_list_for_child(&self, a: &DisplayListBuilder, - b: @mut FlowContext, + b: FlowContext, c: &Rect<Au>, d: &Point2D<Au>, e: &Cell<DisplayList>); } impl FlowDisplayListBuilderMethods for FlowContext { - fn build_display_list(@mut self, + fn build_display_list(&self, builder: &DisplayListBuilder, dirty: &Rect<Au>, list: &Cell<DisplayList>) { @@ -47,27 +47,28 @@ impl FlowDisplayListBuilderMethods for FlowContext { self.build_display_list_recurse(builder, dirty, &zero, list); } - fn build_display_list_for_child(@mut self, + fn build_display_list_for_child(&self, builder: &DisplayListBuilder, - child_flow: @mut FlowContext, - dirty: &Rect<Au>, offset: &Point2D<Au>, + child_flow: FlowContext, + dirty: &Rect<Au>, + offset: &Point2D<Au>, list: &Cell<DisplayList>) { - // adjust the dirty rect to child flow context coordinates - let d = child_flow.d(); // FIXME: borrow checker workaround - let abs_flow_bounds = d.position.translate(offset); - let adj_offset = offset.add(&d.position.origin); + do child_flow.with_common_info |child_flow_info| { + let abs_flow_bounds = child_flow_info.position.translate(offset); + let adj_offset = offset.add(&child_flow_info.position.origin); - debug!("build_display_list_for_child: rel=%?, abs=%?", - d.position, abs_flow_bounds); - debug!("build_display_list_for_child: dirty=%?, offset=%?", - dirty, offset); + debug!("build_display_list_for_child: rel=%?, abs=%?", + child_flow_info.position, + abs_flow_bounds); + debug!("build_display_list_for_child: dirty=%?, offset=%?", dirty, offset); - if dirty.intersects(&abs_flow_bounds) { - debug!("build_display_list_for_child: intersected. recursing into child flow..."); - child_flow.build_display_list_recurse(builder, dirty, &adj_offset, list); - } else { - debug!("build_display_list_for_child: Did not intersect..."); + if dirty.intersects(&abs_flow_bounds) { + debug!("build_display_list_for_child: intersected. recursing into child flow..."); + child_flow.build_display_list_recurse(builder, dirty, &adj_offset, list); + } else { + debug!("build_display_list_for_child: Did not intersect..."); + } } } } diff --git a/src/servo/layout/flow.rs b/src/servo/layout/flow.rs index 701136c2db2..a6ef3c36d5b 100644 --- a/src/servo/layout/flow.rs +++ b/src/servo/layout/flow.rs @@ -28,7 +28,7 @@ use dom::node::AbstractNode; use layout::block::{BlockFlowData, BlockLayout}; use layout::box::RenderBox; use layout::context::LayoutContext; -use layout::debug::BoxedMutDebugMethods; +use layout::debug::DebugMethods; use layout::display_list_builder::DisplayListBuilder; use layout::inline::{InlineFlowData, InlineLayout}; use layout::root::{RootFlowData, RootLayout}; @@ -43,13 +43,13 @@ use gfx::geometry::Au; /// The type of the formatting context and data specific to each context, such as line box /// structures or float lists. pub enum FlowContext { - AbsoluteFlow(FlowData), - BlockFlow(FlowData, BlockFlowData), - FloatFlow(FlowData), - InlineBlockFlow(FlowData), - InlineFlow(FlowData, InlineFlowData), - RootFlow(FlowData, RootFlowData), - TableFlow(FlowData) + AbsoluteFlow(@mut FlowData), + BlockFlow(@mut BlockFlowData), + FloatFlow(@mut FlowData), + InlineBlockFlow(@mut FlowData), + InlineFlow(@mut InlineFlowData), + RootFlow(@mut RootFlowData), + TableFlow(@mut FlowData), } pub enum FlowContextType { @@ -62,16 +62,18 @@ pub enum FlowContextType { Flow_Table } -/* A particular kind of layout context. It manages the positioning of - render boxes within the context. */ +/// Data common to all flows. +/// +/// FIXME: We need a naming convention for pseudo-inheritance like this. How about +/// `CommonFlowInfo`? pub struct FlowData { node: AbstractNode, - parent: Option<@mut FlowContext>, - first_child: Option<@mut FlowContext>, - last_child: Option<@mut FlowContext>, - prev_sibling: Option<@mut FlowContext>, - next_sibling: Option<@mut FlowContext>, + parent: Option<FlowContext>, + first_child: Option<FlowContext>, + last_child: Option<FlowContext>, + prev_sibling: Option<FlowContext>, + next_sibling: Option<FlowContext>, /* TODO (Issue #87): debug only */ id: int, @@ -105,31 +107,53 @@ impl FlowData { } impl<'self> FlowContext { - pub fn d(&'self mut self) -> &'self mut FlowData { - unsafe { - match *self { - AbsoluteFlow(ref d) => cast::transmute(d), - BlockFlow(ref d, _) => cast::transmute(d), - FloatFlow(ref d) => cast::transmute(d), - InlineBlockFlow(ref d) => cast::transmute(d), - InlineFlow(ref d, _) => cast::transmute(d), - RootFlow(ref d, _) => cast::transmute(d), - TableFlow(ref d) => cast::transmute(d) + #[inline(always)] + pub fn with_common_info<R>(&self, block: &fn(&mut FlowData) -> R) -> R { + match *self { + AbsoluteFlow(info) => block(info), + BlockFlow(info) => { + let info = &mut *info; // FIXME: Borrow check workaround. + block(&mut info.common) + } + FloatFlow(info) => block(info), + InlineBlockFlow(info) => block(info), + InlineFlow(info) => { + let info = &mut *info; // FIXME: Borrow check workaround. + block(&mut info.common) } + RootFlow(info) => { + let info = &mut *info; // FIXME: Borrow check workaround. + block(&mut info.common) + } + TableFlow(info) => block(info), + } + } + + pub fn position(&self) -> Rect<Au> { + do self.with_common_info |common_info| { + common_info.position + } + } + + /// Returns the ID of this flow. + #[inline(always)] + pub fn id(&self) -> int { + do self.with_common_info |info| { + info.id } } /// Iterates over the immediate children of this flow. /// /// TODO: Fold me into `util::tree`. - pub fn each_child(@mut self, f: &fn(@mut FlowContext) -> bool) { - let mut current_opt = self.d().first_child; + pub fn each_child(&self, f: &fn(FlowContext) -> bool) { + let mut current_opt = self.with_common_info(|info| info.first_child); while !current_opt.is_none() { let current = current_opt.get(); if !f(current) { break; } - current_opt = current.d().next_sibling; + current_opt = current.with_common_info(|info| info.next_sibling); } } @@ -137,173 +161,187 @@ impl<'self> FlowContext { /// detached from the tree before calling this method. /// /// TODO: Fold me into `util::tree`. - pub fn add_child(@mut self, child: @mut FlowContext) { - let self_data = self.d(), child_data = child.d(); - - assert!(child_data.parent.is_none()); - assert!(child_data.prev_sibling.is_none()); - assert!(child_data.next_sibling.is_none()); + pub fn add_child(&self, child: FlowContext) { + do self.with_common_info |self_info| { + do child.with_common_info |child_info| { + assert!(child_info.parent.is_none()); + assert!(child_info.prev_sibling.is_none()); + assert!(child_info.next_sibling.is_none()); + + match self_info.last_child { + None => { + self_info.first_child = Some(child); + } + Some(last_child) => { + do last_child.with_common_info |last_child_info| { + assert!(last_child_info.next_sibling.is_none()); + last_child_info.next_sibling = Some(child); + child_info.prev_sibling = Some(last_child); + } + } + } - match self_data.last_child { - None => { - self_data.first_child = Some(child); - } - Some(last_child) => { - assert!(last_child.d().next_sibling.is_none()); - last_child.d().next_sibling = Some(child); - child_data.prev_sibling = Some(last_child); + self_info.last_child = Some(child); + child_info.parent = Some(*self); } } - - self_data.last_child = Some(child); - child_data.parent = Some(self); } /// Removes the given flow from the tree. /// /// TODO: Fold me into `util::tree`. - pub fn remove_child(@mut self, child: @mut FlowContext) { - let self_data = self.d(), child_data = child.d(); - - assert!(child_data.parent.is_some()); - assert!(ptr::ref_eq(&*child_data.parent.get(), self)); + pub fn remove_child(&self, child: FlowContext) { + do self.with_common_info |self_info| { + do child.with_common_info |child_info| { + assert!(child_info.parent.is_some()); + + match child_info.prev_sibling { + None => self_info.first_child = child_info.next_sibling, + Some(prev_sibling) => { + do prev_sibling.with_common_info |prev_sibling_info| { + prev_sibling_info.next_sibling = child_info.next_sibling; + child_info.prev_sibling = None; + } + } + } - match child_data.prev_sibling { - None => self_data.first_child = child_data.next_sibling, - Some(prev_sibling) => { - prev_sibling.d().next_sibling = child_data.next_sibling; - child_data.prev_sibling = None; - } - } + match child_info.next_sibling { + None => { + do child.with_common_info |child_info| { + self_info.last_child = child_info.prev_sibling; + } + } + Some(next_sibling) => { + do next_sibling.with_common_info |next_sibling_info| { + next_sibling_info.prev_sibling = Some(next_sibling); + child_info.next_sibling = None; + } + } + } - match child_data.next_sibling { - None => self_data.last_child = child.d().prev_sibling, - Some(next_sibling) => { - next_sibling.d().prev_sibling = Some(next_sibling); - child_data.next_sibling = None; + child_info.parent = None; } } - - child_data.parent = None; } - pub fn inline(&'self mut self) -> &'self mut InlineFlowData { - match self { - &InlineFlow(_, ref i) => unsafe { cast::transmute(i) }, - _ => fail!(fmt!("Tried to access inline data of non-inline: f%d", self.d().id)) + pub fn inline(&self) -> @mut InlineFlowData { + match *self { + InlineFlow(info) => info, + _ => fail!(fmt!("Tried to access inline data of non-inline: f%d", self.id())) } } - pub fn block(&'self mut self) -> &'self mut BlockFlowData { - match self { - &BlockFlow(_, ref mut b) => unsafe { cast::transmute(b) }, - _ => fail!(fmt!("Tried to access block data of non-block: f%d", self.d().id)) + pub fn block(&self) -> @mut BlockFlowData { + match *self { + BlockFlow(info) => info, + _ => fail!(fmt!("Tried to access block data of non-block: f%d", self.id())) } } - pub fn root(&'self mut self) -> &'self mut RootFlowData { - match self { - &RootFlow(_, ref r) => unsafe { cast::transmute(r) }, - _ => fail!(fmt!("Tried to access root data of non-root: f%d", self.d().id)) + pub fn root(&self) -> @mut RootFlowData { + match *self { + RootFlow(info) => info, + _ => fail!(fmt!("Tried to access root data of non-root: f%d", self.id())) } } - pub fn bubble_widths(@mut self, ctx: &mut LayoutContext) { - match self { - @BlockFlow(*) => self.bubble_widths_block(ctx), - @InlineFlow(*) => self.bubble_widths_inline(ctx), - @RootFlow(*) => self.bubble_widths_root(ctx), - _ => fail!(fmt!("Tried to bubble_widths of flow: f%d", self.d().id)) + pub fn bubble_widths(&self, ctx: &mut LayoutContext) { + match *self { + BlockFlow(*) => self.bubble_widths_block(ctx), + InlineFlow(info) => info.bubble_widths_inline(ctx), + RootFlow(info) => info.bubble_widths_root(ctx), + _ => fail!(fmt!("Tried to bubble_widths of flow: f%d", self.id())) } } - pub fn assign_widths(@mut self, ctx: &mut LayoutContext) { - match self { - @BlockFlow(*) => self.assign_widths_block(ctx), - @InlineFlow(*) => self.assign_widths_inline(ctx), - @RootFlow(*) => self.assign_widths_root(ctx), - _ => fail!(fmt!("Tried to assign_widths of flow: f%d", self.d().id)) + pub fn assign_widths(&self, ctx: &mut LayoutContext) { + match *self { + BlockFlow(*) => self.assign_widths_block(ctx), + InlineFlow(info) => info.assign_widths_inline(ctx), + RootFlow(info) => info.assign_widths_root(ctx), + _ => fail!(fmt!("Tried to assign_widths of flow: f%d", self.id())) } } - pub fn assign_height(@mut self, ctx: &mut LayoutContext) { - match self { - @BlockFlow(*) => self.assign_height_block(ctx), - @InlineFlow(*) => self.assign_height_inline(ctx), - @RootFlow(*) => self.assign_height_root(ctx), - _ => fail!(fmt!("Tried to assign_height of flow: f%d", self.d().id)) + pub fn assign_height(&self, ctx: &mut LayoutContext) { + match *self { + BlockFlow(*) => self.assign_height_block(ctx), + InlineFlow(info) => info.assign_height_inline(ctx), + RootFlow(info) => info.assign_height_root(ctx), + _ => fail!(fmt!("Tried to assign_height of flow: f%d", self.id())) } } - pub fn build_display_list_recurse(@mut self, + pub fn build_display_list_recurse(&self, builder: &DisplayListBuilder, dirty: &Rect<Au>, offset: &Point2D<Au>, list: &Cell<DisplayList>) { - let d = self.d(); // FIXME: borrow checker workaround - debug!("FlowContext::build_display_list at %?: %s", d.position, self.debug_str()); + do self.with_common_info |info| { + debug!("FlowContext::build_display_list at %?: %s", info.position, self.debug_str()); + } - match self { - @RootFlow(*) => self.build_display_list_root(builder, dirty, offset, list), - @BlockFlow(*) => self.build_display_list_block(builder, dirty, offset, list), - @InlineFlow(*) => self.build_display_list_inline(builder, dirty, offset, list), + match *self { + RootFlow(info) => info.build_display_list_root(builder, dirty, offset, list), + BlockFlow(*) => self.build_display_list_block(builder, dirty, offset, list), + InlineFlow(info) => info.build_display_list_inline(builder, dirty, offset, list), _ => fail!(fmt!("Tried to build_display_list_recurse of flow: %?", self)) } } // Actual methods that do not require much flow-specific logic - pub fn foldl_all_boxes<B:Copy>(&mut self, - seed: B, - cb: &fn(a: B, b: @mut RenderBox) -> B) - -> B { - match self { - &RootFlow(*) => { - let root = self.root(); // FIXME: borrow checker workaround + pub fn foldl_all_boxes<B:Copy>(&self, seed: B, cb: &fn(a: B, b: @mut RenderBox) -> B) -> B { + match *self { + RootFlow(root) => { + let root = &mut *root; root.box.map_default(seed, |box| { cb(seed, *box) }) } - &BlockFlow(*) => { - let block = self.block(); // FIXME: borrow checker workaround + BlockFlow(block) => { + let block = &mut *block; block.box.map_default(seed, |box| { cb(seed, *box) }) } - &InlineFlow(*) => { - let inline = self.inline(); // FIXME: borrow checker workaround + InlineFlow(inline) => { + let inline = &mut *inline; inline.boxes.foldl(seed, |acc, box| { cb(*acc, *box) }) } _ => fail!(fmt!("Don't know how to iterate node's RenderBoxes for %?", self)) } } - pub fn foldl_boxes_for_node<B:Copy>(&mut self, + pub fn foldl_boxes_for_node<B:Copy>(&self, node: AbstractNode, seed: B, - cb: &fn(a: B, @mut RenderBox) -> B) + callback: &fn(a: B, @mut RenderBox) -> B) -> B { do self.foldl_all_boxes(seed) |acc, box| { - if box.d().node == node { cb(acc, box) } - else { acc } + if box.d().node == node { + callback(acc, box) + } else { + acc + } } } - pub fn iter_all_boxes(&mut self, cb: &fn(@mut RenderBox) -> bool) { - match self { - &RootFlow(*) => { - let root = self.root(); // FIXME: borrow checker workaround + pub fn iter_all_boxes(&self, cb: &fn(@mut RenderBox) -> bool) { + match *self { + RootFlow(root) => { + let root = &mut *root; for root.box.each |box| { if !cb(*box) { break; } } } - &BlockFlow(*) => { - let block = self.block(); // FIXME: borrow checker workaround + BlockFlow(block) => { + let block = &mut *block; for block.box.each |box| { if !cb(*box) { break; } } } - &InlineFlow(*) => { - let inline = self.inline(); // FIXME: borrow checker workaround + InlineFlow(inline) => { + let inline = &mut *inline; for inline.boxes.each |box| { if !cb(*box) { break; @@ -314,10 +352,10 @@ impl<'self> FlowContext { } } - pub fn iter_boxes_for_node(&mut self, node: AbstractNode, cb: &fn(@mut RenderBox) -> bool) { + pub fn iter_boxes_for_node(&self, node: AbstractNode, callback: &fn(@mut RenderBox) -> bool) { for self.iter_all_boxes |box| { if box.d().node == node { - if !cb(box) { + if !callback(box) { break; } } @@ -325,13 +363,13 @@ impl<'self> FlowContext { } } -impl BoxedMutDebugMethods for FlowContext { - fn dump(@mut self) { +impl DebugMethods for FlowContext { + fn dump(&self) { self.dump_indent(0); } /// Dumps the flow tree, for debugging, with indentation. - fn dump_indent(@mut self, indent: uint) { + fn dump_indent(&self, indent: uint) { let mut s = ~"|"; for uint::range(0, indent) |_i| { s += ~"---- "; @@ -346,24 +384,26 @@ impl BoxedMutDebugMethods for FlowContext { } } - fn debug_str(@mut self) -> ~str { + fn debug_str(&self) -> ~str { let repr = match *self { - InlineFlow(*) => { - let inline = self.inline(); // FIXME: borrow checker workaround + InlineFlow(inline) => { + let inline = &mut *inline; let mut s = inline.boxes.foldl(~"InlineFlow(children=", |s, box| { fmt!("%s b%d", *s, box.d().id) }); s += ~")"; s }, - BlockFlow(*) => { - match self.block().box { + BlockFlow(block) => { + let block = &mut *block; + match block.box { Some(box) => fmt!("BlockFlow(box=b%d)", box.d().id), None => ~"BlockFlow", } }, - RootFlow(*) => { - match self.root().box { + RootFlow(root) => { + let root = &mut *root; + match root.box { Some(box) => fmt!("RootFlo(box=b%d)", box.d().id), None => ~"RootFlow", } @@ -371,8 +411,9 @@ impl BoxedMutDebugMethods for FlowContext { _ => ~"(Unknown flow)" }; - let d = self.d(); // FIXME: borrow checker workaround - fmt!("f%? %?", d.id, repr) + do self.with_common_info |info| { + fmt!("f%? %?", info.id, repr) + } } } diff --git a/src/servo/layout/inline.rs b/src/servo/layout/inline.rs index a5a6f5d355b..57cbe17de56 100644 --- a/src/servo/layout/inline.rs +++ b/src/servo/layout/inline.rs @@ -2,26 +2,27 @@ * 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/. */ -use core; use core::cell::Cell; +use core; use dom::node::AbstractNode; use layout::box::*; use layout::context::LayoutContext; use layout::debug::{BoxedDebugMethods, BoxedMutDebugMethods, DebugMethods}; use layout::display_list_builder::DisplayListBuilder; -use layout::flow::{FlowContext, InlineFlow}; +use layout::flow::{FlowContext, FlowData, InlineFlow}; use layout::text::{UnscannedMethods, adapt_textbox_with_range}; +use core::util; use geom::{Point2D, Rect, Size2D}; use gfx::display_list::DisplayList; use gfx::geometry::Au; use gfx::image::holder; use gfx::text::text_run::TextRun; use gfx::text::util::*; +use newcss::values::{CSSTextAlignCenter, CSSTextAlignJustify, CSSTextAlignLeft}; +use newcss::values::{CSSTextAlignRight}; use servo_util::range::Range; -use newcss::values::{CSSTextAlignCenter, CSSTextAlignJustify, CSSTextAlignLeft, CSSTextAlignRight}; use std::deque::Deque; -use core::util; /* Lineboxes are represented as offsets into the child list, rather than @@ -81,7 +82,9 @@ pub impl ElementMapping { do self.entries.eachi |i, nr| { cb(i, nr) } } - fn repair_for_box_changes(&mut self, old_boxes: &[@mut RenderBox], new_boxes: &[@mut RenderBox]) { + fn repair_for_box_changes(&mut self, + old_boxes: &[@mut RenderBox], + new_boxes: &[@mut RenderBox]) { let entries = &mut self.entries; debug!("--- Old boxes: ---"); @@ -166,34 +169,30 @@ priv impl TextRunScanner { } priv impl TextRunScanner { - fn scan_for_runs(&mut self, ctx: &mut LayoutContext, flow: @mut FlowContext) { - let inline = flow.inline(); + fn scan_for_runs(&mut self, ctx: &mut LayoutContext, flow: FlowContext) { + let inline = &mut *flow.inline(); assert!(inline.boxes.len() > 0); + debug!("TextRunScanner: scanning %u boxes for text runs...", inline.boxes.len()); - let in_boxes = &mut flow.inline().boxes; - //do boxes.swap |in_boxes| { - debug!("TextRunScanner: scanning %u boxes for text runs...", in_boxes.len()); - let mut out_boxes = ~[]; - - for uint::range(0, in_boxes.len()) |box_i| { - debug!("TextRunScanner: considering box: %?", in_boxes[box_i].debug_str()); - if box_i > 0 && !can_coalesce_text_nodes(*in_boxes, box_i-1, box_i) { - self.flush_clump_to_list(ctx, flow, *in_boxes, &mut out_boxes); - } - self.clump.extend_by(1); - } - // handle remaining clumps - if self.clump.length() > 0 { - self.flush_clump_to_list(ctx, flow, *in_boxes, &mut out_boxes); + let mut out_boxes = ~[]; + for uint::range(0, inline.boxes.len()) |box_i| { + debug!("TextRunScanner: considering box: %?", inline.boxes[box_i].debug_str()); + if box_i > 0 && !can_coalesce_text_nodes(inline.boxes, box_i-1, box_i) { + self.flush_clump_to_list(ctx, flow, inline.boxes, &mut out_boxes); } + self.clump.extend_by(1); + } + // handle remaining clumps + if self.clump.length() > 0 { + self.flush_clump_to_list(ctx, flow, inline.boxes, &mut out_boxes); + } + + debug!("TextRunScanner: swapping out boxes."); - debug!("TextRunScanner: swapping out boxes."); - // swap out old and new box list of flow, by supplying - // temp boxes as return value to boxes.swap |...| - util::swap(in_boxes, &mut out_boxes); - //} + // Swap out the old and new box list of the flow. + inline.boxes = out_boxes; - // helper functions + // A helper function. fn can_coalesce_text_nodes(boxes: &[@mut RenderBox], left_i: uint, right_i: uint) -> bool { assert!(left_i < boxes.len()); assert!(right_i > 0 && right_i < boxes.len()); @@ -221,7 +220,7 @@ priv impl TextRunScanner { // boxes are appended, the caller swaps the flow's box list. fn flush_clump_to_list(&mut self, ctx: &mut LayoutContext, - flow: @mut FlowContext, + flow: FlowContext, in_boxes: &[@mut RenderBox], out_boxes: &mut ~[@mut RenderBox]) { assert!(self.clump.length() > 0); @@ -337,14 +336,14 @@ struct PendingLine { } struct LineboxScanner { - flow: @mut FlowContext, + flow: FlowContext, new_boxes: ~[@mut RenderBox], work_list: @mut Deque<@mut RenderBox>, pending_line: PendingLine, line_spans: ~[Range], } -fn LineboxScanner(inline: @mut FlowContext) -> LineboxScanner { +fn LineboxScanner(inline: FlowContext) -> LineboxScanner { assert!(inline.starts_inline_flow()); LineboxScanner { @@ -358,7 +357,7 @@ fn LineboxScanner(inline: @mut FlowContext) -> LineboxScanner { impl LineboxScanner { priv fn reset_scanner(&mut self) { - debug!("Resetting line box scanner's state for flow f%d.", self.flow.d().id); + debug!("Resetting line box scanner's state for flow f%d.", self.flow.id()); self.line_spans = ~[]; self.new_boxes = ~[]; self.reset_linebox(); @@ -412,20 +411,13 @@ impl LineboxScanner { priv fn swap_out_results(&mut self) { debug!("LineboxScanner: Propagating scanned lines[n=%u] to inline flow f%d", - self.line_spans.len(), self.flow.d().id); + self.line_spans.len(), + self.flow.id()); - //do self.new_boxes.swap |boxes| { - let inline_boxes = &mut self.flow.inline().boxes; + let inline_boxes = &mut self.flow.inline().boxes; util::swap(inline_boxes, &mut self.new_boxes); - //inline_boxes = boxes; - // ~[] - //}; - //do self.line_spans.swap |boxes| { - let lines = &mut self.flow.inline().lines; + let lines = &mut self.flow.inline().lines; util::swap(lines, &mut self.line_spans); - // lines = boxes; - // ~[] - //}; } priv fn flush_current_line(&mut self) { @@ -450,7 +442,7 @@ impl LineboxScanner { linebox_align = CSSTextAlignLeft; } - let slack_width = self.flow.d().position.size.width - self.pending_line.width; + let slack_width = self.flow.position().size.width - self.pending_line.width; match linebox_align { // So sorry, but justified text is more complicated than shuffling linebox coordinates. // TODO(Issue #213): implement `text-align: justify` @@ -478,6 +470,7 @@ impl LineboxScanner { } }, } + // clear line and add line mapping debug!("LineboxScanner: Saving information for flushed line %u.", self.line_spans.len()); self.line_spans.push(line_range); @@ -486,7 +479,7 @@ impl LineboxScanner { // return value: whether any box was appended. priv fn try_append_to_line(&mut self, ctx: &LayoutContext, in_box: @mut RenderBox) -> bool { - let remaining_width = self.flow.d().position.size.width - self.pending_line.width; + let remaining_width = self.flow.position().size.width - self.pending_line.width; let in_box_width = in_box.d().position.size.width; let line_is_empty: bool = self.pending_line.range.length() == 0; @@ -579,6 +572,9 @@ impl LineboxScanner { } pub struct InlineFlowData { + /// Data common to all flows. + common: FlowData, + // A vec of all inline render boxes. Several boxes may // correspond to one Node/Element. boxes: ~[@mut RenderBox], @@ -591,74 +587,79 @@ pub struct InlineFlowData { elems: ElementMapping } -pub fn InlineFlowData() -> InlineFlowData { - InlineFlowData { - boxes: ~[], - lines: ~[], - elems: ElementMapping::new(), +impl InlineFlowData { + pub fn new(common: FlowData) -> InlineFlowData { + InlineFlowData { + common: common, + boxes: ~[], + lines: ~[], + elems: ElementMapping::new(), + } } } pub trait InlineLayout { fn starts_inline_flow(&self) -> bool; - - fn bubble_widths_inline(@mut self, ctx: &mut LayoutContext); - fn assign_widths_inline(@mut self, ctx: &mut LayoutContext); - fn assign_height_inline(@mut self, ctx: &mut LayoutContext); - fn build_display_list_inline(@mut self, a: &DisplayListBuilder, b: &Rect<Au>, c: &Point2D<Au>, - d: &Cell<DisplayList>); } impl InlineLayout for FlowContext { - fn starts_inline_flow(&self) -> bool { match *self { InlineFlow(*) => true, _ => false } } - - fn bubble_widths_inline(@mut self, ctx: &mut LayoutContext) { - assert!(self.starts_inline_flow()); + fn starts_inline_flow(&self) -> bool { + match *self { + InlineFlow(*) => true, + _ => false + } + } +} +impl InlineFlowData { + pub fn bubble_widths_inline(@mut self, ctx: &mut LayoutContext) { let mut scanner = TextRunScanner::new(); - scanner.scan_for_runs(ctx, self); + scanner.scan_for_runs(ctx, InlineFlow(self)); - let mut min_width = Au(0); - let mut pref_width = Au(0); + { + let this = &mut *self; - let boxes = &mut self.inline().boxes; - for boxes.each |box| { - debug!("FlowContext[%d]: measuring %s", self.d().id, box.debug_str()); - min_width = Au::max(min_width, box.get_min_width(ctx)); - pref_width = Au::max(pref_width, box.get_pref_width(ctx)); - } + let mut min_width = Au(0); + let mut pref_width = Au(0); - self.d().min_width = min_width; - self.d().pref_width = pref_width; + for this.boxes.each |box| { + debug!("FlowContext[%d]: measuring %s", self.common.id, box.debug_str()); + min_width = Au::max(min_width, box.get_min_width(ctx)); + pref_width = Au::max(pref_width, box.get_pref_width(ctx)); + } + + this.common.min_width = min_width; + this.common.pref_width = pref_width; + } } - /* Recursively (top-down) determines the actual width of child - contexts and boxes. When called on this context, the context has - had its width set by the parent context. */ - fn assign_widths_inline(@mut self, ctx: &mut LayoutContext) { - assert!(self.starts_inline_flow()); - - // initialize (content) box widths, if they haven't been - // already. This could be combined with LineboxScanner's walk - // over the box list, and/or put into RenderBox. - let boxes = &mut self.inline().boxes; - for boxes.each |&box| { - let box2 = &mut *box; - box.d().position.size.width = match *box2 { - ImageBox(_, ref img) => { - let img2: &mut holder::ImageHolder = unsafe { cast::transmute(img) }; - Au::from_px(img2.get_size().get_or_default(Size2D(0,0)).width) - } - TextBox(*) => { /* text boxes are initialized with dimensions */ - box.d().position.size.width - }, - // TODO(Issue #225): different cases for 'inline-block', other replaced content - GenericBox(*) => Au::from_px(45), - _ => fail!(fmt!("Tried to assign width to unknown Box variant: %?", box)) - }; - } // for boxes.each |box| + /// Recursively (top-down) determines the actual width of child contexts and boxes. When called + /// on this context, the context has had its width set by the parent context. + pub fn assign_widths_inline(@mut self, ctx: &mut LayoutContext) { + { + // initialize (content) box widths, if they haven't been + // already. This could be combined with LineboxScanner's walk + // over the box list, and/or put into RenderBox. + let this = &mut *self; + for this.boxes.each |&box| { + let box2 = &mut *box; + box.d().position.size.width = match *box2 { + ImageBox(_, ref img) => { + let img2: &mut holder::ImageHolder = unsafe { cast::transmute(img) }; + Au::from_px(img2.get_size().get_or_default(Size2D(0,0)).width) + } + TextBox(*) => { + // Text boxes are initialized with dimensions. + box.d().position.size.width + }, + // TODO(Issue #225): different cases for 'inline-block', other replaced content + GenericBox(*) => Au::from_px(45), + _ => fail!(fmt!("Tried to assign width to unknown Box variant: %?", box)) + }; + } // for boxes.each |box| + } - let mut scanner = LineboxScanner(self); + let mut scanner = LineboxScanner(InlineFlow(self)); scanner.scan_for_lines(ctx); /* There are no child contexts, so stop here. */ @@ -670,31 +671,34 @@ impl InlineLayout for FlowContext { // 'inline-block' box that created this flow before recursing. } - fn assign_height_inline(@mut self, _ctx: &mut LayoutContext) { - // TODO(Issue #226): get CSS 'line-height' property from - // containing block's style to determine minimum linebox height. - // TODO(Issue #226): get CSS 'line-height' property from each non-replaced - // inline element to determine its height for computing linebox height. + pub fn assign_height_inline(&mut self, _ctx: &mut LayoutContext) { + // TODO(#226): Get the CSS `line-height` property from the containing block's style to + // determine minimum linebox height. + // + // TODO(#226): Get the CSS `line-height` property from each non-replaced inline element to + // determine its height for computing linebox height. + let line_height = Au::from_px(20); let mut cur_y = Au(0); - let lines = &mut self.inline().lines; - for lines.eachi |i, line_span| { + for self.lines.eachi |i, line_span| { debug!("assign_height_inline: processing line %u with box span: %?", i, line_span); // coords relative to left baseline let mut linebox_bounding_box = Au::zero_rect(); - let boxes = &mut self.inline().boxes; + let boxes = &mut self.boxes; for line_span.eachi |box_i| { - let cur_box : &mut RenderBox = boxes[box_i]; // FIXME: borrow checker workaround + let cur_box: &mut RenderBox = boxes[box_i]; // FIXME: borrow checker workaround - // compute box height. + // Compute the height of each box. let d = cur_box.d(); // FIXME: borrow checker workaround - let cur_box : &mut RenderBox = boxes[box_i]; // FIXME: borrow checker workaround + let cur_box: &mut RenderBox = boxes[box_i]; // FIXME: borrow checker workaround + d.position.size.height = match *cur_box { ImageBox(_, ref img) => { Au::from_px(img.size().height) } - TextBox(*) => { /* text boxes are initialized with dimensions */ + TextBox(*) => { + // Text boxes are initialized with dimensions. d.position.size.height }, // TODO(Issue #225): different cases for 'inline-block', other replaced content @@ -706,31 +710,38 @@ impl InlineLayout for FlowContext { } }; - // compute bounding rect, with left baseline as origin. - // so, linebox height is a matter of lining up ideal baselines, - // and then using the union of all these rects. + // Compute the bounding rect with the left baseline as origin. Linebox height is a + // matter of lining up ideal baselines and then using the union of all these rects. let bounding_box = match *cur_box { - // adjust to baseline coords - // TODO(Issue #227): use left/right margins, border, padding for nonreplaced content, - // and also use top/bottom margins, border, padding for replaced or inline-block content. - // TODO(Issue #225): use height, width for 'inline-block', other replaced content + // Adjust to baseline coordinates. + // + // TODO(#227): Use left/right margins, border, padding for nonreplaced content, + // and also use top/bottom margins, border, padding for replaced or + // inline-block content. + // + // TODO(#225): Use height, width for 'inline-block' and other replaced content. ImageBox(*) | GenericBox(*) => { let box_bounds = d.position; box_bounds.translate(&Point2D(Au(0), -d.position.size.height)) }, - // adjust bounding box metric to box's horizontal offset - // TODO: we can use font metrics directly instead of re-measuring for the bounding box. + + // Adjust the bounding box metric to the box's horizontal offset. + // TODO: We can use font metrics directly instead of re-measuring for the + // bounding box. TextBox(_, data) => { let text_bounds = data.run.metrics_for_range(&data.range).bounding_box; text_bounds.translate(&Point2D(d.position.origin.x, Au(0))) }, + _ => { let cur_box = boxes[box_i]; // FIXME: borrow checker workaround fail!(fmt!("Tried to compute bounding box of unknown Box variant: %s", cur_box.debug_str())) } }; - debug!("assign_height_inline: bounding box for box b%d = %?", cur_box.d().id, bounding_box); + debug!("assign_height_inline: bounding box for box b%d = %?", + cur_box.d().id, + bounding_box); linebox_bounding_box = linebox_bounding_box.union(&bounding_box); debug!("assign_height_inline: linebox bounding box = %?", linebox_bounding_box); } @@ -743,30 +754,33 @@ impl InlineLayout for FlowContext { // 'line-height' when calculating linebox height. Then, go back over // and set y offsets according to 'vertical-align' property of containing block. let halfleading = match cur_box { - @TextBox(_, data) => { (data.run.font.metrics.em_size - line_height).scale_by(0.5f) }, - _ => { Au(0) } + @TextBox(_, data) => { + (data.run.font.metrics.em_size - line_height).scale_by(0.5f) + }, + _ => Au(0), }; - cur_box.d().position.origin.y = cur_y + halfleading + (baseline_offset - cur_box.d().position.size.height); + cur_box.d().position.origin.y = + cur_y + halfleading + (baseline_offset - cur_box.d().position.size.height); } cur_y += Au::max(line_height, linebox_height); } // /lines.each |line_span| - self.d().position.size.height = cur_y; + self.common.position.size.height = cur_y; } - fn build_display_list_inline(@mut self, builder: &DisplayListBuilder, dirty: &Rect<Au>, - offset: &Point2D<Au>, list: &Cell<DisplayList>) { - - assert!(self.starts_inline_flow()); - + pub fn build_display_list_inline(&mut self, + builder: &DisplayListBuilder, + dirty: &Rect<Au>, + offset: &Point2D<Au>, + list: &Cell<DisplayList>) { // TODO(Issue #228): once we form line boxes and have their cached bounds, we can be // smarter and not recurse on a line if nothing in it can intersect dirty - let inline = self.inline(); // FIXME: borrow checker workaround debug!("FlowContext[%d]: building display list for %u inline boxes", - self.d().id, inline.boxes.len()); - let boxes = &mut self.inline().boxes; - for boxes.each |box| { + self.common.id, + self.boxes.len()); + + for self.boxes.each |box| { box.build_display_list(builder, dirty, offset, list) } diff --git a/src/servo/layout/layout_task.rs b/src/servo/layout/layout_task.rs index 3a3b106324d..c8f7bf23708 100644 --- a/src/servo/layout/layout_task.rs +++ b/src/servo/layout/layout_task.rs @@ -205,10 +205,9 @@ impl Layout { } } - let layout_root: @mut FlowContext = do time("layout: tree construction") { + let layout_root: FlowContext = do time("layout: tree construction") { let mut builder = LayoutTreeBuilder::new(); - let layout_root: @mut FlowContext = match builder.construct_trees(&layout_ctx, - *node) { + let layout_root: FlowContext = match builder.construct_trees(&layout_ctx, *node) { Ok(root) => root, Err(*) => fail!(~"Root flow should always exist") }; @@ -235,9 +234,11 @@ impl Layout { // TODO: set options on the builder before building // TODO: be smarter about what needs painting - layout_root.build_display_list(&builder, - © layout_root.d().position, - display_list); + do layout_root.with_common_info |layout_root_info| { + layout_root.build_display_list(&builder, + © layout_root_info.position, + display_list); + } let render_layer = RenderLayer { display_list: display_list.take(), diff --git a/src/servo/layout/root.rs b/src/servo/layout/root.rs index af92b037d78..d74d8bb345b 100644 --- a/src/servo/layout/root.rs +++ b/src/servo/layout/root.rs @@ -10,27 +10,28 @@ use gfx::geometry::Au; use layout::block::BlockLayout; use layout::box::RenderBox; use layout::context::LayoutContext; -use layout::flow::{FlowContext, RootFlow}; +use layout::flow::{FlowContext, FlowData, RootFlow}; use layout::display_list_builder::DisplayListBuilder; pub struct RootFlowData { + /// Data common to all flows. + common: FlowData, + + /// The render box at the root of the tree. box: Option<@mut RenderBox> } -pub fn RootFlowData() -> RootFlowData { - RootFlowData { - box: None +impl RootFlowData { + pub fn new(common: FlowData) -> RootFlowData { + RootFlowData { + common: common, + box: None, + } } } pub trait RootLayout { fn starts_root_flow(&self) -> bool; - - fn bubble_widths_root(@mut self, ctx: &LayoutContext); - fn assign_widths_root(@mut self, ctx: &LayoutContext); - fn assign_height_root(@mut self, ctx: &LayoutContext); - fn build_display_list_root(@mut self, a: &DisplayListBuilder, b: &Rect<Au>, - c: &Point2D<Au>, d: &Cell<DisplayList>); } impl RootLayout for FlowContext { @@ -40,47 +41,47 @@ impl RootLayout for FlowContext { _ => false } } +} - /* defer to the block algorithm */ - fn bubble_widths_root(@mut self, ctx: &LayoutContext) { - assert!(self.starts_root_flow()); - self.bubble_widths_block(ctx) +impl RootFlowData { + /// Defer to the block algorithm. + pub fn bubble_widths_root(@mut self, ctx: &LayoutContext) { + RootFlow(self).bubble_widths_block(ctx) } - fn assign_widths_root(@mut self, ctx: &LayoutContext) { - assert!(self.starts_root_flow()); + pub fn assign_widths_root(@mut self, ctx: &LayoutContext) { + self.common.position.origin = Au::zero_point(); + self.common.position.size.width = ctx.screen_size.size.width; - self.d().position.origin = Au::zero_point(); - self.d().position.size.width = ctx.screen_size.size.width; - - self.assign_widths_block(ctx) + RootFlow(self).assign_widths_block(ctx) } - fn assign_height_root(@mut self, ctx: &LayoutContext) { - assert!(self.starts_root_flow()); - + pub fn assign_height_root(@mut self, ctx: &LayoutContext) { // this is essentially the same as assign_height_block(), except // the root adjusts self height to at least cover the viewport. let mut cur_y = Au(0); - for self.each_child |child_ctx| { - child_ctx.d().position.origin.y = cur_y; - cur_y += child_ctx.d().position.size.height; + for RootFlow(self).each_child |child_flow| { + do child_flow.with_common_info |child_common_info| { + child_common_info.position.origin.y = cur_y; + cur_y += child_common_info.position.size.height; + } } - self.d().position.size.height = Au::max(ctx.screen_size.size.height, cur_y); + self.common.position.size.height = Au::max(ctx.screen_size.size.height, cur_y); - do self.with_block_box |box| { + do RootFlow(self).with_block_box |box| { box.d().position.origin.y = Au(0); box.d().position.size.height = Au::max(ctx.screen_size.size.height, cur_y); let (_used_top, _used_bot) = box.get_used_height(); } } - fn build_display_list_root(@mut self, builder: &DisplayListBuilder, dirty: &Rect<Au>, - offset: &Point2D<Au>, list: &Cell<DisplayList>) { - assert!(self.starts_root_flow()); - - self.build_display_list_block(builder, dirty, offset, list); + pub fn build_display_list_root(@mut self, + builder: &DisplayListBuilder, + dirty: &Rect<Au>, + offset: &Point2D<Au>, + list: &Cell<DisplayList>) { + RootFlow(self).build_display_list_block(builder, dirty, offset, list); } } diff --git a/src/servo/layout/traverse.rs b/src/servo/layout/traverse.rs index b4710b974bc..d242bcae4f5 100644 --- a/src/servo/layout/traverse.rs +++ b/src/servo/layout/traverse.rs @@ -6,22 +6,22 @@ use layout::flow::FlowContext; /// A trait for running tree-based traversals over flows contexts. pub trait FlowContextTraversals { - fn traverse_preorder(@mut self, preorder_cb: &fn(@mut FlowContext)); - fn traverse_postorder(@mut self, postorder_cb: &fn(@mut FlowContext)); + fn traverse_preorder(&self, preorder_cb: &fn(FlowContext)); + fn traverse_postorder(&self, postorder_cb: &fn(FlowContext)); } impl FlowContextTraversals for FlowContext { - fn traverse_preorder(@mut self, preorder_cb: &fn(@mut FlowContext)) { - preorder_cb(self); + fn traverse_preorder(&self, preorder_cb: &fn(FlowContext)) { + preorder_cb(*self); for self.each_child |child| { child.traverse_preorder(preorder_cb); } } - fn traverse_postorder(@mut self, postorder_cb: &fn(@mut FlowContext)) { + fn traverse_postorder(&self, postorder_cb: &fn(FlowContext)) { for self.each_child |child| { child.traverse_postorder(postorder_cb); } - postorder_cb(self); + postorder_cb(*self); } } |