diff options
Diffstat (limited to 'src/components/main/layout/layout_task.rs')
-rw-r--r-- | src/components/main/layout/layout_task.rs | 240 |
1 files changed, 157 insertions, 83 deletions
diff --git a/src/components/main/layout/layout_task.rs b/src/components/main/layout/layout_task.rs index 07a638a2604..cb029942710 100644 --- a/src/components/main/layout/layout_task.rs +++ b/src/components/main/layout/layout_task.rs @@ -11,7 +11,9 @@ use layout::aux::LayoutAuxMethods; use layout::box_builder::LayoutTreeBuilder; use layout::context::LayoutContext; use layout::display_list_builder::{DisplayListBuilder}; -use layout::flow::FlowContext; +use layout::flow::{FlowContext, ImmutableFlowUtils, MutableFlowUtils, PreorderFlowTraversal}; +use layout::flow::{PostorderFlowTraversal}; +use layout::flow; use layout::incremental::{RestyleDamage, BubbleWidths}; use std::cast::transmute; @@ -42,7 +44,7 @@ use script::layout_interface::{ReflowForDisplay, ReflowMsg}; use script::script_task::{ReflowCompleteMsg, ScriptChan, SendEventMsg}; use servo_msg::constellation_msg::{ConstellationChan, PipelineId}; use servo_net::image_cache_task::{ImageCacheTask, ImageResponseMsg}; -use servo_net::local_image_cache::LocalImageCache; +use servo_net::local_image_cache::{ImageResponder, LocalImageCache}; use servo_util::tree::TreeNodeRef; use servo_util::time::{ProfilerChan, profile}; use servo_util::time; @@ -67,6 +69,129 @@ struct LayoutTask { profiler_chan: ProfilerChan, } +/// The damage computation traversal. +#[deriving(Clone)] +struct ComputeDamageTraversal; + +impl PostorderFlowTraversal for ComputeDamageTraversal { + #[inline] + fn process(&mut self, flow: &mut FlowContext) -> bool { + let mut damage = flow::base(flow).restyle_damage; + for child in flow::child_iter(flow) { + damage.union_in_place(flow::base(*child).restyle_damage) + } + flow::mut_base(flow).restyle_damage = damage; + true + } +} + +/// Propagates restyle damage up and down the tree as appropriate. +/// +/// FIXME(pcwalton): Merge this with flow tree building and/or other traversals. +struct PropagateDamageTraversal { + resized: bool, +} + +impl PreorderFlowTraversal for PropagateDamageTraversal { + #[inline] + fn process(&mut self, flow: &mut FlowContext) -> bool { + // Also set any damage implied by resize. + if self.resized { + flow::mut_base(flow).restyle_damage.union_in_place(RestyleDamage::for_resize()) + } + + let prop = flow::base(flow).restyle_damage.propagate_down(); + if prop.is_nonempty() { + for kid_ctx in flow::child_iter(flow) { + flow::mut_base(*kid_ctx).restyle_damage.union_in_place(prop) + } + } + true + } +} + +/// The bubble-widths traversal, the first part of layout computation. This computes preferred +/// and intrinsic widths and bubbles them up the tree. +struct BubbleWidthsTraversal<'self>(&'self mut LayoutContext); + +impl<'self> PostorderFlowTraversal for BubbleWidthsTraversal<'self> { + #[inline] + fn process(&mut self, flow: &mut FlowContext) -> bool { + flow.bubble_widths(**self); + true + } + + #[inline] + fn should_prune(&mut self, flow: &mut FlowContext) -> bool { + flow::mut_base(flow).restyle_damage.lacks(BubbleWidths) + } +} + +/// The assign-widths traversal. In Gecko this corresponds to `Reflow`. +struct AssignWidthsTraversal<'self>(&'self mut LayoutContext); + +impl<'self> PreorderFlowTraversal for AssignWidthsTraversal<'self> { + #[inline] + fn process(&mut self, flow: &mut FlowContext) -> bool { + flow.assign_widths(**self); + true + } +} + +/// The assign-heights traversal, the last (and most expensive) part of layout computation. +/// Determines the final heights for all layout objects. In Gecko this corresponds to +/// `FinishAndStoreOverflow`. +struct AssignHeightsTraversal<'self>(&'self mut LayoutContext); + +impl<'self> PostorderFlowTraversal for AssignHeightsTraversal<'self> { + #[inline] + fn process(&mut self, flow: &mut FlowContext) -> bool { + flow.assign_height(**self); + true + } + + #[inline] + fn should_process(&mut self, flow: &mut FlowContext) -> bool { + !flow::base(flow).is_inorder + } +} + +/// The display list building traversal. In WebKit this corresponds to `paint`. In Gecko this +/// corresponds to `BuildDisplayListForChild`. +struct DisplayListBuildingTraversal<'self> { + builder: DisplayListBuilder<'self>, + root_pos: Rect<Au>, + display_list: ~Cell<DisplayList<AbstractNode<()>>>, +} + +impl<'self> PreorderFlowTraversal for DisplayListBuildingTraversal<'self> { + #[inline] + fn process(&mut self, _: &mut FlowContext) -> bool { + true + } + + #[inline] + fn should_prune(&mut self, flow: &mut FlowContext) -> bool { + flow.build_display_list(&self.builder, &self.root_pos, self.display_list) + } +} + +struct LayoutImageResponder { + id: PipelineId, + script_chan: ScriptChan, +} + +impl ImageResponder for LayoutImageResponder { + fn respond(&self) -> ~fn(ImageResponseMsg) { + let id = self.id.clone(); + let script_chan = self.script_chan.clone(); + let f: ~fn(ImageResponseMsg) = |_| { + script_chan.send(SendEventMsg(id.clone(), ReflowEvent)) + }; + f + } +} + impl LayoutTask { pub fn create(id: PipelineId, port: Port<Msg>, @@ -180,14 +305,13 @@ impl LayoutTask { // FIXME: Bad copy! let doc_url = data.url.clone(); - let script_chan = data.script_chan.clone(); debug!("layout: received layout request for: %s", doc_url.to_str()); debug!("layout: damage is %?", data.damage); debug!("layout: parsed Node tree"); debug!("%?", node.dump()); // Reset the image cache. - self.local_image_cache.next_round(self.make_on_image_available_cb(script_chan)); + self.local_image_cache.next_round(self.make_on_image_available_cb()); self.doc_url = Some(doc_url); let screen_size = Size2D(Au::from_px(data.window_size.width as int), @@ -218,10 +342,10 @@ impl LayoutTask { } // Construct the flow tree. - let mut layout_root: FlowContext = do profile(time::LayoutTreeBuilderCategory, - self.profiler_chan.clone()) { + 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) { + let layout_root: ~FlowContext: = match builder.construct_trees(&layout_ctx, *node) { Ok(root) => root, Err(*) => fail!(~"Root flow should always exist") }; @@ -229,41 +353,11 @@ impl LayoutTask { layout_root }; - // Propagate restyle damage up and down the tree, as appropriate. - // FIXME: Merge this with flow tree building and/or the other traversals. - do layout_root.each_preorder |flow| { - // Also set any damage implied by resize. - if resized { - do flow.with_mut_base |base| { - base.restyle_damage.union_in_place(RestyleDamage::for_resize()); - } - } - - let prop = flow.with_base(|base| base.restyle_damage.propagate_down()); - if prop.is_nonempty() { - for kid_ctx in flow.child_iter() { - do kid_ctx.with_mut_base |kid| { - kid.restyle_damage.union_in_place(prop); - } - } - } - true - }; - - 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| { - damage.union_in_place(child_base.restyle_damage); - } - } - do flow.with_mut_base |base| { - base.restyle_damage = damage; - } - true - }; + // Propagate damage. + layout_root.traverse_preorder(&mut PropagateDamageTraversal { + resized: resized, + }); + layout_root.traverse_postorder(&mut ComputeDamageTraversal.clone()); debug!("layout: constructed Flow tree"); debug!("%?", layout_root.dump()); @@ -271,51 +365,37 @@ 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()) { - do layout_root.each_postorder_prune(|f| f.restyle_damage().lacks(BubbleWidths)) |flow| { - flow.bubble_widths(&mut layout_ctx); - true - }; + let _ = layout_root.traverse_postorder(&mut BubbleWidthsTraversal(&mut layout_ctx)); - // FIXME: We want to do + // FIXME(kmc): We want to do // 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. // NOTE: this currently computes borders, so any pruning should separate that operation out. - debug!("assigning widths"); - do layout_root.each_preorder |flow| { - flow.assign_widths(&mut layout_ctx); - true - }; + let _ = layout_root.traverse_preorder(&mut AssignWidthsTraversal(&mut layout_ctx)); // For now, this is an inorder traversal // FIXME: prune this traversal as well - debug!("assigning height"); - do layout_root.each_bu_sub_inorder |flow| { - flow.assign_height(&mut layout_ctx); - true - }; + let _ = layout_root.traverse_postorder(&mut AssignHeightsTraversal(&mut layout_ctx)); } // Build the display list if necessary, and send it to the renderer. if data.goal == ReflowForDisplay { do profile(time::LayoutDispListBuildCategory, self.profiler_chan.clone()) { - let builder = DisplayListBuilder { - ctx: &layout_ctx, - }; - - let display_list = ~Cell::new(DisplayList::<AbstractNode<()>>::new()); - // TODO: Set options on the builder before building. // TODO: Be smarter about what needs painting. - 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 mut traversal = DisplayListBuildingTraversal { + builder: DisplayListBuilder { + ctx: &layout_ctx, + }, + root_pos: flow::base(layout_root).position.clone(), + display_list: ~Cell::new(DisplayList::<AbstractNode<()>>::new()), }; - let display_list = Arc::new(display_list.take()); + let _ = layout_root.traverse_preorder(&mut traversal); + + let root_size = flow::base(layout_root).position.size; + + let display_list = Arc::new(traversal.display_list.take()); for i in range(0,display_list.get().list.len()) { let node: AbstractNode<LayoutView> = unsafe { @@ -445,7 +525,7 @@ impl LayoutTask { Some(ref list) => { let display_list = list.get(); let (x, y) = (Au::from_frac_px(point.x as f64), - Au::from_frac_px(point.y as f64)); + Au::from_frac_px(point.y as f64)); 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() { @@ -481,21 +561,15 @@ impl LayoutTask { // to the script task, and ultimately cause the image to be // re-requested. We probably don't need to go all the way back to // the script task for this. - fn make_on_image_available_cb(&self, script_chan: ScriptChan) - -> ~fn() -> ~fn(ImageResponseMsg) { + fn make_on_image_available_cb(&self) -> @ImageResponder { // This has a crazy signature because the image cache needs to // make multiple copies of the callback, and the dom event // channel is not a copyable type, so this is actually a // little factory to produce callbacks - let id = self.id.clone(); - let f: ~fn() -> ~fn(ImageResponseMsg) = || { - let script_chan = script_chan.clone(); - let f: ~fn(ImageResponseMsg) = |_| { - script_chan.send(SendEventMsg(id.clone(), ReflowEvent)) - }; - f - }; - return f; + @LayoutImageResponder { + id: self.id.clone(), + script_chan: self.script_chan.clone(), + } as @ImageResponder } } |