diff options
Diffstat (limited to 'src/servo/layout/box_builder.rs')
-rw-r--r-- | src/servo/layout/box_builder.rs | 286 |
1 files changed, 147 insertions, 139 deletions
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?"); } |