diff --git a/src/components/gfx/render_task.rs b/src/components/gfx/render_task.rs index d91465ae583..1586c28b860 100644 --- a/src/components/gfx/render_task.rs +++ b/src/components/gfx/render_task.rs @@ -19,19 +19,22 @@ use render_context::RenderContext; use std::cell::Cell; use std::comm::{Chan, Port, SharedChan}; +use extra::arc::Arc; use servo_util::time::{ProfilerChan, profile}; use servo_util::time; use extra::arc; -pub struct RenderLayer { - display_list: DisplayList<()>, + + +pub struct RenderLayer { + display_list: Arc>, size: Size2D } -pub enum Msg { - RenderMsg(RenderLayer), +pub enum Msg { + RenderMsg(RenderLayer), ReRenderMsg(~[BufferRequest], f32, PipelineId, Epoch), PaintPermissionGranted, PaintPermissionRevoked, @@ -56,24 +59,24 @@ pub fn BufferRequest(screen_rect: Rect, page_rect: Rect) -> BufferReq } #[deriving(Clone)] -pub struct RenderChan { - chan: SharedChan, +pub struct RenderChan { + chan: SharedChan>, } -impl RenderChan { - pub fn new(chan: Chan) -> RenderChan { +impl RenderChan { + pub fn new(chan: Chan>) -> RenderChan { RenderChan { chan: SharedChan::new(chan), } } - pub fn send(&self, msg: Msg) { + pub fn send(&self, msg: Msg) { self.chan.send(msg); } } -struct RenderTask { +struct RenderTask { id: PipelineId, - port: Port, + port: Port>, compositor: C, font_ctx: @mut FontContext, opts: Opts, @@ -84,7 +87,7 @@ struct RenderTask { share_gl_context: AzGLContext, /// The layer to be rendered - render_layer: Option, + render_layer: Option>, /// Permission to send paint messages to the compositor paint_permission: bool, /// Cached copy of last layers rendered @@ -93,9 +96,9 @@ struct RenderTask { epoch: Epoch, } -impl RenderTask { +impl RenderTask { pub fn create(id: PipelineId, - port: Port, + port: Port>, compositor: C, opts: Opts, profiler_chan: ProfilerChan) { @@ -226,7 +229,7 @@ impl RenderTask { // Draw the display list. do profile(time::RenderingDrawingCategory, self.profiler_chan.clone()) { - render_layer.display_list.draw_into_context(&ctx); + render_layer.display_list.get().draw_into_context(&ctx); ctx.canvas.draw_target.flush(); } } diff --git a/src/components/main/layout/aux.rs b/src/components/main/layout/aux.rs index f49d73de913..f891ce03389 100644 --- a/src/components/main/layout/aux.rs +++ b/src/components/main/layout/aux.rs @@ -4,13 +4,20 @@ //! Code for managing the layout data in the DOM. -use layout::flow::FlowContext; use layout::incremental::RestyleDamage; +use gfx::display_list::DisplayList; +use servo_util::range::Range; +use extra::arc::Arc; use newcss::complete::CompleteSelectResults; use script::dom::node::{AbstractNode, LayoutView}; use servo_util::tree::TreeNodeRef; +pub struct DisplayBoxes { + display_list: Option>>>, + range: Option, +} + /// Data that layout associates with a node. pub struct LayoutData { /// The results of CSS styling for this node. @@ -19,8 +26,9 @@ pub struct LayoutData { /// Description of how to account for recent style changes. restyle_damage: Option, - /// The CSS flow that this node is associated with. - flow: Option, + /// The boxes assosiated with this flow. + /// Used for getBoundingClientRect and friends. + boxes: DisplayBoxes, } impl LayoutData { @@ -29,7 +37,7 @@ impl LayoutData { LayoutData { style: None, restyle_damage: None, - flow: None, + boxes: DisplayBoxes { display_list: None, range: None }, } } } @@ -65,14 +73,8 @@ impl LayoutAuxMethods for AbstractNode { /// box in the COW model) and populates it with an empty style object. fn initialize_layout_data(self) -> Option<@mut LayoutData> { if self.has_layout_data() { - { - let layout_data = &mut self.layout_data().flow; - match *layout_data { - Some(ref flow) => flow.teardown(), - None => () - } - } - self.layout_data().flow = None; + self.layout_data().boxes.display_list = None; + self.layout_data().boxes.range = None; None } else { let data = @mut LayoutData::new(); diff --git a/src/components/main/layout/block.rs b/src/components/main/layout/block.rs index 0379689d5fb..8486d5225be 100644 --- a/src/components/main/layout/block.rs +++ b/src/components/main/layout/block.rs @@ -19,7 +19,6 @@ use geom::rect::Rect; use gfx::display_list::DisplayList; use gfx::geometry::{Au, to_frac_px}; use gfx::geometry; -use servo_util::tree::TreeNodeRef; pub struct BlockFlowData { /// Data common to all flows. @@ -50,7 +49,6 @@ impl BlockFlowData { } pub fn teardown(&mut self) { - self.common.teardown(); for box in self.box.iter() { box.teardown(); } @@ -66,7 +64,7 @@ pub trait BlockLayout { impl BlockLayout for FlowContext { fn starts_root_flow(&self) -> bool { match *self { - BlockFlow(info) => info.is_root, + BlockFlow(ref info) => info.is_root, _ => false } } @@ -89,13 +87,13 @@ impl BlockFlowData { /* TODO: floats */ /* TODO: absolute contexts */ /* TODO: inline-blocks */ - pub fn bubble_widths_block(@mut self, ctx: &LayoutContext) { + pub fn bubble_widths_block(&mut self, ctx: &LayoutContext) { let mut min_width = Au(0); let mut pref_width = Au(0); let mut num_floats = 0; /* find max width from child block contexts */ - for child_ctx in BlockFlow(self).children() { + for child_ctx in self.common.child_iter() { assert!(child_ctx.starts_block_flow() || child_ctx.starts_inline_flow()); do child_ctx.with_mut_base |child_node| { @@ -179,7 +177,7 @@ impl BlockFlowData { /// /// Dual boxes consume some width first, and the remainder is assigned to all child (block) /// contexts. - pub fn assign_widths_block(@mut self, ctx: &LayoutContext) { + pub fn assign_widths_block(&mut self, ctx: &LayoutContext) { debug!("assign_widths_block: assigning width for flow %?", self.common.id); if self.is_root { debug!("Setting root position"); @@ -240,7 +238,7 @@ impl BlockFlowData { } let has_inorder_children = self.common.is_inorder || self.common.num_floats > 0; - for kid in BlockFlow(self).children() { + for kid in self.common.child_iter() { assert!(kid.starts_block_flow() || kid.starts_inline_flow()); do kid.with_mut_base |child_node| { @@ -255,12 +253,12 @@ impl BlockFlowData { } } - pub fn assign_height_inorder_block(@mut self, ctx: &mut LayoutContext) { + pub fn assign_height_inorder_block(&mut self, ctx: &mut LayoutContext) { debug!("assign_height_inorder_block: assigning height for block %?", self.common.id); self.assign_height_block_base(ctx, true); } - pub fn assign_height_block(@mut self, ctx: &mut LayoutContext) { + pub fn assign_height_block(&mut self, ctx: &mut LayoutContext) { debug!("assign_height_block: assigning height for block %?", self.common.id); // This is the only case in which a block flow can start an inorder // subtraversal. @@ -271,7 +269,7 @@ impl BlockFlowData { self.assign_height_block_base(ctx, false); } - fn assign_height_block_base(@mut self, ctx: &mut LayoutContext, inorder: bool) { + fn assign_height_block_base(&mut self, ctx: &mut LayoutContext, inorder: bool) { let mut cur_y = Au(0); let mut clearance = Au(0); let mut top_offset = Au(0); @@ -304,7 +302,7 @@ impl BlockFlowData { // repeat until all children are visited. // last_child.floats_out -> self.floats_out (done at the end of this method) float_ctx = self.common.floats_in.translate(Point2D(-left_offset, -top_offset)); - for kid in BlockFlow(self).children() { + for kid in self.common.child_iter() { do kid.with_mut_base |child_node| { child_node.floats_in = float_ctx.clone(); } @@ -314,7 +312,7 @@ impl BlockFlowData { } } } - for kid in BlockFlow(self).children() { + for kid in self.common.child_iter() { do kid.with_mut_base |child_node| { child_node.position.origin.y = cur_y; cur_y = cur_y + child_node.position.size.height; @@ -359,7 +357,7 @@ impl BlockFlowData { } } - pub fn build_display_list_block(@mut self, + pub fn build_display_list_block(&mut self, builder: &DisplayListBuilder, dirty: &Rect, list: &Cell>) @@ -388,9 +386,11 @@ impl BlockFlowData { let abs_rect = Rect(self.common.abs_position, self.common.position.size); if !abs_rect.intersects(dirty) { - return false; + return true; } + debug!("build_display_list_block: adding display element"); + // add box that starts block context self.box.map(|&box| { box.build_display_list(builder, dirty, &self.common.abs_position, list) @@ -398,16 +398,14 @@ impl BlockFlowData { // TODO: handle any out-of-flow elements - - // go deeper into the flow tree - let flow = BlockFlow(self); - for child in flow.children() { + let this_position = self.common.abs_position; + for child in self.common.child_iter() { do child.with_mut_base |base| { - base.abs_position = self.common.abs_position + base.position.origin; + base.abs_position = this_position + base.position.origin; } } - true + false } } diff --git a/src/components/main/layout/box.rs b/src/components/main/layout/box.rs index fbe4c3dae44..2d30a2d584b 100644 --- a/src/components/main/layout/box.rs +++ b/src/components/main/layout/box.rs @@ -8,7 +8,6 @@ use css::node_style::StyledNode; use layout::context::LayoutContext; use layout::display_list_builder::{DisplayListBuilder, ExtraDisplayListData, ToGfxColor}; use layout::float_context::{ClearType, ClearLeft, ClearRight, ClearBoth}; -use layout::flow::FlowContext; use layout::model::{BoxModel, MaybeAuto}; use layout::text; @@ -153,9 +152,6 @@ pub struct RenderBoxBase { /// The DOM node that this `RenderBox` originates from. node: AbstractNode, - /// The reference to the containing flow context which this box participates in. - ctx: FlowContext, - /// The position of this box relative to its owning flow. position: Rect, @@ -170,11 +166,10 @@ pub struct RenderBoxBase { impl RenderBoxBase { /// Constructs a new `RenderBoxBase` instance. - pub fn new(node: AbstractNode, flow_context: FlowContext, id: int) + pub fn new(node: AbstractNode, id: int) -> RenderBoxBase { RenderBoxBase { node: node, - ctx: flow_context, position: Au::zero_rect(), model: Zero::zero(), id: id, diff --git a/src/components/main/layout/box_builder.rs b/src/components/main/layout/box_builder.rs index 3e7cd563206..3e3f5a6c25b 100644 --- a/src/components/main/layout/box_builder.rs +++ b/src/components/main/layout/box_builder.rs @@ -4,7 +4,6 @@ //! Creates CSS boxes from a DOM tree. -use layout::aux::LayoutAuxMethods; use layout::block::BlockFlowData; use layout::float::FloatFlowData; use layout::box::{GenericRenderBoxClass, ImageRenderBox, ImageRenderBoxClass, RenderBox}; @@ -31,9 +30,9 @@ use script::dom::node::{AbstractNode, CommentNodeTypeId, DoctypeNodeTypeId}; use script::dom::node::{ElementNodeTypeId, LayoutView, TextNodeTypeId}; use servo_util::range::Range; use servo_util::tree::{TreeNodeRef, TreeNode}; +use std::cell::Cell; pub struct LayoutTreeBuilder { - root_flow: Option, next_cid: int, next_bid: int, } @@ -41,7 +40,6 @@ pub struct LayoutTreeBuilder { impl LayoutTreeBuilder { pub fn new() -> LayoutTreeBuilder { LayoutTreeBuilder { - root_flow: None, next_cid: -1, next_bid: -1, } @@ -50,9 +48,9 @@ impl LayoutTreeBuilder { // helper object for building the initial box list and making the // mapping between DOM nodes and boxes. -struct BoxGenerator { - flow: FlowContext, - range_stack: ~[uint], +struct BoxGenerator<'self> { + flow: &'self mut FlowContext, + range_stack: @mut ~[uint], } enum InlineSpacerSide { @@ -94,25 +92,32 @@ fn simulate_UA_display_rules(node: AbstractNode) -> CSSDisplay { } } -impl BoxGenerator { +impl<'self> BoxGenerator<'self> { /* Debug ids only */ - fn new(flow: FlowContext) -> BoxGenerator { + fn new(flow: &'self mut FlowContext) -> BoxGenerator<'self> { debug!("Creating box generator for flow: %s", flow.debug_str()); BoxGenerator { flow: flow, - range_stack: ~[] + range_stack: @mut ~[] } } + fn with_clone (&mut self, cb: &fn(BoxGenerator<'self>) -> R) -> R { + let gen = BoxGenerator { + flow: &mut *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(&self, _: AbstractNode) -> bool { + fn inline_spacers_needed_for_node(_: AbstractNode) -> bool { return false; } // TODO: implement this, generating spacer - fn make_inline_spacer_for_node_side(&self, - _: &LayoutContext, + fn make_inline_spacer_for_node_side(_: &LayoutContext, _: AbstractNode, _: InlineSpacerSide) -> Option { @@ -132,28 +137,29 @@ impl BoxGenerator { debug!("BoxGenerator[f%d]: point a", self.flow.id()); + let range_stack = &mut self.range_stack; // depending on flow, make a box for this node. - match self.flow { - InlineFlow(inline) => { + match *self.flow { + InlineFlow(ref mut inline) => { let node_range_start = inline.boxes.len(); - self.range_stack.push(node_range_start); + range_stack.push(node_range_start); // if a leaf, make a box. if node.is_leaf() { - let new_box = self.make_box(ctx, box_type, node, self.flow, builder); + let new_box = BoxGenerator::make_box(ctx, box_type, node, builder); inline.boxes.push(new_box); - } else if self.inline_spacers_needed_for_node(node) { + } else if BoxGenerator::inline_spacers_needed_for_node(node) { // else, maybe make a spacer for "left" margin, border, padding - let inline_spacer = self.make_inline_spacer_for_node_side(ctx, node, LogicalBefore); + 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. }, - BlockFlow(block) => { + BlockFlow(ref mut block) => { debug!("BoxGenerator[f%d]: point b", block.common.id); - let new_box = self.make_box(ctx, box_type, node, self.flow, builder); + 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.common.id, @@ -162,46 +168,20 @@ impl BoxGenerator { assert!(block.box.is_none()); block.box = Some(new_box); - }, - FloatFlow(float) => { + } + FloatFlow(ref mut float) => { debug!("BoxGenerator[f%d]: point b", float.common.id); - let mut parent_flow = None; + let new_box = BoxGenerator::make_box(ctx, box_type, node, builder); - do self.flow.with_base |base| { - parent_flow = base.parent_node(); - } + debug!("BoxGenerator[f%d]: attaching box[b%d] to float flow (node: %s)", + float.common.id, + new_box.id(), + node.debug_str()); - match parent_flow { - None => fail!("Float flow as root node"), - Some(BlockFlow(*)) | - Some(FloatFlow(*)) => { - let new_box = self.make_box(ctx, box_type, node, self.flow, builder); - - debug!("BoxGenerator[f%d]: attaching box[b%d] to float flow (node: %s)", - float.common.id, - new_box.id(), - node.debug_str()); - - assert!(float.box.is_none() && float.index.is_none()); - float.box = Some(new_box); - } - Some(InlineFlow(inline)) => { - let new_box = self.make_box(ctx, box_type, node, self.flow, builder); - - debug!("BoxGenerator[f%d]: attaching box[b%d] to float flow (node: %s)", - float.common.id, - new_box.id(), - node.debug_str()); - - - assert!(float.box.is_none() && float.index.is_none()); - inline.boxes.push(new_box); - float.index = Some(inline.boxes.len() - 1); - } - _ => warn!("push_node() not implemented for flow f%d", self.flow.id()) - } - }, + assert!(float.box.is_none() && float.index.is_none()); + float.box = Some(new_box); + } _ => warn!("push_node() not implemented for flow f%d", self.flow.id()), } } @@ -211,16 +191,16 @@ impl BoxGenerator { node: AbstractNode) { debug!("BoxGenerator[f%d]: popping node: %s", self.flow.id(), node.debug_str()); - match self.flow { - InlineFlow(inline) => { + match *self.flow { + InlineFlow(ref mut inline) => { let inline = &mut *inline; - if self.inline_spacers_needed_for_node(node) { + if BoxGenerator::inline_spacers_needed_for_node(node) { // 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); + let result = BoxGenerator::make_inline_spacer_for_node_side(ctx, node, LogicalAfter); for spacer in result.iter() { - let boxes = &mut self.flow.inline().boxes; + let boxes = &mut inline.boxes; boxes.push(*spacer); } } @@ -235,31 +215,29 @@ impl BoxGenerator { inline.elems.add_mapping(node, &node_range); }, BlockFlow(*) => assert!(self.range_stack.len() == 0), + FloatFlow(*) => assert!(self.range_stack.len() == 0), _ => warn!("pop_node() not implemented for flow %?", self.flow.id()), } } /// Disambiguate between different methods here instead of inlining, since each case has very /// different complexity. - fn make_box(&self, - layout_ctx: &LayoutContext, + fn make_box(layout_ctx: &LayoutContext, ty: RenderBoxType, node: AbstractNode, - flow_context: FlowContext, builder: &mut LayoutTreeBuilder) -> RenderBox { - let base = RenderBoxBase::new(node, flow_context, builder.next_box_id()); + let base = RenderBoxBase::new(node, builder.next_box_id()); let result = match ty { RenderBox_Generic => GenericRenderBoxClass(@mut base), RenderBox_Text => UnscannedTextRenderBoxClass(@mut UnscannedTextRenderBox::new(base)), - RenderBox_Image => self.make_image_box(layout_ctx, node, base), + RenderBox_Image => BoxGenerator::make_image_box(layout_ctx, node, base), }; debug!("BoxGenerator: created box: %s", result.debug_str()); result } - fn make_image_box(&self, - layout_ctx: &LayoutContext, + fn make_image_box(layout_ctx: &LayoutContext, node: AbstractNode, base: RenderBoxBase) -> RenderBox { @@ -297,6 +275,13 @@ impl BoxGenerator { } +enum BoxGenResult<'self> { + NoGenerator, + ParentGenerator, + SiblingGenerator, + NewGenerator(BoxGenerator<'self>), + Mixed(BoxGenerator<'self>, ~BoxGenResult<'self>), +} impl LayoutTreeBuilder { /* Debug-only ids */ @@ -305,59 +290,74 @@ impl LayoutTreeBuilder { /// Creates necessary box(es) and flow context(s) for the current DOM node, /// and recurses on its children. - pub fn construct_recursively(&mut self, + pub fn construct_recursively<'a>(&mut self, layout_ctx: &LayoutContext, cur_node: AbstractNode, - parent_generator: @mut BoxGenerator, - prev_sibling_generator: Option<@mut BoxGenerator>) - -> Option<@mut BoxGenerator> { + mut parent_generator: BoxGenerator<'a>, + mut prev_sibling_generator: Option>) + -> Option> { debug!("Considering node: %s", cur_node.debug_str()); + let box_gen_result = { + let sibling_gen_ref = match prev_sibling_generator { + Some(ref mut generator) => Some(generator), + None => None, + }; + self.box_generator_for_node(cur_node, &mut parent_generator, sibling_gen_ref) + }; + 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 self.box_generator_for_node(cur_node, parent_generator, prev_sibling_generator) { - - Some((gen, n_gen)) => (gen, n_gen), - None => { return prev_sibling_generator; } + match box_gen_result { + NoGenerator => return prev_sibling_generator, + ParentGenerator => (parent_generator, None), + SiblingGenerator => (prev_sibling_generator.take_unwrap(), None), + NewGenerator(gen) => (gen, None), + Mixed(gen, next_gen) => (gen, Some(match *next_gen { + ParentGenerator => parent_generator, + 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 mut prev_generator: Option<@mut BoxGenerator> = None; + let prev_gen_cell = Cell::new(None); for child_node in cur_node.children() { - prev_generator = self.construct_recursively(layout_ctx, child_node, this_generator, prev_generator); + do this_generator.with_clone |clone| { + let mut prev_generator = prev_gen_cell.take(); + prev_generator = self.construct_recursively(layout_ctx, child_node, clone, prev_generator); + prev_gen_cell.put_back(prev_generator); + } } this_generator.pop_node(layout_ctx, cur_node); - self.simplify_children_of_flow(layout_ctx, &mut this_generator.flow); + self.simplify_children_of_flow(layout_ctx, this_generator.flow); - // store reference to the flow context which contains any - // boxes that correspond to child_flow.node. These boxes may - // eventually be elided or split, but the mapping between - // nodes and FlowContexts should not change during layout. - let flow: &FlowContext = &this_generator.flow; - for child_flow in flow.children() { - do child_flow.with_base |child_node| { - let dom_node = child_node.node; - assert!(dom_node.has_layout_data()); - dom_node.layout_data().flow = Some(child_flow); - } + match next_generator { + Some(n_gen) => Some(n_gen), + None => Some(this_generator), } - Some(next_generator) } - pub fn box_generator_for_node(&mut self, + + + pub fn box_generator_for_node<'a>(&mut self, node: AbstractNode, - parent_generator: @mut BoxGenerator, - sibling_generator: Option<@mut BoxGenerator>) - -> Option<(@mut BoxGenerator, @mut BoxGenerator)> { + parent_generator: &mut BoxGenerator<'a>, + mut sibling_generator: Option<&mut BoxGenerator<'a>>) + -> BoxGenResult<'a> { let display = if node.is_element() { match node.style().display(node.is_root()) { - CSSDisplayNone => return None, // tree ends here if 'display: none' + CSSDisplayNone => return NoGenerator, // tree ends here if 'display: none' // TODO(eatkinson) these are hacks so that the code doesn't crash // when unsupported display values are used. They should be deleted // as they are implemented. @@ -368,8 +368,8 @@ impl LayoutTreeBuilder { CSSDisplayTableHeaderGroup => CSSDisplayBlock, CSSDisplayTableFooterGroup => CSSDisplayBlock, CSSDisplayTableRow => CSSDisplayBlock, - CSSDisplayTableColumnGroup => return None, - CSSDisplayTableColumn => return None, + CSSDisplayTableColumnGroup => return NoGenerator, + CSSDisplayTableColumn => return NoGenerator, CSSDisplayTableCell => CSSDisplayBlock, CSSDisplayTableCaption => CSSDisplayBlock, v => v @@ -379,11 +379,11 @@ impl LayoutTreeBuilder { ElementNodeTypeId(_) => CSSDisplayInline, TextNodeTypeId => CSSDisplayInline, - DoctypeNodeTypeId | CommentNodeTypeId => return None, + DoctypeNodeTypeId | CommentNodeTypeId => return NoGenerator, } }; - let sibling_flow: Option = sibling_generator.map(|gen| gen.flow); + let sibling_flow: Option<&mut FlowContext> = sibling_generator.map_mut(|gen| &mut *gen.flow); // TODO(eatkinson): use the value of the float property to // determine whether to float left or right. @@ -398,105 +398,91 @@ impl LayoutTreeBuilder { }; - let new_generator = match (display, parent_generator.flow, sibling_flow) { + let new_generator = match (display, &mut parent_generator.flow, sibling_flow) { // Floats - (CSSDisplayBlock, BlockFlow(_), _) | - (CSSDisplayBlock, FloatFlow(_), _) if !is_float.is_none() => { + (CSSDisplayBlock, & &BlockFlow(_), _) | + (CSSDisplayBlock, & &FloatFlow(_), _) if !is_float.is_none() => { self.create_child_generator(node, parent_generator, Flow_Float(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. - (CSSDisplayBlock, _, Some(InlineFlow(_))) if !is_float.is_none() => { + (CSSDisplayBlock, _, Some(&InlineFlow(_))) if !is_float.is_none() => { let float_generator = self.create_child_generator(node, - sibling_generator.unwrap(), + sibling_generator.unwrap(), Flow_Float(is_float.unwrap())); - return Some((float_generator, sibling_generator.unwrap())); + return Mixed(float_generator, ~SiblingGenerator); } // This is a catch-all case for when: // a) sibling_flow is None // b) sibling_flow is a BlockFlow - (CSSDisplayBlock, InlineFlow(_), _) if !is_float.is_none() => { + (CSSDisplayBlock, & &InlineFlow(_), _) if !is_float.is_none() => { self.create_child_generator(node, parent_generator, Flow_Float(is_float.unwrap())) } - (CSSDisplayBlock, BlockFlow(info), _) => match (info.is_root, node.parent_node()) { + (CSSDisplayBlock, & &BlockFlow(ref info), _) => match (info.is_root, node.parent_node().is_some()) { // If this is the root node, then use the root flow's // context. Otherwise, make a child block context. - (true, Some(_)) => { self.create_child_generator(node, parent_generator, Flow_Block) } - (true, None) => { parent_generator } - (false, _) => { + (true, true) => self.create_child_generator(node, parent_generator, Flow_Block), + (true, false) => { return ParentGenerator } + (false, _) => { self.create_child_generator(node, parent_generator, Flow_Block) } }, - (CSSDisplayBlock, FloatFlow(*), _) => { + (CSSDisplayBlock, & &FloatFlow(*), _) => { self.create_child_generator(node, parent_generator, Flow_Block) } // Inlines that are children of inlines are part of the same flow - (CSSDisplayInline, InlineFlow(*), _) => parent_generator, - (CSSDisplayInlineBlock, InlineFlow(*), _) => parent_generator, + (CSSDisplayInline, & &InlineFlow(*), _) => return ParentGenerator, + (CSSDisplayInlineBlock, & &InlineFlow(*), _) => return ParentGenerator, // Inlines that are children of blocks create new flows if their // previous sibling was a block. - (CSSDisplayInline, BlockFlow(*), Some(BlockFlow(*))) | - (CSSDisplayInlineBlock, BlockFlow(*), Some(BlockFlow(*))) => { + (CSSDisplayInline, & &BlockFlow(*), Some(&BlockFlow(*))) | + (CSSDisplayInlineBlock, & &BlockFlow(*), Some(&BlockFlow(*))) => { self.create_child_generator(node, parent_generator, Flow_Inline) } // The first two cases should only be hit when a FloatFlow // is the first child of a BlockFlow. Other times, we will - (CSSDisplayInline, _, Some(FloatFlow(*))) | - (CSSDisplayInlineBlock, _, Some(FloatFlow(*))) | - (CSSDisplayInline, FloatFlow(*), _) | - (CSSDisplayInlineBlock, FloatFlow(*), _) => { + (CSSDisplayInline, _, Some(&FloatFlow(*))) | + (CSSDisplayInlineBlock, _, Some(&FloatFlow(*))) | + (CSSDisplayInline, & &FloatFlow(*), _) | + (CSSDisplayInlineBlock, & &FloatFlow(*), _) => { self.create_child_generator(node, parent_generator, Flow_Inline) } // Inlines whose previous sibling was not a block try to use their // sibling's flow context. - (CSSDisplayInline, BlockFlow(*), _) | - (CSSDisplayInlineBlock, BlockFlow(*), _) => { - self.create_child_generator_if_needed(node, - parent_generator, - sibling_generator, - Flow_Inline) + (CSSDisplayInline, & &BlockFlow(*), _) | + (CSSDisplayInlineBlock, & &BlockFlow(*), _) => { + return match sibling_generator { + None => NewGenerator(self.create_child_generator(node, + parent_generator, + Flow_Inline)), + Some(*) => SiblingGenerator + } } // TODO(eatkinson): blocks that are children of inlines need // to split their parent flows. - _ => parent_generator + _ => return ParentGenerator }; - // Usually, the node we add boxes to will be prev_sibling on the - // next call to this function. - Some((new_generator, new_generator)) + NewGenerator(new_generator) } - pub fn create_child_generator(&mut self, + pub fn create_child_generator<'a>(&mut self, node: AbstractNode, - parent_generator: @mut BoxGenerator, + parent_generator: &mut BoxGenerator<'a>, ty: FlowContextType) - -> @mut BoxGenerator { + -> BoxGenerator<'a> { let new_flow = self.make_flow(ty, node); - parent_generator.flow.add_child(new_flow); - - @mut BoxGenerator::new(new_flow) - } - - pub fn create_child_generator_if_needed(&mut self, - node: AbstractNode, - parent_generator: @mut BoxGenerator, - maybe_generator: Option<@mut BoxGenerator>, - ty: FlowContextType) - -> @mut BoxGenerator { - - match maybe_generator { - None => self.create_child_generator(node, parent_generator, ty), - Some(gen) => gen - } + parent_generator.flow.add_new_child(new_flow); + BoxGenerator::new(parent_generator.flow.last_child().unwrap()) } /// Fix up any irregularities such as: @@ -513,74 +499,71 @@ impl LayoutTreeBuilder { let mut found_child_inline = false; let mut found_child_block = false; - let flow = *parent_flow; - for child_ctx in flow.children() { + for child_ctx in parent_flow.child_iter() { match child_ctx { - InlineFlow(*) | InlineBlockFlow(*) => found_child_inline = true, - BlockFlow(*) => found_child_block = true, + &InlineFlow(*) | &InlineBlockFlow(*) => found_child_inline = true, + &BlockFlow(*) => found_child_block = true, _ => {} } } if found_child_block && found_child_inline { - self.fixup_split_inline(*parent_flow) + self.fixup_split_inline(parent_flow) } }, BlockFlow(*) | FloatFlow(*) => { - // FIXME: this will create refcounted cycles between the removed flow and any - // of its RenderBox or FlowContext children, and possibly keep alive other junk - // check first/last child for whitespace-ness - let first_child = do parent_flow.with_base |parent_node| { - parent_node.first_child - }; - for &first_flow in first_child.iter() { - if first_flow.starts_inline_flow() { - // FIXME: workaround for rust#6393 - let mut do_remove = false; - { - let boxes = &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.id(), - parent_flow.id()); - do_remove = true; + let mut do_remove = false; + let p_id = 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 boxes = &first_flow.imm_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.id(), + p_id); + do_remove = true; + } } } - if (do_remove) { - (*parent_flow).remove_child(first_flow); - } } } + if (do_remove) { + parent_flow.remove_first(); + } - let last_child = do parent_flow.with_base |parent_node| { - parent_node.last_child - }; - for &last_flow in last_child.iter() { - if last_flow.starts_inline_flow() { - // FIXME: workaround for rust#6393 - let mut do_remove = false; - { - let boxes = &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.id(), - parent_flow.id()); - do_remove = true; + + do_remove = false; + let p_id = 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 boxes = &last_flow.imm_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.id(), + p_id); + do_remove = true; + } } } - if (do_remove) { - (*parent_flow).remove_child(last_flow); - } } } + 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 (*parent_flow).children() { - match child_flow { + for child_flow in (*parent_flow).child_iter() { + match *child_flow { InlineFlow(*) | InlineBlockFlow(*) => { let mut scanner = TextRunScanner::new(); scanner.scan_for_runs(ctx, child_flow); @@ -593,7 +576,7 @@ impl LayoutTreeBuilder { } } - pub fn fixup_split_inline(&self, _: FlowContext) { + pub fn fixup_split_inline(&self, _: &mut FlowContext) { // TODO: finish me. fail!(~"TODO: handle case where an inline is split by a block") } @@ -601,11 +584,14 @@ impl LayoutTreeBuilder { /// 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) -> Result { - let new_flow = self.make_flow(Flow_Root, root); - let new_generator = @mut BoxGenerator::new(new_flow); + debug!("Constructing flow tree for DOM: "); + root.dump(); - self.root_flow = Some(new_flow); - self.construct_recursively(layout_ctx, root, new_generator, None); + let mut new_flow = self.make_flow(Flow_Root, root); + { + let new_generator = BoxGenerator::new(&mut new_flow); + self.construct_recursively(layout_ctx, root, new_generator, None); + } return Ok(new_flow) } @@ -613,13 +599,13 @@ impl LayoutTreeBuilder { pub 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(f_type) => FloatFlow(@mut FloatFlowData::new(info, f_type)), - Flow_InlineBlock => InlineBlockFlow(@mut info), - Flow_Inline => InlineFlow(@mut InlineFlowData::new(info)), - Flow_Root => BlockFlow(@mut BlockFlowData::new_root(info)), - Flow_Table => TableFlow(@mut info), + Flow_Absolute => AbsoluteFlow(~info), + Flow_Block => BlockFlow(~BlockFlowData::new(info)), + Flow_Float(f_type) => FloatFlow(~FloatFlowData::new(info, f_type)), + Flow_InlineBlock => InlineBlockFlow(~info), + Flow_Inline => InlineFlow(~InlineFlowData::new(info)), + Flow_Root => BlockFlow(~BlockFlowData::new_root(info)), + Flow_Table => TableFlow(~info), }; debug!("LayoutTreeBuilder: created flow: %s", result.debug_str()); result diff --git a/src/components/main/layout/display_list_builder.rs b/src/components/main/layout/display_list_builder.rs index c992a32fc69..cc89f7cb61b 100644 --- a/src/components/main/layout/display_list_builder.rs +++ b/src/components/main/layout/display_list_builder.rs @@ -6,20 +6,29 @@ use layout::box::RenderBox; use layout::context::LayoutContext; +use std::cast::transmute; +use script::dom::node::AbstractNode; use gfx; use newcss; -/// Extra display list data is either nothing (if the display list is to be rendered) or the -/// originating render box (if the display list is generated for hit testing). +/// Display list data is usually an AbstractNode with view () to indicate +/// that nodes in this view shoud not really be touched. The idea is to +/// store the nodes in the display list and have layout transmute them. pub trait ExtraDisplayListData { fn new(box: RenderBox) -> Self; } -/// The type representing the lack of extra display list data. This is used when sending display -/// list data off to be rendered. pub type Nothing = (); +impl ExtraDisplayListData for AbstractNode<()> { + fn new (box: RenderBox) -> AbstractNode<()> { + unsafe { + transmute(box.node()) + } + } +} + impl ExtraDisplayListData for Nothing { fn new(_: RenderBox) -> Nothing { () diff --git a/src/components/main/layout/float.rs b/src/components/main/layout/float.rs index 8f4ffb8824b..d56f8a8269b 100644 --- a/src/components/main/layout/float.rs +++ b/src/components/main/layout/float.rs @@ -5,7 +5,7 @@ use layout::box::{RenderBox}; use layout::context::LayoutContext; use layout::display_list_builder::{DisplayListBuilder, ExtraDisplayListData}; -use layout::flow::{FloatFlow, FlowData}; +use layout::flow::{FlowData}; use layout::model::{MaybeAuto}; use layout::float_context::{FloatContext, PlacementInfo, FloatType}; @@ -15,7 +15,6 @@ use geom::rect::Rect; use gfx::display_list::DisplayList; use gfx::geometry::Au; use gfx::geometry; -use servo_util::tree::TreeNodeRef; pub struct FloatFlowData { /// Data common to all flows. @@ -54,7 +53,6 @@ impl FloatFlowData { } pub fn teardown(&mut self) { - self.common.teardown(); for box in self.box.iter() { box.teardown(); } @@ -64,12 +62,12 @@ impl FloatFlowData { } impl FloatFlowData { - pub fn bubble_widths_float(@mut self, ctx: &LayoutContext) { + pub fn bubble_widths_float(&mut self, ctx: &LayoutContext) { let mut min_width = Au(0); let mut pref_width = Au(0); let mut num_floats = 0; - for child_ctx in FloatFlow(self).children() { + for child_ctx in self.common.child_iter() { //assert!(child_ctx.starts_block_flow() || child_ctx.starts_inline_flow()); do child_ctx.with_mut_base |child_node| { @@ -97,7 +95,7 @@ impl FloatFlowData { self.common.pref_width = pref_width; } - pub fn assign_widths_float(@mut self) { + pub fn assign_widths_float(&mut self) { debug!("assign_widths_float: assigning width for flow %?", self.common.id); // position.size.width is set by parent even though we don't know // position.origin yet. @@ -162,7 +160,7 @@ impl FloatFlowData { self.common.position.size.width = remaining_width; let has_inorder_children = self.common.num_floats > 0; - for kid in FloatFlow(self).children() { + for kid in self.common.child_iter() { //assert!(kid.starts_block_flow() || kid.starts_inline_flow()); do kid.with_mut_base |child_node| { @@ -177,7 +175,7 @@ impl FloatFlowData { } } - pub fn assign_height_inorder_float(@mut self) { + pub fn assign_height_inorder_float(&mut self) { debug!("assign_height_inorder_float: assigning height for float %?", self.common.id); // assign_height_float was already called by the traversal function // so this is well-defined @@ -222,12 +220,12 @@ impl FloatFlowData { self.rel_pos = self.common.floats_out.last_float_pos(); } - pub fn assign_height_float(@mut self, ctx: &mut LayoutContext) { + pub fn assign_height_float(&mut self, ctx: &mut LayoutContext) { debug!("assign_height_float: assigning height for float %?", self.common.id); let has_inorder_children = self.common.num_floats > 0; if has_inorder_children { let mut float_ctx = FloatContext::new(self.floated_children); - for kid in FloatFlow(self).children() { + for kid in self.common.child_iter() { do kid.with_mut_base |child_node| { child_node.floats_in = float_ctx.clone(); } @@ -248,7 +246,7 @@ impl FloatFlowData { } } - for kid in FloatFlow(self).children() { + for kid in self.common.child_iter() { do kid.with_mut_base |child_node| { child_node.position.origin.y = cur_y; cur_y = cur_y + child_node.position.size.height; @@ -289,7 +287,7 @@ impl FloatFlowData { } } - pub fn build_display_list_float(@mut self, + pub fn build_display_list_float(&mut self, builder: &DisplayListBuilder, dirty: &Rect, list: &Cell>) @@ -301,7 +299,7 @@ impl FloatFlowData { } let abs_rect = Rect(self.common.abs_position, self.common.position.size); if !abs_rect.intersects(dirty) { - return false; + return true; } @@ -315,14 +313,13 @@ impl FloatFlowData { // TODO: handle any out-of-flow elements // go deeper into the flow tree - let flow = FloatFlow(self); - for child in flow.children() { + for child in self.common.child_iter() { do child.with_mut_base |base| { base.abs_position = offset + base.position.origin; } } - true + false } } diff --git a/src/components/main/layout/float_context.rs b/src/components/main/layout/float_context.rs index 3c4bf3773ad..2a49921bb18 100644 --- a/src/components/main/layout/float_context.rs +++ b/src/components/main/layout/float_context.rs @@ -48,12 +48,12 @@ pub struct PlacementInfo{ /// destroy the context on modification. pub enum FloatContext { Invalid, - Valid(FloatContextBase) + Valid(~FloatContextBase) } impl FloatContext { pub fn new(num_floats: uint) -> FloatContext { - Valid(FloatContextBase::new(num_floats)) + Valid(~FloatContextBase::new(num_floats)) } #[inline(always)] @@ -68,7 +68,7 @@ impl FloatContext { fn with_mut_base(&mut self, callback: &fn(&mut FloatContextBase) -> R) -> R { match *self { Invalid => fail!("Float context no longer available"), - Valid(ref mut base) => callback(base) + Valid(ref mut base) => callback(&mut **base) } } @@ -76,7 +76,7 @@ impl FloatContext { pub fn with_base(&self, callback: &fn(&FloatContextBase) -> R) -> R { match *self { Invalid => fail!("Float context no longer available"), - Valid(ref base) => callback(base) + Valid(ref base) => callback(& **base) } } diff --git a/src/components/main/layout/flow.rs b/src/components/main/layout/flow.rs index 83edbfa61ba..9228a6a9911 100644 --- a/src/components/main/layout/flow.rs +++ b/src/components/main/layout/flow.rs @@ -34,6 +34,8 @@ use layout::inline::{InlineFlowData}; use layout::float_context::{FloatContext, Invalid, FloatType}; use layout::incremental::RestyleDamage; use css::node_style::StyledNode; +use extra::dlist::{DList,MutDListIterator}; +use extra::container::Deque; use std::cell::Cell; use std::io::stderr; @@ -42,18 +44,16 @@ use geom::rect::Rect; use gfx::display_list::DisplayList; use gfx::geometry::Au; use script::dom::node::{AbstractNode, LayoutView}; -use servo_util::tree::{TreeNode, TreeNodeRef}; /// The type of the formatting context and data specific to each context, such as line box /// structures or float lists. -#[deriving(Clone)] pub enum FlowContext { - AbsoluteFlow(@mut FlowData), - BlockFlow(@mut BlockFlowData), - FloatFlow(@mut FloatFlowData), - InlineBlockFlow(@mut FlowData), - InlineFlow(@mut InlineFlowData), - TableFlow(@mut FlowData), + AbsoluteFlow(~FlowData), + BlockFlow(~BlockFlowData), + FloatFlow(~FloatFlowData), + InlineBlockFlow(~FlowData), + InlineFlow(~InlineFlowData), + TableFlow(~FlowData), } pub enum FlowContextType { @@ -67,142 +67,161 @@ pub enum FlowContextType { } impl FlowContext { - pub fn teardown(&self) { - match *self { - AbsoluteFlow(data) | - InlineBlockFlow(data) | - TableFlow(data) => data.teardown(), - BlockFlow(data) => data.teardown(), - FloatFlow(data) => data.teardown(), - InlineFlow(data) => data.teardown() - } - } - - /// Like traverse_preorder, but don't end the whole traversal if the callback - /// returns false. - // - // FIXME: Unify this with traverse_preorder_prune, which takes a separate - // 'prune' function. - pub fn partially_traverse_preorder(&self, callback: &fn(FlowContext) -> bool) { - if !callback((*self).clone()) { - return; - } - - for kid in self.children() { + pub fn each_bu_sub_inorder (&mut self, callback: &fn(&mut FlowContext) -> bool) -> bool { + for kid in self.child_iter() { // FIXME: Work around rust#2202. We should be able to pass the callback directly. - kid.partially_traverse_preorder(|a| callback(a)); - } - } - - pub fn traverse_bu_sub_inorder (&self, callback: &fn(FlowContext)) { - for kid in self.children() { - // FIXME: Work around rust#2202. We should be able to pass the callback directly. - kid.traverse_bu_sub_inorder(|a| callback(a)); + if !kid.each_bu_sub_inorder(|a| callback(a)) { + return false; + } } if !self.is_inorder() { - callback((*self).clone()) + callback(self) + } else { + true } } + + pub fn each_preorder_prune(&mut self, prune: &fn(&mut FlowContext) -> bool, + callback: &fn(&mut FlowContext) -> bool) + -> bool { + if prune(self) { + return true; + } + + if !callback(self) { + return false; + } + + for kid in self.child_iter() { + // FIXME: Work around rust#2202. We should be able to pass the callback directly. + if !kid.each_preorder_prune(|a| prune(a), |a| callback(a)) { + return false; + } + } + + true + } + + pub fn each_postorder_prune(&mut self, prune: &fn(&mut FlowContext) -> bool, + callback: &fn(&mut FlowContext) -> bool) + -> bool { + if prune(self) { + return true; + } + + for kid in self.child_iter() { + // FIXME: Work around rust#2202. We should be able to pass the callback directly. + if !kid.each_postorder_prune(|a| prune(a), |a| callback(a)) { + return false; + } + } + + callback(self) + } + + pub fn each_preorder(&mut self, callback: &fn(&mut FlowContext) -> bool) -> bool { + self.each_preorder_prune(|_| false, callback) + } + + pub fn each_postorder(&mut self, callback: &fn(&mut FlowContext) -> bool) -> bool { + self.each_postorder_prune(|_| false, callback) + } } -impl FlowData { - pub fn teardown(&mut self) { - // Under the assumption that all flows exist in a tree, - // we must restrict ourselves to finalizing flows that - // are descendents and subsequent siblings to ourselves, - // or we risk dynamic borrow failures. - self.parent = None; - - for flow in self.first_child.iter() { - flow.teardown(); +impl<'self> FlowContext { + pub fn leaf(&self) -> bool { + do self.with_base |base| { + base.children.len() == 0 } - self.first_child = None; - - self.last_child = None; - - for flow in self.next_sibling.iter() { - flow.teardown(); - } - self.next_sibling = None; - - self.prev_sibling = None; } + + pub fn add_new_child(&mut self, new_child: FlowContext) { + let cell = Cell::new(new_child); + do self.with_mut_base |base| { + base.children.push_back(cell.take()); + } + } + + pub fn with_first_child(&mut self, cb: &fn(Option<&mut FlowContext>) -> R) -> R { + do self.with_mut_base |base| { + cb(base.children.front_mut()) + } + } + + pub fn with_last_child(&mut self, cb: &fn(Option<&mut FlowContext>) -> R) -> R { + do self.with_mut_base |base| { + cb(base.children.back_mut()) + } + } + + pub fn last_child(&'self mut self) -> Option<&'self mut FlowContext> { + self.mut_base().children.back_mut() + } + + pub fn remove_first(&mut self) { + do self.with_mut_base |base| { + base.children.pop_front(); + } + } + + pub fn remove_last(&mut self) { + do self.with_mut_base |base| { + base.children.pop_back(); + } + } + + pub fn child_iter<'a>(&'a mut self) -> MutDListIterator<'a, FlowContext> { + self.mut_base().children.mut_iter() + } + } -impl TreeNodeRef for FlowContext { - fn with_base(&self, callback: &fn(&FlowData) -> R) -> R { +impl<'self> FlowContext { + pub fn with_base(&self, callback: &fn(&FlowData) -> R) -> R { match *self { - AbsoluteFlow(info) => callback(info), - BlockFlow(info) => { + AbsoluteFlow(ref info) => callback(&**info), + BlockFlow(ref info) => { callback(&info.common) } - FloatFlow(info) => callback(&info.common), - InlineBlockFlow(info) => callback(info), - InlineFlow(info) => { + FloatFlow(ref info) => callback(&info.common), + InlineBlockFlow(ref info) => callback(&**info), + InlineFlow(ref info) => { callback(&info.common) } - TableFlow(info) => callback(info) + TableFlow(ref info) => callback(&**info) } } - fn with_mut_base(&self, callback: &fn(&mut FlowData) -> R) -> R { + pub fn with_mut_base(&mut self, callback: &fn(&mut FlowData) -> R) -> R { match *self { - AbsoluteFlow(info) => callback(info), - BlockFlow(info) => { + AbsoluteFlow(ref mut info) => callback(&mut **info), + BlockFlow(ref mut info) => { callback(&mut info.common) } - FloatFlow(info) => callback(&mut info.common), - InlineBlockFlow(info) => callback(info), - InlineFlow(info) => { + FloatFlow(ref mut info) => callback(&mut info.common), + InlineBlockFlow(ref mut info) => callback(&mut **info), + InlineFlow(ref mut info) => { callback(&mut info.common) } - TableFlow(info) => callback(info), + TableFlow(ref mut info) => callback(&mut **info), } } - - fn parent_node(node: &FlowData) -> Option { - node.parent - } - - fn first_child(node: &FlowData) -> Option { - node.first_child - } - - fn last_child(node: &FlowData) -> Option { - node.last_child - } - - fn prev_sibling(node: &FlowData) -> Option { - node.prev_sibling - } - - fn next_sibling(node: &FlowData) -> Option { - node.next_sibling - } - - fn set_parent_node(node: &mut FlowData, new_parent_node: Option) { - node.parent = new_parent_node - } - - fn set_first_child(node: &mut FlowData, new_first_child: Option) { - node.first_child = new_first_child - } - - fn set_last_child(node: &mut FlowData, new_last_child: Option) { - node.last_child = new_last_child - } - - fn set_prev_sibling(node: &mut FlowData, new_prev_sibling: Option) { - node.prev_sibling = new_prev_sibling - } - - fn set_next_sibling(node: &mut FlowData, new_next_sibling: Option) { - node.next_sibling = new_next_sibling + pub fn mut_base(&'self mut self) -> &'self mut FlowData { + match *self { + AbsoluteFlow(ref mut info) => &mut(**info), + BlockFlow(ref mut info) => { + &mut info.common + } + FloatFlow(ref mut info) => &mut info.common, + InlineBlockFlow(ref mut info) => &mut(**info), + InlineFlow(ref mut info) => { + &mut info.common + } + TableFlow(ref mut info) => &mut(**info), + } } } -impl TreeNode for FlowData { } - /// Data common to all flows. /// /// FIXME: We need a naming convention for pseudo-inheritance like this. How about @@ -211,11 +230,7 @@ pub struct FlowData { node: AbstractNode, restyle_damage: RestyleDamage, - parent: Option, - first_child: Option, - last_child: Option, - prev_sibling: Option, - next_sibling: Option, + children: DList, /* TODO (Issue #87): debug only */ id: int, @@ -237,7 +252,6 @@ pub struct BoxIterator { priv boxes: ~[RenderBox], priv index: uint, } - impl Iterator for BoxIterator { fn next(&mut self) -> Option { if self.index >= self.boxes.len() { @@ -249,18 +263,13 @@ impl Iterator for BoxIterator { } } } - impl FlowData { pub fn new(id: int, node: AbstractNode) -> FlowData { FlowData { node: node, restyle_damage: node.restyle_damage(), - parent: None, - first_child: None, - last_child: None, - prev_sibling: None, - next_sibling: None, + children: DList::new(), id: id, @@ -274,6 +283,11 @@ impl FlowData { is_inorder: false } } + + pub fn child_iter<'a>(&'a mut self) -> MutDListIterator<'a, FlowContext> { + self.children.mut_iter() + } + } impl<'self> FlowContext { @@ -302,75 +316,88 @@ impl<'self> FlowContext { } } - - pub fn inline(&self) -> @mut InlineFlowData { + pub fn inline(&'self mut self) -> &'self mut InlineFlowData { match *self { - InlineFlow(info) => info, + InlineFlow(ref mut info) => &mut (**info), _ => fail!(fmt!("Tried to access inline data of non-inline: f%d", self.id())) } } - pub fn block(&self) -> @mut BlockFlowData { + pub fn imm_inline(&'self self) -> &'self InlineFlowData { match *self { - BlockFlow(info) => info, + InlineFlow(ref 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 info) => &mut (**info), _ => fail!(fmt!("Tried to access block data of non-block: f%d", self.id())) } } - pub fn root(&self) -> @mut BlockFlowData { + pub fn root(&'self mut self) -> &'self mut BlockFlowData { match *self { - BlockFlow(info) if info.is_root => info, + BlockFlow(ref mut info) if info.is_root => &mut (**info), _ => fail!(fmt!("Tried to access root block data of non-root: f%d", self.id())) } } - pub fn bubble_widths(&self, ctx: &mut LayoutContext) { + pub fn bubble_widths(&mut self, ctx: &mut LayoutContext) { + + debug!("FlowContext: bubbling widths for f%?", self.id()); match *self { - BlockFlow(info) => info.bubble_widths_block(ctx), - InlineFlow(info) => info.bubble_widths_inline(ctx), - FloatFlow(info) => info.bubble_widths_float(ctx), + BlockFlow(ref mut info) => info.bubble_widths_block(ctx), + InlineFlow(ref mut info) => info.bubble_widths_inline(ctx), + FloatFlow(ref mut info) => info.bubble_widths_float(ctx), _ => fail!(fmt!("Tried to bubble_widths of flow: f%d", self.id())) } } - pub fn assign_widths(&self, ctx: &mut LayoutContext) { + pub fn assign_widths(&mut self, ctx: &mut LayoutContext) { + + debug!("FlowContext: assigning widths for f%?", self.id()); match *self { - BlockFlow(info) => info.assign_widths_block(ctx), - InlineFlow(info) => info.assign_widths_inline(ctx), - FloatFlow(info) => info.assign_widths_float(), + BlockFlow(ref mut info) => info.assign_widths_block(ctx), + InlineFlow(ref mut info) => info.assign_widths_inline(ctx), + FloatFlow(ref mut info) => info.assign_widths_float(), _ => fail!(fmt!("Tried to assign_widths of flow: f%d", self.id())) } } - pub fn assign_height(&self, ctx: &mut LayoutContext) { + pub fn assign_height(&mut self, ctx: &mut LayoutContext) { + + debug!("FlowContext: assigning height for f%?", self.id()); match *self { - BlockFlow(info) => info.assign_height_block(ctx), - InlineFlow(info) => info.assign_height_inline(ctx), - FloatFlow(info) => info.assign_height_float(ctx), + BlockFlow(ref mut info) => info.assign_height_block(ctx), + InlineFlow(ref mut info) => info.assign_height_inline(ctx), + FloatFlow(ref mut info) => info.assign_height_float(ctx), _ => fail!(fmt!("Tried to assign_height of flow: f%d", self.id())) } } - pub fn assign_height_inorder(&self, ctx: &mut LayoutContext) { + pub fn assign_height_inorder(&mut self, ctx: &mut LayoutContext) { match *self { - BlockFlow(info) => info.assign_height_inorder_block(ctx), - InlineFlow(info) => info.assign_height_inorder_inline(ctx), - FloatFlow(info) => info.assign_height_inorder_float(), + BlockFlow(ref mut info) => info.assign_height_inorder_block(ctx), + InlineFlow(ref mut info) => info.assign_height_inorder_inline(ctx), + FloatFlow(ref mut info) => info.assign_height_inorder_float(), _ => fail!(fmt!("Tried to assign_height of flow: f%d", self.id())) } } - pub fn build_display_list(&self, + pub fn build_display_list(&mut self, builder: &DisplayListBuilder, dirty: &Rect, list: &Cell>) -> bool { + debug!("FlowContext: building display list for f%?", self.id()); match *self { - BlockFlow(info) => info.build_display_list_block(builder, dirty, list), - InlineFlow(info) => info.build_display_list_inline(builder, dirty, list), - FloatFlow(info) => info.build_display_list_float(builder, dirty, list), + BlockFlow(ref mut info) => info.build_display_list_block(builder, dirty, list), + InlineFlow(ref mut info) => info.build_display_list_inline(builder, dirty, list), + FloatFlow(ref mut info) => info.build_display_list_float(builder, dirty, list), _ => { fail!("Tried to build_display_list_recurse of flow: %?", self) } @@ -388,16 +415,14 @@ impl<'self> FlowContext { // Actual methods that do not require much flow-specific logic - pub fn foldl_all_boxes(&self, seed: B, cb: &fn(a: B, b: RenderBox) -> B) -> B { + pub fn foldl_all_boxes(&mut self, seed: B, cb: &fn(a: B, b: RenderBox) -> B) -> B { match *self { - BlockFlow(block) => { - let block = &mut *block; + BlockFlow(ref mut block) => { do block.box.map_default(seed.clone()) |box| { cb(seed.clone(), *box) } } - InlineFlow(inline) => { - let inline = &mut *inline; + InlineFlow(ref mut inline) => { do inline.boxes.iter().fold(seed) |acc, box| { cb(acc.clone(), *box) } @@ -406,7 +431,7 @@ impl<'self> FlowContext { } } - pub fn foldl_boxes_for_node(&self, + pub fn foldl_boxes_for_node(&mut self, node: AbstractNode, seed: B, callback: &fn(a: B, RenderBox) -> B) @@ -420,11 +445,11 @@ impl<'self> FlowContext { } } - pub fn iter_all_boxes(&self) -> BoxIterator { + pub fn iter_all_boxes(&mut self) -> BoxIterator { BoxIterator { boxes: match *self { - BlockFlow (block) => block.box.map_default(~[], |&x| ~[x]), - InlineFlow(inline) => inline.boxes.clone(), + BlockFlow (ref mut block) => block.box.map_default(~[], |&x| ~[x]), + InlineFlow(ref mut inline) => inline.boxes.clone(), _ => fail!(fmt!("Don't know how to iterate node's RenderBoxes for %?", self)) }, index: 0, @@ -432,12 +457,12 @@ impl<'self> FlowContext { } /// Dumps the flow tree for debugging. - pub fn dump(&self) { + pub fn dump(&mut self) { self.dump_indent(0); } /// Dumps the flow tree, for debugging, with indentation. - pub fn dump_indent(&self, indent: uint) { + pub fn dump_indent(&mut self, indent: uint) { let mut s = ~"|"; for _ in range(0, indent) { s.push_str("---- "); @@ -447,27 +472,27 @@ impl<'self> FlowContext { stderr().write_line(s); // FIXME: this should have a pure/const version? - for child in self.children() { + for child in self.child_iter() { child.dump_indent(indent + 1) } } pub fn debug_str(&self) -> ~str { let repr = match *self { - InlineFlow(inline) => { + InlineFlow(ref inline) => { let mut s = inline.boxes.iter().fold(~"InlineFlow(children=", |s, box| { fmt!("%s b%d", s, box.id()) }); s.push_str(")"); s }, - BlockFlow(block) => { + BlockFlow(ref block) => { match block.box { Some(box) => fmt!("BlockFlow(box=b%d)", box.id()), None => ~"BlockFlow", } }, - FloatFlow(float) => { + FloatFlow(ref float) => { match float.box { Some(box) => fmt!("FloatFlow(box=b%d)", box.id()), None => ~"FloatFlow", diff --git a/src/components/main/layout/inline.rs b/src/components/main/layout/inline.rs index 45bb3db3ebb..08119ee308c 100644 --- a/src/components/main/layout/inline.rs +++ b/src/components/main/layout/inline.rs @@ -60,7 +60,6 @@ struct LineBox { } struct LineboxScanner { - flow: FlowContext, floats: FloatContext, new_boxes: ~[RenderBox], work_list: @mut RingBuf, @@ -70,11 +69,8 @@ struct LineboxScanner { } impl LineboxScanner { - pub fn new(inline: FlowContext, float_ctx: FloatContext) -> LineboxScanner { - assert!(inline.starts_inline_flow()); - + pub fn new(float_ctx: FloatContext) -> LineboxScanner { LineboxScanner { - flow: inline, floats: float_ctx, new_boxes: ~[], work_list: @mut RingBuf::new(), @@ -92,8 +88,8 @@ impl LineboxScanner { self.floats.clone() } - fn reset_scanner(&mut self) { - debug!("Resetting line box scanner's state for flow f%d.", self.flow.id()); + fn reset_scanner(&mut self, flow: &mut InlineFlowData) { + debug!("Resetting line box scanner's state for flow f%d.", flow.common.id); self.lines = ~[]; self.new_boxes = ~[]; self.cur_y = Au(0); @@ -106,60 +102,55 @@ impl LineboxScanner { self.pending_line.green_zone = Size2D(Au(0), Au(0)) } - pub fn scan_for_lines(&mut self) { - self.reset_scanner(); + pub fn scan_for_lines(&mut self, flow: &mut InlineFlowData) { + self.reset_scanner(flow); - { // FIXME: manually control borrow length - let inline: &InlineFlowData = self.flow.inline(); - let mut i = 0u; + let mut i = 0u; - loop { - // acquire the next box to lay out from work list or box list - let cur_box = if self.work_list.is_empty() { - if i == inline.boxes.len() { - break - } - let box = inline.boxes[i]; i += 1; - debug!("LineboxScanner: Working with box from box list: b%d", box.id()); - box - } else { - let box = self.work_list.pop_front().unwrap(); - debug!("LineboxScanner: Working with box from work list: b%d", box.id()); - box - }; - - let box_was_appended = self.try_append_to_line(cur_box); - if !box_was_appended { - debug!("LineboxScanner: Box wasn't appended, because line %u was full.", - self.lines.len()); - self.flush_current_line(); - } else { - debug!("LineboxScanner: appended a box to line %u", self.lines.len()); + loop { + // acquire the next box to lay out from work list or box list + let cur_box = if self.work_list.is_empty() { + if i == flow.boxes.len() { + break } - } + let box = flow.boxes[i]; i += 1; + debug!("LineboxScanner: Working with box from box list: b%d", box.id()); + box + } else { + let box = self.work_list.pop_front().unwrap(); + debug!("LineboxScanner: Working with box from work list: b%d", box.id()); + box + }; - if self.pending_line.range.length() > 0 { - debug!("LineboxScanner: Partially full linebox %u left at end of scanning.", - self.lines.len()); + let box_was_appended = self.try_append_to_line(cur_box, flow); + if !box_was_appended { + debug!("LineboxScanner: Box wasn't appended, because line %u was full.", + self.lines.len()); self.flush_current_line(); + } else { + debug!("LineboxScanner: appended a box to line %u", self.lines.len()); } } - { // FIXME: scope the borrow - let inline: &mut InlineFlowData = self.flow.inline(); - inline.elems.repair_for_box_changes(inline.boxes, self.new_boxes); + if self.pending_line.range.length() > 0 { + debug!("LineboxScanner: Partially full linebox %u left at end of scanning.", + self.lines.len()); + self.flush_current_line(); } - self.swap_out_results(); + + + flow.elems.repair_for_box_changes(flow.boxes, self.new_boxes); + + self.swap_out_results(flow); } - fn swap_out_results(&mut self) { + fn swap_out_results(&mut self, flow: &mut InlineFlowData) { debug!("LineboxScanner: Propagating scanned lines[n=%u] to inline flow f%d", self.lines.len(), - self.flow.id()); + flow.common.id); - let inline: &mut InlineFlowData = self.flow.inline(); - util::swap(&mut inline.boxes, &mut self.new_boxes); - util::swap(&mut inline.lines, &mut self.lines); + util::swap(&mut flow.boxes, &mut self.new_boxes); + util::swap(&mut flow.lines, &mut self.lines); } fn flush_current_line(&mut self) { @@ -222,7 +213,7 @@ impl LineboxScanner { /// Computes the position of a line that has only the provided RenderBox. /// Returns: the bounding rect of the line's green zone (whose origin coincides /// with the line's origin) and the actual width of the first box after splitting. - fn initial_line_placement (&self, first_box: RenderBox, ceiling: Au) -> (Rect, Au) { + fn initial_line_placement (&self, first_box: RenderBox, ceiling: Au, flow: &mut InlineFlowData) -> (Rect, Au) { debug!("LineboxScanner: Trying to place first box of line %?", self.lines.len()); debug!("LineboxScanner: box size: %?", first_box.position().size); let splitable = first_box.can_split(); @@ -241,7 +232,7 @@ impl LineboxScanner { width: placement_width, height: first_box.position().size.height, ceiling: ceiling, - max_width: self.flow.position().size.width, + max_width: flow.common.position.size.width, f_type: FloatLeft }; @@ -307,11 +298,11 @@ impl LineboxScanner { } /// Returns false only if we should break the line. - fn try_append_to_line(&mut self, in_box: RenderBox) -> bool { + fn try_append_to_line(&mut self, in_box: RenderBox, flow: &mut InlineFlowData) -> bool { let line_is_empty: bool = self.pending_line.range.length() == 0; if line_is_empty { - let (line_bounds, _) = self.initial_line_placement(in_box, self.cur_y); + let (line_bounds, _) = self.initial_line_placement(in_box, self.cur_y, flow); self.pending_line.bounds.origin = line_bounds.origin; self.pending_line.green_zone = line_bounds.size; } @@ -348,7 +339,7 @@ impl LineboxScanner { // First predict where the next line is going to be let this_line_y = self.pending_line.bounds.origin.y; - let (next_line, first_box_width) = self.initial_line_placement(in_box, this_line_y); + let (next_line, first_box_width) = self.initial_line_placement(in_box, this_line_y, flow); let next_green_zone = next_line.size; let new_width = self.pending_line.bounds.size.width + first_box_width; @@ -490,7 +481,6 @@ impl InlineFlowData { } pub fn teardown(&mut self) { - self.common.teardown(); for box in self.boxes.iter() { box.teardown(); } @@ -512,10 +502,10 @@ impl InlineLayout for FlowContext { } impl InlineFlowData { - pub fn bubble_widths_inline(@mut self, ctx: &mut LayoutContext) { + pub fn bubble_widths_inline(&mut self, ctx: &mut LayoutContext) { let mut num_floats = 0; - for kid in InlineFlow(self).children() { + for kid in self.common.child_iter() { do kid.with_mut_base |base| { num_floats += base.num_floats; base.floats_in = FloatContext::new(base.num_floats); @@ -542,11 +532,13 @@ impl InlineFlowData { /// 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, _: &LayoutContext) { + pub fn assign_widths_inline(&mut self, _: &LayoutContext) { // Initialize content box widths if they haven't been initialized already. // // TODO: Combine this with `LineboxScanner`'s walk in the box list, or put this into // `RenderBox`. + + debug!("assign_widths_inline: floats_in: %?", self.common.floats_in); { let this = &mut *self; for &box in this.boxes.iter() { @@ -571,7 +563,7 @@ impl InlineFlowData { } // End of for loop. } - for kid in InlineFlow(self).children() { + for kid in self.common.child_iter() { do kid.with_mut_base |base| { base.position.size.width = self.common.position.size.width; base.is_inorder = self.common.is_inorder; @@ -586,14 +578,14 @@ impl InlineFlowData { // 'inline-block' box that created this flow before recursing. } - pub fn assign_height_inorder_inline(@mut self, ctx: &mut LayoutContext) { - for kid in InlineFlow(self).children() { + pub fn assign_height_inorder_inline(&mut self, ctx: &mut LayoutContext) { + for kid in self.common.child_iter() { kid.assign_height_inorder(ctx); } self.assign_height_inline(ctx); } - pub fn assign_height_inline(@mut self, _: &LayoutContext) { + pub fn assign_height_inline(&mut self, _: &LayoutContext) { debug!("assign_height_inline: assigning height for flow %?", self.common.id); @@ -603,8 +595,10 @@ impl InlineFlowData { // // TODO(#226): Get the CSS `line-height` property from each non-replaced inline element to // determine its height for computing linebox height. - let mut scanner = LineboxScanner::new(InlineFlow(self), self.common.floats_in.clone()); - scanner.scan_for_lines(); + debug!("assign_height_inline: floats_in: %?", self.common.floats_in); + let scanner_floats = self.common.floats_in.clone(); + let mut scanner = LineboxScanner::new(scanner_floats); + scanner.scan_for_lines(self); // Now, go through each line and lay out the boxes inside for line in self.lines.iter() { @@ -760,7 +754,7 @@ impl InlineFlowData { let abs_rect = Rect(self.common.abs_position, self.common.position.size); if !abs_rect.intersects(dirty) { - return false; + return true; } // TODO(#228): Once we form line boxes and have their cached bounds, we can be smarter and @@ -777,7 +771,7 @@ impl InlineFlowData { // should the flow be nested inside the box somehow? // For now, don't traverse the subtree rooted here - false + true } } diff --git a/src/components/main/layout/layout_task.rs b/src/components/main/layout/layout_task.rs index a3e7901c3c0..ee9ce45de6e 100644 --- a/src/components/main/layout/layout_task.rs +++ b/src/components/main/layout/layout_task.rs @@ -8,7 +8,6 @@ use css::matching::MatchMethods; use css::select::new_css_select_ctx; use layout::aux::{LayoutData, LayoutAuxMethods}; -use layout::box::RenderBox; use layout::box_builder::LayoutTreeBuilder; use layout::context::LayoutContext; use layout::display_list_builder::{DisplayListBuilder}; @@ -18,6 +17,7 @@ use layout::incremental::{RestyleDamage, BubbleWidths}; use std::cast::transmute; use std::cell::Cell; use std::comm::{Port}; +use extra::arc::Arc; use geom::point::Point2D; use geom::rect::Rect; use geom::size::Size2D; @@ -44,6 +44,7 @@ use servo_net::local_image_cache::LocalImageCache; use servo_util::tree::TreeNodeRef; use servo_util::time::{ProfilerChan, profile}; use servo_util::time; +use servo_util::range::Range; use extra::url::Url; struct LayoutTask { @@ -51,7 +52,7 @@ struct LayoutTask { port: Port, constellation_chan: ConstellationChan, script_chan: ScriptChan, - render_chan: RenderChan, + render_chan: RenderChan>, image_cache_task: ImageCacheTask, local_image_cache: @mut LocalImageCache, font_ctx: @mut FontContext, @@ -61,6 +62,8 @@ struct LayoutTask { /// This is used to root reader data. layout_refs: ~[@mut LayoutData], + display_list: Option>>>, + css_select_ctx: @mut SelectCtx, profiler_chan: ProfilerChan, } @@ -70,7 +73,7 @@ impl LayoutTask { port: Port, constellation_chan: ConstellationChan, script_chan: ScriptChan, - render_chan: RenderChan, + render_chan: RenderChan>, img_cache_task: ImageCacheTask, opts: Opts, profiler_chan: ProfilerChan) { @@ -99,7 +102,7 @@ impl LayoutTask { port: Port, constellation_chan: ConstellationChan, script_chan: ScriptChan, - render_chan: RenderChan, + render_chan: RenderChan>, image_cache_task: ImageCacheTask, opts: &Opts, profiler_chan: ProfilerChan) @@ -117,6 +120,8 @@ impl LayoutTask { font_ctx: fctx, doc_url: None, screen_size: None, + + display_list: None, layout_refs: ~[], css_select_ctx: @mut new_css_select_ctx(), @@ -219,7 +224,7 @@ impl LayoutTask { } // Construct the flow tree. - let layout_root: FlowContext = do profile(time::LayoutTreeBuilderCategory, + 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) { @@ -232,7 +237,7 @@ impl LayoutTask { // Propagate restyle damage up and down the tree, as appropriate. // FIXME: Merge this with flow tree building and/or the other traversals. - for flow in layout_root.traverse_preorder() { + do layout_root.each_preorder |flow| { // Also set any damage implied by resize. if resized { do flow.with_mut_base |base| { @@ -242,23 +247,29 @@ impl LayoutTask { let prop = flow.with_base(|base| base.restyle_damage.propagate_down()); if prop.is_nonempty() { - for kid_ctx in flow.children() { + for kid_ctx in flow.child_iter() { do kid_ctx.with_mut_base |kid| { kid.restyle_damage.union_in_place(prop); } } } - } + true + }; - for flow in layout_root.traverse_postorder() { - for child in flow.children() { + do layout_root.each_postorder |flow| { + let mut damage = do flow.with_base |base| { + base.restyle_damage + }; + for child in flow.child_iter() { do child.with_base |child_base| { - do flow.with_mut_base |base| { - base.restyle_damage.union_in_place(child_base.restyle_damage); - } + damage.union_in_place(child_base.restyle_damage); } } - } + do flow.with_mut_base |base| { + base.restyle_damage = damage; + } + true + }; debug!("layout: constructed Flow tree"); debug!("%?", layout_root.dump()); @@ -266,22 +277,27 @@ impl LayoutTask { // Perform the primary layout passes over the flow tree to compute the locations of all // the boxes. do profile(time::LayoutMainCategory, self.profiler_chan.clone()) { - for flow in layout_root.traverse_postorder_prune(|f| f.restyle_damage().lacks(BubbleWidths)) { + do layout_root.each_postorder_prune(|f| f.restyle_damage().lacks(BubbleWidths)) |flow| { flow.bubble_widths(&mut layout_ctx); + true }; // FIXME: We want to do - // for flow in layout_root.traverse_preorder_prune(|f| f.restyle_damage().lacks(Reflow)) { + // for flow in layout_root.traverse_preorder_prune(|f| f.restyle_damage().lacks(Reflow)) // but FloatContext values can't be reused, so we need to recompute them every time. - for flow in layout_root.traverse_preorder() { + debug!("assigning widths"); + do layout_root.each_preorder |flow| { flow.assign_widths(&mut layout_ctx); + true }; // For now, this is an inorder traversal // FIXME: prune this traversal as well - do layout_root.traverse_bu_sub_inorder |flow| { + debug!("assigning height"); + do layout_root.each_bu_sub_inorder |flow| { flow.assign_height(&mut layout_ctx); - } + true + }; } // Build the display list if necessary, and send it to the renderer. @@ -291,23 +307,52 @@ impl LayoutTask { ctx: &layout_ctx, }; - let display_list = @Cell::new(DisplayList::new()); + let display_list = ~Cell::new(DisplayList::new::>()); // TODO: Set options on the builder before building. // TODO: Be smarter about what needs painting. - do layout_root.partially_traverse_preorder |flow| { - flow.build_display_list(&builder, &layout_root.position(), display_list) - } + let root_pos = &layout_root.position().clone(); + layout_root.each_preorder_prune(|flow| { + flow.build_display_list(&builder, root_pos, display_list) + }, |_| { true } ); let root_size = do layout_root.with_base |base| { base.position.size }; + let display_list = Arc::new(display_list.take()); + + for i in range(0,display_list.get().list.len()) { + let node: AbstractNode = unsafe { + transmute(display_list.get().list[i].base().extra) + }; + assert!(node.has_layout_data(), "Node has display item but no layout data"); + + let layout_data = node.layout_data(); + layout_data.boxes.display_list = Some(display_list.clone()); + + if layout_data.boxes.range.is_none() { + debug!("Creating initial range for node"); + layout_data.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"); + } + + layout_data.boxes.range.unwrap().extend_by(1); + } + } + let render_layer = RenderLayer { - display_list: display_list.take(), + display_list: display_list.clone(), size: Size2D(root_size.width.to_px() as uint, root_size.height.to_px() as uint) }; + self.display_list = Some(display_list.clone()); + self.render_chan.send(RenderMsg(render_layer)); } // time(layout: display list building) } @@ -330,19 +375,15 @@ impl LayoutTask { transmute(node) }; - let response = match node.layout_data().flow { - None => { - error!("no flow present"); - Err(()) - } - Some(flow) => { - let start_val: Option> = None; - let rect = do flow.foldl_boxes_for_node(node, start_val) |acc, box| { - match acc { - Some(acc) => Some(acc.union(&box.content_box())), - None => Some(box.content_box()) + let response = match (node.layout_data().boxes.display_list.clone(), node.layout_data().boxes.range) { + (Some(display_list), Some(range)) => { + let mut rect: Option> = None; + for i in range.eachi() { + rect = match rect { + Some(acc) => Some(acc.union(&display_list.get().list[i].bounds())), + None => Some(display_list.get().list[i].bounds()) } - }; + } match rect { None => { @@ -352,6 +393,10 @@ impl LayoutTask { Some(rect) => Ok(ContentBoxResponse(rect)) } } + _ => { + error!("no display list present"); + Err(()) + } }; reply_chan.send(response) @@ -362,71 +407,49 @@ impl LayoutTask { transmute(node) }; - let response = match node.layout_data().flow { - None => Err(()), - Some(flow) => { + let response = match (node.layout_data().boxes.display_list.clone(), node.layout_data().boxes.range) { + (Some(display_list), Some(range)) => { let mut boxes = ~[]; - for box in flow.iter_all_boxes() { - if box.node() == node { - boxes.push(box.content_box()); - } + for i in range.eachi() { + boxes.push(display_list.get().list[i].bounds()); } Ok(ContentBoxesResponse(boxes)) } + _ => Err(()), }; reply_chan.send(response) } - HitTestQuery(node, point, reply_chan) => { - // FIXME: Isolate this transmutation into a single "bridge" module. - let node: AbstractNode = unsafe { - transmute(node) - }; - let mut flow_node: AbstractNode = node; - for node in node.traverse_preorder() { - if node.layout_data().flow.is_some() { - flow_node = node; - break; - } - }; - - let response = match flow_node.layout_data().flow { - None => { - debug!("HitTestQuery: flow is None"); - Err(()) - } - Some(flow) => { - let layout_ctx = self.build_layout_context(); - let builder = DisplayListBuilder { - ctx: &layout_ctx, - }; - let display_list: @Cell> = - @Cell::new(DisplayList::new()); - - do flow.partially_traverse_preorder |this_flow| { - this_flow.build_display_list(&builder, - &flow.position(), - display_list) - - } - let (x, y) = (Au::from_frac_px(point.x as float), - Au::from_frac_px(point.y as float)); - let mut resp = Err(()); - let display_list = &display_list.take().list; - // iterate in reverse to ensure we have the most recently painted render box - for display_item in display_list.rev_iter() { - let bounds = display_item.bounds(); - // TODO this check should really be performed by a method of DisplayItem - if x <= bounds.origin.x + bounds.size.width && - bounds.origin.x <= x && - y < bounds.origin.y + bounds.size.height && - bounds.origin.y < y { - resp = Ok(HitTestResponse(display_item.base().extra.node())); - break; + HitTestQuery(_, point, reply_chan) => { + let response = { + match self.display_list { + Some(ref list) => { + let display_list = list.get(); + let (x, y) = (Au::from_frac_px(point.x as float), + Au::from_frac_px(point.y as float)); + let mut resp = Err(()); + // iterate in reverse to ensure we have the most recently painted render box + for display_item in display_list.list.rev_iter() { + let bounds = display_item.bounds(); + // TODO this check should really be performed by a method of DisplayItem + if x <= bounds.origin.x + bounds.size.width && + bounds.origin.x <= x && + y < bounds.origin.y + bounds.size.height && + bounds.origin.y < y { + let node: AbstractNode = unsafe { + transmute(display_item.base().extra) + }; + resp = Ok(HitTestResponse(node)); + break; + } } + resp } - resp + None => { + error!("Can't hit test: no display list"); + Err(()) + }, } }; diff --git a/src/components/main/layout/text.rs b/src/components/main/layout/text.rs index 1e744a4f337..56651c67067 100644 --- a/src/components/main/layout/text.rs +++ b/src/components/main/layout/text.rs @@ -66,16 +66,20 @@ impl TextRunScanner { } } - pub fn scan_for_runs(&mut self, ctx: &LayoutContext, flow: FlowContext) { - let inline = flow.inline(); - assert!(inline.boxes.len() > 0); - debug!("TextRunScanner: scanning %u boxes for text runs...", inline.boxes.len()); + pub fn scan_for_runs(&mut self, ctx: &LayoutContext, flow: &mut FlowContext) { + { + let inline = flow.imm_inline(); + // FIXME: this assertion fails on wikipedia, but doesn't seem + // to cause problems. + // assert!(inline.boxes.len() > 0); + debug!("TextRunScanner: scanning %u boxes for text runs...", inline.boxes.len()); + } let mut last_whitespace = true; let mut out_boxes = ~[]; - for box_i in range(0, flow.inline().boxes.len()) { - debug!("TextRunScanner: considering box: %?", flow.inline().boxes[box_i].debug_str()); - if box_i > 0 && !can_coalesce_text_nodes(flow.inline().boxes, box_i-1, box_i) { + for box_i in range(0, flow.imm_inline().boxes.len()) { + debug!("TextRunScanner: considering box: %?", flow.imm_inline().boxes[box_i].debug_str()); + if box_i > 0 && !can_coalesce_text_nodes(flow.imm_inline().boxes, box_i-1, box_i) { last_whitespace = self.flush_clump_to_list(ctx, flow, last_whitespace, &mut out_boxes); } self.clump.extend_by(1); @@ -118,10 +122,10 @@ impl TextRunScanner { /// necessary. pub fn flush_clump_to_list(&mut self, ctx: &LayoutContext, - flow: FlowContext, + flow: &mut FlowContext, last_whitespace: bool, out_boxes: &mut ~[RenderBox]) -> bool { - let inline = &mut *flow.inline(); + let inline = flow.inline(); let in_boxes = &inline.boxes; fn has_underline(decoration: CSSTextDecoration) -> bool{ diff --git a/src/components/main/pipeline.rs b/src/components/main/pipeline.rs index 33e70ed6ebf..0204748c20b 100644 --- a/src/components/main/pipeline.rs +++ b/src/components/main/pipeline.rs @@ -12,6 +12,7 @@ use layout::layout_task::LayoutTask; use script::layout_interface::LayoutChan; use script::script_task::{ExecuteMsg, LoadMsg}; use servo_msg::constellation_msg::{ConstellationChan, PipelineId, SubpageId}; +use script::dom::node::AbstractNode; use script::script_task::{AttachLayoutMsg, NewLayoutInfo, ScriptTask, ScriptChan}; use script::script_task; use servo_net::image_cache_task::ImageCacheTask; @@ -28,7 +29,7 @@ pub struct Pipeline { subpage_id: Option, script_chan: ScriptChan, layout_chan: LayoutChan, - render_chan: RenderChan, + render_chan: RenderChan>, /// The most recently loaded url url: Option, } @@ -130,7 +131,7 @@ impl Pipeline { subpage_id: Option, script_chan: ScriptChan, layout_chan: LayoutChan, - render_chan: RenderChan) + render_chan: RenderChan>) -> Pipeline { Pipeline { id: id,