aboutsummaryrefslogtreecommitdiffstats
path: root/src/components/main/layout/layout_task.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/components/main/layout/layout_task.rs')
-rw-r--r--src/components/main/layout/layout_task.rs240
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
}
}