From 2d72f00ccf8abfd5805dccdca5a635fa4e8e0cb8 Mon Sep 17 00:00:00 2001 From: Martin Robinson Date: Wed, 29 Oct 2014 19:02:31 -0700 Subject: [PATCH] Have ContentBox(es)Queries consult the flow tree Instead of looking at the display tree, have ContentBox(es)Query consult the flow tree. This allow optimizing away parts of the display tree later. To do this we need to be more careful about how we send reflow requests, only querying the flow tree when possible. Fixes #3790. --- components/layout/block.rs | 11 +- components/layout/display_list_builder.rs | 12 +- components/layout/flow.rs | 13 +- components/layout/fragment.rs | 19 +- components/layout/inline.rs | 21 +- components/layout/layout_task.rs | 355 +++++++++++++--------- components/layout/sequential.rs | 14 + components/layout/table.rs | 6 +- components/layout/table_caption.rs | 5 + components/layout/table_cell.rs | 6 +- components/layout/table_colgroup.rs | 5 +- components/layout/table_row.rs | 6 +- components/layout/table_rowgroup.rs | 6 +- components/layout/table_wrapper.rs | 6 +- components/script/dom/node.rs | 16 +- components/script/dom/window.rs | 8 +- components/script/layout_interface.rs | 15 +- components/script/page.rs | 55 +++- components/script/script_task.rs | 8 +- 19 files changed, 374 insertions(+), 213 deletions(-) diff --git a/components/layout/block.rs b/components/layout/block.rs index b0ba90c2742..8835682644d 100644 --- a/components/layout/block.rs +++ b/components/layout/block.rs @@ -35,7 +35,8 @@ use floats::{ClearBoth, ClearLeft, ClearRight, FloatKind, FloatLeft, Floats, Pla use flow::{BaseFlow, BlockFlowClass, FlowClass, Flow, ImmutableFlowUtils}; use flow::{MutableFlowUtils, PreorderFlowTraversal, PostorderFlowTraversal, mut_base}; use flow; -use fragment::{Fragment, ImageFragment, InlineBlockFragment, ScannedTextFragment}; +use fragment::{Fragment, ImageFragment, InlineBlockFragment, FragmentBoundsIterator}; +use fragment::ScannedTextFragment; use incremental::{Reflow, ReflowOutOfFlow}; use layout_debug; use model::{Auto, IntrinsicISizes, MarginCollapseInfo, MarginsCollapse, MarginsCollapseThrough}; @@ -1816,6 +1817,14 @@ impl Flow for BlockFlow { fn repair_style(&mut self, new_style: &Arc) { self.fragment.repair_style(new_style) } + + fn iterate_through_fragment_bounds(&self, iterator: &mut FragmentBoundsIterator) { + if iterator.should_process(&self.fragment) { + let fragment_origin = self.base.child_fragment_absolute_position(&self.fragment); + iterator.process(&self.fragment, + self.fragment.abs_bounds_from_origin(&fragment_origin)); + } + } } impl fmt::Show for BlockFlow { diff --git a/components/layout/display_list_builder.rs b/components/layout/display_list_builder.rs index 3baac9781b4..868765b7e78 100644 --- a/components/layout/display_list_builder.rs +++ b/components/layout/display_list_builder.rs @@ -322,7 +322,7 @@ impl FragmentDisplayListBuilding for Fragment { Rect(physical_rect.origin + flow_origin, physical_rect.size) }; // Fragment position wrt to the owning flow. - let absolute_fragment_bounds = rect_to_absolute(self.style.writing_mode, self.border_box); + let absolute_fragment_bounds = self.abs_bounds_from_origin(&flow_origin); debug!("Fragment::build_display_list at rel={}, abs={}: {}", self.border_box, absolute_fragment_bounds, @@ -615,18 +615,12 @@ impl BlockFlowDisplayListBuilding for BlockFlow { fn build_display_list_for_block(&mut self, layout_context: &LayoutContext, background_border_level: BackgroundAndBorderLevel) { - let relative_offset = - self.fragment.relative_position(&self.base - .absolute_position_info - .relative_containing_block_size); // Add the box that starts the block context. - self.base.display_list = DisplayList::new(); - let absolute_position = - self.base.abs_position.add_size(&relative_offset.to_physical(self.base.writing_mode)); + let absolute_fragment_origin = self.base.child_fragment_absolute_position(&self.fragment); self.fragment.build_display_list(&mut self.base.display_list, layout_context, - absolute_position, + absolute_fragment_origin, background_border_level, &self.base.clip_rect); diff --git a/components/layout/flow.rs b/components/layout/flow.rs index 2479dc14c9d..91a16966557 100644 --- a/components/layout/flow.rs +++ b/components/layout/flow.rs @@ -31,7 +31,7 @@ use context::LayoutContext; use floats::Floats; use flow_list::{FlowList, FlowListIterator, MutFlowListIterator}; use flow_ref::FlowRef; -use fragment::{Fragment, TableRowFragment, TableCellFragment}; +use fragment::{Fragment, FragmentBoundsIterator, TableRowFragment, TableCellFragment}; use incremental::{ReconstructFlow, Reflow, ReflowOutOfFlow, RestyleDamage}; use inline::InlineFlow; use model::{CollapsibleMargins, IntrinsicISizes, MarginCollapseInfo}; @@ -214,6 +214,9 @@ pub trait Flow: fmt::Show + ToString + Sync { /// Phase 5 of reflow: builds display lists. fn build_display_list(&mut self, layout_context: &LayoutContext); + /// Perform an iteration of fragment bounds on this flow. + fn iterate_through_fragment_bounds(&self, iterator: &mut FragmentBoundsIterator); + fn compute_collapsible_block_start_margin(&mut self, _layout_context: &mut LayoutContext, _margin_collapse_info: &mut MarginCollapseInfo) { @@ -943,6 +946,14 @@ impl BaseFlow { } } } + + pub fn child_fragment_absolute_position(&self, fragment: &Fragment) -> Point2D { + let relative_offset = + fragment.relative_position(&self + .absolute_position_info + .relative_containing_block_size); + self.abs_position.add_size(&relative_offset.to_physical(self.writing_mode)) + } } impl<'a> ImmutableFlowUtils for &'a Flow + 'a { diff --git a/components/layout/fragment.rs b/components/layout/fragment.rs index 2844495bb23..568fd4dfa71 100644 --- a/components/layout/fragment.rs +++ b/components/layout/fragment.rs @@ -22,7 +22,7 @@ use text; use util::OpaqueNodeMethods; use wrapper::{TLayoutNode, ThreadSafeLayoutNode}; -use geom::Size2D; +use geom::{Point2D, Rect, Size2D}; use gfx::display_list::OpaqueNode; use gfx::text::glyph::CharIndex; use gfx::text::text_run::TextRun; @@ -1482,6 +1482,13 @@ impl Fragment { pub fn repair_style(&mut self, new_style: &Arc) { self.style = (*new_style).clone() } + + pub fn abs_bounds_from_origin(&self, fragment_origin: &Point2D) -> Rect { + // FIXME(#2795): Get the real container size + let container_size = Size2D::zero(); + self.border_box.to_physical(self.style.writing_mode, container_size) + .translate(fragment_origin) + } } impl fmt::Show for Fragment { @@ -1502,3 +1509,13 @@ bitflags! { static IntrinsicInlineSizeIncludesSpecified = 0x08, } } + +/// A top-down fragment bounds iteration handler. +pub trait FragmentBoundsIterator { + /// The operation to perform. + fn process(&mut self, fragment: &Fragment, bounds: Rect); + + /// Returns true if this fragment must be processed in-order. If this returns false, + /// we skip the operation for this fragment, but continue processing siblings. + fn should_process(&mut self, fragment: &Fragment) -> bool; +} diff --git a/components/layout/inline.rs b/components/layout/inline.rs index 83e18ac3668..8a4deb386fb 100644 --- a/components/layout/inline.rs +++ b/components/layout/inline.rs @@ -11,7 +11,8 @@ use floats::{FloatLeft, Floats, PlacementInfo}; use flow::{BaseFlow, FlowClass, Flow, InlineFlowClass, MutableFlowUtils}; use flow; use fragment::{Fragment, InlineAbsoluteHypotheticalFragment, InlineBlockFragment}; -use fragment::{ScannedTextFragment, ScannedTextFragmentInfo, SplitInfo}; +use fragment::{FragmentBoundsIterator, ScannedTextFragment, ScannedTextFragmentInfo}; +use fragment::SplitInfo; use incremental::{Reflow, ReflowOutOfFlow}; use layout_debug; use model::IntrinsicISizesContribution; @@ -1195,15 +1196,10 @@ impl Flow for InlineFlow { debug!("Flow: building display list for {:u} inline fragments", self.fragments.len()); for fragment in self.fragments.fragments.iter_mut() { - let rel_offset = fragment.relative_position(&self.base - .absolute_position_info - .relative_containing_block_size); - let fragment_position = self.base - .abs_position - .add_size(&rel_offset.to_physical(self.base.writing_mode)); + let fragment_origin = self.base.child_fragment_absolute_position(fragment); fragment.build_display_list(&mut self.base.display_list, layout_context, - fragment_position, + fragment_origin, ContentLevel, &self.base.clip_rect); match fragment.specific { @@ -1223,6 +1219,15 @@ impl Flow for InlineFlow { } fn repair_style(&mut self, _: &Arc) {} + + fn iterate_through_fragment_bounds(&self, iterator: &mut FragmentBoundsIterator) { + for fragment in self.fragments.fragments.iter() { + if iterator.should_process(fragment) { + let fragment_origin = self.base.child_fragment_absolute_position(fragment); + iterator.process(fragment, fragment.abs_bounds_from_origin(&fragment_origin)); + } + } + } } impl fmt::Show for InlineFlow { diff --git a/components/layout/layout_task.rs b/components/layout/layout_task.rs index 9a433003bf9..9edc1e3f1a5 100644 --- a/components/layout/layout_task.rs +++ b/components/layout/layout_task.rs @@ -10,6 +10,7 @@ use construct::FlowConstructionResult; use context::SharedLayoutContext; use flow::{mod, Flow, ImmutableFlowUtils, MutableFlowUtils, MutableOwnedFlowUtils}; use flow_ref::FlowRef; +use fragment::{Fragment, FragmentBoundsIterator}; use incremental::{LayoutDamageComputation, Reflow, ReflowEntireDocument, Repaint}; use layout_debug; use parallel::UnsafeFlow; @@ -24,7 +25,7 @@ use encoding::all::UTF_8; use geom::point::Point2D; use geom::rect::Rect; use geom::size::Size2D; -use gfx::display_list::{ContentStackingLevel, DisplayItem, DisplayItemIterator, DisplayList}; +use gfx::display_list::{ContentStackingLevel, DisplayItem, DisplayList}; use gfx::display_list::{OpaqueNode}; use gfx::render_task::{RenderInitMsg, RenderChan, RenderLayer}; use gfx::{render_task, color}; @@ -34,12 +35,12 @@ use log; use script::dom::bindings::js::JS; use script::dom::node::{ElementNodeTypeId, LayoutDataRef, Node}; use script::dom::element::{HTMLBodyElementTypeId, HTMLHtmlElementTypeId}; -use script::layout_interface::{AddStylesheetMsg, LoadStylesheetMsg, ScriptLayoutChan}; -use script::layout_interface::{TrustedNodeAddress, ContentBoxesResponse, ExitNowMsg}; -use script::layout_interface::{ContentBoxResponse, HitTestResponse, MouseOverResponse}; -use script::layout_interface::{LayoutChan, Msg, PrepareToExitMsg}; -use script::layout_interface::{GetRPCMsg, LayoutRPC, ReapLayoutDataMsg, Reflow}; -use script::layout_interface::{ReflowForDisplay, ReflowMsg}; +use script::layout_interface::{ + AddStylesheetMsg, ContentBoxResponse, ContentBoxesResponse, ContentBoxesQuery, + ContentBoxQuery, ExitNowMsg, GetRPCMsg, HitTestResponse, LayoutChan, LayoutRPC, + LoadStylesheetMsg, MouseOverResponse, Msg, NoQuery, PrepareToExitMsg, ReapLayoutDataMsg, + Reflow, ReflowForDisplay, ReflowMsg, ScriptLayoutChan, TrustedNodeAddress, +}; use script_traits::{SendEventMsg, ReflowEvent, ReflowCompleteMsg, OpaqueScriptLayoutChannel}; use script_traits::{ScriptControlChan, UntrustedNodeAddress}; use servo_msg::compositor_msg::Scrollable; @@ -95,6 +96,12 @@ pub struct LayoutTaskData { /// True if a style sheet was added since the last reflow. Currently, this causes all nodes to /// be dirtied at the next reflow. pub stylesheet_dirty: bool, + + /// A queued response for the union of the content boxes of a node. + pub content_box_response: Rect, + + /// A queued response for the content boxes of a node. + pub content_boxes_response: Vec>, } /// Information needed by the layout task. @@ -284,6 +291,8 @@ impl LayoutTask { dirty: Rect::zero(), generation: 0, stylesheet_dirty: false, + content_box_response: Rect::zero(), + content_boxes_response: Vec::new(), })), } } @@ -587,6 +596,124 @@ impl LayoutTask { fn verify_flow_tree(&self, _: &mut FlowRef) { } + fn process_content_box_request<'a>(&'a self, + requested_node: TrustedNodeAddress, + layout_root: &mut FlowRef, + rw_data: &mut RWGuard<'a>) { + let requested_node: OpaqueNode = OpaqueNodeMethods::from_script_node(requested_node); + let mut iterator = UnioningFragmentBoundsIterator::new(requested_node); + sequential::iterate_through_flow_tree_fragment_bounds(layout_root, &mut iterator); + rw_data.content_box_response = iterator.rect; + } + + fn process_content_boxes_request<'a>(&'a self, + requested_node: TrustedNodeAddress, + layout_root: &mut FlowRef, + rw_data: &mut RWGuard<'a>) { + let requested_node: OpaqueNode = OpaqueNodeMethods::from_script_node(requested_node); + let mut iterator = CollectingFragmentBoundsIterator::new(requested_node); + sequential::iterate_through_flow_tree_fragment_bounds(layout_root, &mut iterator); + rw_data.content_boxes_response = iterator.rects; + } + + fn build_display_list_for_reflow<'a>(&'a self, + data: &Reflow, + node: &mut LayoutNode, + layout_root: &mut FlowRef, + shared_layout_ctx: &mut SharedLayoutContext, + rw_data: &mut RWGuard<'a>) { + let writing_mode = flow::base(layout_root.deref()).writing_mode; + profile(time::LayoutDispListBuildCategory, + Some((&data.url, data.iframe, self.first_reflow.get())), + self.time_profiler_chan.clone(), + || { + shared_layout_ctx.dirty = + flow::base(layout_root.deref()).position.to_physical(writing_mode, + rw_data.screen_size); + flow::mut_base(layout_root.deref_mut()).abs_position = + LogicalPoint::zero(writing_mode).to_physical(writing_mode, + rw_data.screen_size); + + let rw_data = rw_data.deref_mut(); + match rw_data.parallel_traversal { + None => { + sequential::build_display_list_for_subtree(layout_root, shared_layout_ctx); + } + Some(ref mut traversal) => { + parallel::build_display_list_for_subtree(layout_root, + &data.url, + data.iframe, + self.first_reflow.get(), + self.time_profiler_chan.clone(), + shared_layout_ctx, + traversal); + } + } + + debug!("Done building display list. Display List = {}", + flow::base(layout_root.deref()).display_list); + + flow::mut_base(layout_root.deref_mut()).display_list.flatten(ContentStackingLevel); + let display_list = + Arc::new(mem::replace(&mut flow::mut_base(layout_root.deref_mut()).display_list, + DisplayList::new())); + + // FIXME(pcwalton): This is really ugly and can't handle overflow: scroll. Refactor + // it with extreme prejudice. + let mut color = color::rgba(1.0, 1.0, 1.0, 1.0); + for child in node.traverse_preorder() { + if child.type_id() == Some(ElementNodeTypeId(HTMLHtmlElementTypeId)) || + child.type_id() == Some(ElementNodeTypeId(HTMLBodyElementTypeId)) { + let element_bg_color = { + let thread_safe_child = ThreadSafeLayoutNode::new(&child); + thread_safe_child.style() + .resolve_color(thread_safe_child.style() + .get_background() + .background_color) + .to_gfx_color() + }; + match element_bg_color { + color::rgba(0., 0., 0., 0.) => {} + _ => { + color = element_bg_color; + break; + } + } + } + } + + let root_size = { + let root_flow = flow::base(layout_root.deref()); + root_flow.position.size.to_physical(root_flow.writing_mode) + }; + let root_size = Size2D(root_size.width.to_nearest_px() as uint, + root_size.height.to_nearest_px() as uint); + let render_layer = RenderLayer { + id: layout_root.layer_id(0), + display_list: display_list.clone(), + position: Rect(Point2D(0u, 0u), root_size), + background_color: color, + scroll_policy: Scrollable, + }; + + rw_data.display_list = Some(display_list); + + // TODO(pcwalton): Eventually, when we have incremental reflow, this will have to + // be smarter in order to handle retained layer contents properly from reflow to + // reflow. + let mut layers = SmallVec1::new(); + layers.push(render_layer); + for layer in mem::replace(&mut flow::mut_base(layout_root.deref_mut()).layers, + DList::new()).into_iter() { + layers.push(layer) + } + + debug!("Layout done!"); + + self.render_chan.send(RenderInitMsg(layers)); + }); + } + /// The high-level routine that performs layout tasks. fn handle_reflow<'a>(&'a self, data: &Reflow, @@ -713,97 +840,19 @@ impl LayoutTask { // Build the display list if necessary, and send it to the renderer. if data.goal == ReflowForDisplay { - let writing_mode = flow::base(layout_root.deref()).writing_mode; - profile(time::LayoutDispListBuildCategory, - Some((&data.url, data.iframe, self.first_reflow.get())), - self.time_profiler_chan.clone(), - || { - shared_layout_ctx.dirty = - flow::base(layout_root.deref()).position.to_physical(writing_mode, - rw_data.screen_size); - flow::mut_base(layout_root.deref_mut()).abs_position = - LogicalPoint::zero(writing_mode).to_physical(writing_mode, - rw_data.screen_size); + self.build_display_list_for_reflow(data, + node, + &mut layout_root, + &mut shared_layout_ctx, + &mut rw_data); + } - let rw_data = rw_data.deref_mut(); - match rw_data.parallel_traversal { - None => { - sequential::build_display_list_for_subtree(&mut layout_root, - &shared_layout_ctx); - } - Some(ref mut traversal) => { - parallel::build_display_list_for_subtree(&mut layout_root, - &data.url, - data.iframe, - self.first_reflow.get(), - self.time_profiler_chan.clone(), - &shared_layout_ctx, - traversal); - } - } - - debug!("Done building display list. Display List = {}", - flow::base(layout_root.deref()).display_list); - - flow::mut_base(&mut *layout_root).display_list.flatten(ContentStackingLevel); - let display_list = - Arc::new(mem::replace(&mut flow::mut_base(&mut *layout_root).display_list, - DisplayList::new())); - - // FIXME(pcwalton): This is really ugly and can't handle overflow: scroll. Refactor - // it with extreme prejudice. - let mut color = color::rgba(1.0, 1.0, 1.0, 1.0); - for child in node.traverse_preorder() { - if child.type_id() == Some(ElementNodeTypeId(HTMLHtmlElementTypeId)) || - child.type_id() == Some(ElementNodeTypeId(HTMLBodyElementTypeId)) { - let element_bg_color = { - let thread_safe_child = ThreadSafeLayoutNode::new(&child); - thread_safe_child.style() - .resolve_color(thread_safe_child.style() - .get_background() - .background_color) - .to_gfx_color() - }; - match element_bg_color { - color::rgba(0., 0., 0., 0.) => {} - _ => { - color = element_bg_color; - break; - } - } - } - } - - let root_size = { - let root_flow = flow::base(layout_root.deref()); - root_flow.position.size.to_physical(root_flow.writing_mode) - }; - let root_size = Size2D(root_size.width.to_nearest_px() as uint, - root_size.height.to_nearest_px() as uint); - let render_layer = RenderLayer { - id: layout_root.layer_id(0), - display_list: display_list.clone(), - position: Rect(Point2D(0u, 0u), root_size), - background_color: color, - scroll_policy: Scrollable, - }; - - rw_data.display_list = Some(display_list); - - // TODO(pcwalton): Eventually, when we have incremental reflow, this will have to - // be smarter in order to handle retained layer contents properly from reflow to - // reflow. - let mut layers = SmallVec1::new(); - layers.push(render_layer); - for layer in mem::replace(&mut flow::mut_base(layout_root.deref_mut()).layers, - DList::new()).into_iter() { - layers.push(layer) - } - - debug!("Layout done!"); - - self.render_chan.send(RenderInitMsg(layers)); - }); + match data.query_type { + ContentBoxQuery(node) => + self.process_content_box_request(node, &mut layout_root, &mut rw_data), + ContentBoxesQuery(node) => + self.process_content_boxes_request(node, &mut layout_root, &mut rw_data), + NoQuery => {}, } self.first_reflow.set(false); @@ -876,61 +925,17 @@ struct LayoutRPCImpl(Arc>); impl LayoutRPC for LayoutRPCImpl { // The neat thing here is that in order to answer the following two queries we only // need to compare nodes for equality. Thus we can safely work only with `OpaqueNode`. - fn content_box(&self, node: TrustedNodeAddress) -> ContentBoxResponse { - let node: OpaqueNode = OpaqueNodeMethods::from_script_node(node); - fn union_boxes_for_node(accumulator: &mut Option>, - mut iter: DisplayItemIterator, - node: OpaqueNode) { - for item in iter { - if item.base().node == node { - match *accumulator { - None => *accumulator = Some(item.base().bounds), - Some(ref mut acc) => *acc = acc.union(&item.base().bounds), - } - } - } - } - - let mut rect = None; - { - let &LayoutRPCImpl(ref rw_data) = self; - let rw_data = rw_data.lock(); - match rw_data.display_list { - None => fail!("no display list!"), - Some(ref display_list) => { - union_boxes_for_node(&mut rect, display_list.iter(), node) - } - } - } - ContentBoxResponse(rect.unwrap_or(Rect::zero())) + fn content_box(&self) -> ContentBoxResponse { + let &LayoutRPCImpl(ref rw_data) = self; + let rw_data = rw_data.lock(); + ContentBoxResponse(rw_data.content_box_response) } /// Requests the dimensions of all the content boxes, as in the `getClientRects()` call. - fn content_boxes(&self, node: TrustedNodeAddress) -> ContentBoxesResponse { - let node: OpaqueNode = OpaqueNodeMethods::from_script_node(node); - - fn add_boxes_for_node(accumulator: &mut Vec>, - mut iter: DisplayItemIterator, - node: OpaqueNode) { - for item in iter { - if item.base().node == node { - accumulator.push(item.base().bounds) - } - } - } - - let mut boxes = vec!(); - { - let &LayoutRPCImpl(ref rw_data) = self; - let rw_data = rw_data.lock(); - match rw_data.display_list { - None => fail!("no display list!"), - Some(ref display_list) => { - add_boxes_for_node(&mut boxes, display_list.iter(), node) - } - } - } - ContentBoxesResponse(boxes) + fn content_boxes(&self) -> ContentBoxesResponse { + let &LayoutRPCImpl(ref rw_data) = self; + let mut rw_data = rw_data.lock(); + ContentBoxesResponse(rw_data.content_boxes_response.clone()) } /// Requests the node containing the point of interest @@ -997,3 +1002,55 @@ impl LayoutRPC for LayoutRPCImpl { } } } + +struct UnioningFragmentBoundsIterator { + node_address: OpaqueNode, + rect: Rect, +} + +impl UnioningFragmentBoundsIterator { + fn new(node_address: OpaqueNode) -> UnioningFragmentBoundsIterator { + UnioningFragmentBoundsIterator { + node_address: node_address, + rect: Rect::zero(), + } + } +} + +impl FragmentBoundsIterator for UnioningFragmentBoundsIterator { + fn process(&mut self, _: &Fragment, bounds: Rect) { + if self.rect.is_empty() { + self.rect = bounds; + } else { + self.rect = self.rect.union(&bounds); + } + } + + fn should_process(&mut self, fragment: &Fragment) -> bool { + self.node_address == fragment.node + } +} + +struct CollectingFragmentBoundsIterator { + node_address: OpaqueNode, + rects: Vec>, +} + +impl CollectingFragmentBoundsIterator { + fn new(node_address: OpaqueNode) -> CollectingFragmentBoundsIterator { + CollectingFragmentBoundsIterator { + node_address: node_address, + rects: Vec::new(), + } + } +} + +impl FragmentBoundsIterator for CollectingFragmentBoundsIterator { + fn process(&mut self, _: &Fragment, bounds: Rect) { + self.rects.push(bounds); + } + + fn should_process(&mut self, fragment: &Fragment) -> bool { + self.node_address == fragment.node + } +} diff --git a/components/layout/sequential.rs b/components/layout/sequential.rs index 57afc925918..4636fa9c3eb 100644 --- a/components/layout/sequential.rs +++ b/components/layout/sequential.rs @@ -8,6 +8,7 @@ use context::{LayoutContext, SharedLayoutContext}; use flow::{Flow, MutableFlowUtils, PreorderFlowTraversal, PostorderFlowTraversal}; use flow; use flow_ref::FlowRef; +use fragment::FragmentBoundsIterator; use servo_util::opts; use traversal::{BubbleISizes, RecalcStyleForNode, ConstructFlows}; use traversal::{AssignBSizesAndStoreOverflow, AssignISizes}; @@ -92,3 +93,16 @@ pub fn build_display_list_for_subtree(root: &mut FlowRef, doit(root.deref_mut(), compute_absolute_positions, build_display_list); } + +pub fn iterate_through_flow_tree_fragment_bounds(root: &mut FlowRef, + iterator: &mut FragmentBoundsIterator) { + fn doit(flow: &mut Flow, iterator: &mut FragmentBoundsIterator) { + flow.iterate_through_fragment_bounds(iterator); + + for kid in flow::mut_base(flow).child_iter() { + doit(kid, iterator); + } + } + + doit(root.deref_mut(), iterator); +} diff --git a/components/layout/table.rs b/components/layout/table.rs index cb41e887747..f9264bad334 100644 --- a/components/layout/table.rs +++ b/components/layout/table.rs @@ -12,7 +12,7 @@ use construct::FlowConstructor; use context::LayoutContext; use floats::FloatKind; use flow::{TableFlowClass, FlowClass, Flow, ImmutableFlowUtils}; -use fragment::Fragment; +use fragment::{Fragment, FragmentBoundsIterator}; use layout_debug; use model::{IntrinsicISizes, IntrinsicISizesContribution}; use table_wrapper::{TableLayout, FixedLayout, AutoLayout}; @@ -327,6 +327,10 @@ impl Flow for TableFlow { fn repair_style(&mut self, new_style: &Arc) { self.block_flow.repair_style(new_style) } + + fn iterate_through_fragment_bounds(&self, iterator: &mut FragmentBoundsIterator) { + self.block_flow.iterate_through_fragment_bounds(iterator); + } } impl fmt::Show for TableFlow { diff --git a/components/layout/table_caption.rs b/components/layout/table_caption.rs index b27a8260f7c..cffc05dd93b 100644 --- a/components/layout/table_caption.rs +++ b/components/layout/table_caption.rs @@ -10,6 +10,7 @@ use block::BlockFlow; use construct::FlowConstructor; use context::LayoutContext; use flow::{TableCaptionFlowClass, FlowClass, Flow}; +use fragment::FragmentBoundsIterator; use wrapper::ThreadSafeLayoutNode; use servo_util::geometry::Au; @@ -79,6 +80,10 @@ impl Flow for TableCaptionFlow { fn repair_style(&mut self, new_style: &Arc) { self.block_flow.repair_style(new_style) } + + fn iterate_through_fragment_bounds(&self, iterator: &mut FragmentBoundsIterator) { + self.iterate_through_fragment_bounds(iterator); + } } impl fmt::Show for TableCaptionFlow { diff --git a/components/layout/table_cell.rs b/components/layout/table_cell.rs index c31454b162f..4d9677b3c88 100644 --- a/components/layout/table_cell.rs +++ b/components/layout/table_cell.rs @@ -9,7 +9,7 @@ use block::{BlockFlow, MarginsMayNotCollapse, ISizeAndMarginsComputer}; use context::LayoutContext; use flow::{TableCellFlowClass, FlowClass, Flow}; -use fragment::Fragment; +use fragment::{Fragment, FragmentBoundsIterator}; use model::{MaybeAuto}; use layout_debug; use table::InternalTable; @@ -148,6 +148,10 @@ impl Flow for TableCellFlow { fn repair_style(&mut self, new_style: &Arc) { self.block_flow.repair_style(new_style) } + + fn iterate_through_fragment_bounds(&self, iterator: &mut FragmentBoundsIterator) { + self.block_flow.iterate_through_fragment_bounds(iterator); + } } impl fmt::Show for TableCellFlow { diff --git a/components/layout/table_colgroup.rs b/components/layout/table_colgroup.rs index f726d6a3270..a6ea6efdae4 100644 --- a/components/layout/table_colgroup.rs +++ b/components/layout/table_colgroup.rs @@ -9,7 +9,7 @@ use context::LayoutContext; use css::node_style::StyledNode; use flow::{BaseFlow, TableColGroupFlowClass, FlowClass, Flow}; -use fragment::{Fragment, TableColumnFragment}; +use fragment::{Fragment, FragmentBoundsIterator, TableColumnFragment}; use layout_debug; use wrapper::ThreadSafeLayoutNode; @@ -95,6 +95,9 @@ impl Flow for TableColGroupFlow { fn build_display_list(&mut self, _: &LayoutContext) {} fn repair_style(&mut self, _: &Arc) {} + + fn iterate_through_fragment_bounds(&self, _: &mut FragmentBoundsIterator) { + } } impl fmt::Show for TableColGroupFlow { diff --git a/components/layout/table_row.rs b/components/layout/table_row.rs index ff7eaa9a0ab..10e648a9f24 100644 --- a/components/layout/table_row.rs +++ b/components/layout/table_row.rs @@ -12,7 +12,7 @@ use construct::FlowConstructor; use context::LayoutContext; use flow::{TableRowFlowClass, FlowClass, Flow, ImmutableFlowUtils}; use flow; -use fragment::Fragment; +use fragment::{Fragment, FragmentBoundsIterator}; use layout_debug; use table::{ColumnInlineSize, InternalTable}; use model::{MaybeAuto, Specified, Auto}; @@ -251,6 +251,10 @@ impl Flow for TableRowFlow { fn repair_style(&mut self, new_style: &Arc) { self.block_flow.repair_style(new_style) } + + fn iterate_through_fragment_bounds(&self, iterator: &mut FragmentBoundsIterator) { + self.block_flow.iterate_through_fragment_bounds(iterator); + } } impl fmt::Show for TableRowFlow { diff --git a/components/layout/table_rowgroup.rs b/components/layout/table_rowgroup.rs index 10eb65f6c84..41f585dad0d 100644 --- a/components/layout/table_rowgroup.rs +++ b/components/layout/table_rowgroup.rs @@ -12,7 +12,7 @@ use construct::FlowConstructor; use context::LayoutContext; use flow::{TableRowGroupFlowClass, FlowClass, Flow, ImmutableFlowUtils}; use flow; -use fragment::Fragment; +use fragment::{Fragment, FragmentBoundsIterator}; use layout_debug; use model::IntrinsicISizesContribution; use table::{ColumnInlineSize, InternalTable, TableFlow}; @@ -211,6 +211,10 @@ impl Flow for TableRowGroupFlow { fn repair_style(&mut self, new_style: &Arc) { self.block_flow.repair_style(new_style) } + + fn iterate_through_fragment_bounds(&self, iterator: &mut FragmentBoundsIterator) { + self.block_flow.iterate_through_fragment_bounds(iterator); + } } impl fmt::Show for TableRowGroupFlow { diff --git a/components/layout/table_wrapper.rs b/components/layout/table_wrapper.rs index b4494b129f0..9314d99e8cc 100644 --- a/components/layout/table_wrapper.rs +++ b/components/layout/table_wrapper.rs @@ -19,7 +19,7 @@ use construct::FlowConstructor; use context::LayoutContext; use floats::FloatKind; use flow::{TableWrapperFlowClass, FlowClass, Flow, ImmutableFlowUtils}; -use fragment::Fragment; +use fragment::{Fragment, FragmentBoundsIterator}; use table::ColumnInlineSize; use wrapper::ThreadSafeLayoutNode; @@ -334,6 +334,10 @@ impl Flow for TableWrapperFlow { fn repair_style(&mut self, new_style: &Arc) { self.block_flow.repair_style(new_style) } + + fn iterate_through_fragment_bounds(&self, iterator: &mut FragmentBoundsIterator) { + self.block_flow.iterate_through_fragment_bounds(iterator); + } } impl fmt::Show for TableWrapperFlow { diff --git a/components/script/dom/node.rs b/components/script/dom/node.rs index af1afccc3b1..54bda6acc03 100644 --- a/components/script/dom/node.rs +++ b/components/script/dom/node.rs @@ -45,8 +45,7 @@ use dom::text::Text; use dom::virtualmethods::{VirtualMethods, vtable_for}; use dom::window::Window; use geom::rect::Rect; -use layout_interface::{ContentBoxResponse, ContentBoxesResponse, LayoutRPC, - LayoutChan, ReapLayoutDataMsg}; +use layout_interface::{LayoutChan, ReapLayoutDataMsg}; use devtools_traits::NodeInfo; use script_traits::UntrustedNodeAddress; use servo_util::geometry::Au; @@ -689,20 +688,11 @@ impl<'a> NodeHelpers<'a> for JSRef<'a, Node> { } fn get_bounding_content_box(self) -> Rect { - let window = window_from_node(self).root(); - let page = window.page(); - let addr = self.to_trusted_node_address(); - - let ContentBoxResponse(rect) = page.layout().content_box(addr); - rect + window_from_node(self).root().page().content_box_query(self.to_trusted_node_address()) } fn get_content_boxes(self) -> Vec> { - let window = window_from_node(self).root(); - let page = window.page(); - let addr = self.to_trusted_node_address(); - let ContentBoxesResponse(rects) = page.layout().content_boxes(addr); - rects + window_from_node(self).root().page().content_boxes_query(self.to_trusted_node_address()) } // http://dom.spec.whatwg.org/#dom-parentnode-queryselector diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs index 71c8890ab6f..c9eeedc2a2a 100644 --- a/components/script/dom/window.rs +++ b/components/script/dom/window.rs @@ -19,7 +19,7 @@ use dom::location::Location; use dom::navigator::Navigator; use dom::performance::Performance; use dom::screen::Screen; -use layout_interface::ReflowGoal; +use layout_interface::NoQuery; use page::Page; use script_task::{ExitWindowMsg, ScriptChan, TriggerLoadMsg, TriggerFragmentMsg}; use script_task::FromWindow; @@ -312,7 +312,7 @@ impl Reflectable for Window { pub trait WindowHelpers { fn reflow(self); - fn flush_layout(self, goal: ReflowGoal); + fn flush_layout(self); fn wait_until_safe_to_modify_dom(self); fn init_browser_context(self, doc: JSRef); fn load_url(self, href: DOMString); @@ -350,8 +350,8 @@ impl<'a> WindowHelpers for JSRef<'a, Window> { self.page().damage(); } - fn flush_layout(self, goal: ReflowGoal) { - self.page().flush_layout(goal); + fn flush_layout(self) { + self.page().flush_layout(NoQuery); } fn wait_until_safe_to_modify_dom(self) { diff --git a/components/script/layout_interface.rs b/components/script/layout_interface.rs index 735893f497a..2cf64f961fb 100644 --- a/components/script/layout_interface.rs +++ b/components/script/layout_interface.rs @@ -60,9 +60,9 @@ pub enum Msg { // 3) and really needs to be fast. pub trait LayoutRPC { /// Requests the dimensions of the content box, as in the `getBoundingClientRect()` call. - fn content_box(&self, node: TrustedNodeAddress) -> ContentBoxResponse; + fn content_box(&self) -> ContentBoxResponse; /// Requests the dimensions of all the content boxes, as in the `getClientRects()` call. - fn content_boxes(&self, node: TrustedNodeAddress) -> ContentBoxesResponse; + fn content_boxes(&self) -> ContentBoxesResponse; /// Requests the node containing the point of interest fn hit_test(&self, node: TrustedNodeAddress, point: Point2D) -> Result; fn mouse_over(&self, node: TrustedNodeAddress, point: Point2D) -> Result; @@ -82,6 +82,13 @@ pub enum ReflowGoal { ReflowForScriptQuery, } +/// Any query to perform with this reflow. +pub enum ReflowQueryType { + NoQuery, + ContentBoxQuery(TrustedNodeAddress), + ContentBoxesQuery(TrustedNodeAddress), +} + /// Information needed for a reflow. pub struct Reflow { /// The document node. @@ -99,7 +106,9 @@ pub struct Reflow { /// The channel that we send a notification to. pub script_join_chan: Sender<()>, /// Unique identifier - pub id: uint + pub id: uint, + /// The type of query if any to perform during this reflow. + pub query_type: ReflowQueryType, } /// Encapsulates a channel to the layout task. diff --git a/components/script/page.rs b/components/script/page.rs index 9fc7d74d2e9..01b396126ad 100644 --- a/components/script/page.rs +++ b/components/script/page.rs @@ -11,19 +11,22 @@ use dom::document::{Document, DocumentHelpers}; use dom::element::Element; use dom::node::{Node, NodeHelpers}; use dom::window::Window; -use layout_interface::{ReflowForDisplay}; -use layout_interface::{HitTestResponse, MouseOverResponse}; -use layout_interface::{GetRPCMsg, LayoutChan, LayoutRPC}; -use layout_interface::{Reflow, ReflowGoal, ReflowMsg}; +use layout_interface::{ + ContentBoxQuery, ContentBoxResponse, ContentBoxesQuery, ContentBoxesResponse, + GetRPCMsg, HitTestResponse, LayoutChan, LayoutRPC, MouseOverResponse, NoQuery, + Reflow, ReflowForDisplay, ReflowForScriptQuery, ReflowGoal, ReflowMsg, + ReflowQueryType, TrustedNodeAddress +}; use script_traits::{UntrustedNodeAddress, ScriptControlChan}; -use geom::point::Point2D; +use geom::{Point2D, Rect}; use js::rust::Cx; use servo_msg::compositor_msg::PerformingLayout; use servo_msg::compositor_msg::ScriptListener; use servo_msg::constellation_msg::{ConstellationChan, WindowSizeData}; use servo_msg::constellation_msg::{PipelineId, SubpageId}; use servo_net::resource_task::ResourceTask; +use servo_util::geometry::Au; use servo_util::str::DOMString; use servo_util::smallvec::{SmallVec1, SmallVec}; use std::cell::Cell; @@ -164,26 +167,48 @@ impl Page { } } - pub fn flush_layout(&self, goal: ReflowGoal) { - if self.damaged.get() { + pub fn flush_layout(&self, query: ReflowQueryType) { + // If we are damaged, we need to force a full reflow, so that queries interact with + // an accurate flow tree. + let (reflow_goal, force_reflow) = if self.damaged.get() { + (ReflowForDisplay, true) + } else { + match query { + ContentBoxQuery(_) | ContentBoxesQuery(_) => (ReflowForScriptQuery, true), + NoQuery => (ReflowForDisplay, false), + } + }; + + if force_reflow { let frame = self.frame(); let window = frame.as_ref().unwrap().window.root(); - self.reflow(goal, window.control_chan().clone(), window.compositor()); + self.reflow(reflow_goal, window.control_chan().clone(), window.compositor(), query); } else { self.avoided_reflows.set(self.avoided_reflows.get() + 1); } } - pub fn layout(&self) -> &LayoutRPC { - // FIXME This should probably be ReflowForQuery, not Display. All queries currently - // currently rely on the display list, which means we can't destroy it by - // doing a query reflow. - self.flush_layout(ReflowForDisplay); + pub fn layout(&self) -> &LayoutRPC { + self.flush_layout(NoQuery); self.join_layout(); //FIXME: is this necessary, or is layout_rpc's mutex good enough? let layout_rpc: &LayoutRPC = &*self.layout_rpc; layout_rpc } + pub fn content_box_query(&self, content_box_request: TrustedNodeAddress) -> Rect { + self.flush_layout(ContentBoxQuery(content_box_request)); + self.join_layout(); //FIXME: is this necessary, or is layout_rpc's mutex good enough? + let ContentBoxResponse(rect) = self.layout_rpc.content_box(); + rect + } + + pub fn content_boxes_query(&self, content_boxes_request: TrustedNodeAddress) -> Vec> { + self.flush_layout(ContentBoxesQuery(content_boxes_request)); + self.join_layout(); //FIXME: is this necessary, or is layout_rpc's mutex good enough? + let ContentBoxesResponse(rects) = self.layout_rpc.content_boxes(); + rects + } + // must handle root case separately pub fn remove(&self, id: PipelineId) -> Option> { let remove_idx = { @@ -303,7 +328,8 @@ impl Page { pub fn reflow(&self, goal: ReflowGoal, script_chan: ScriptControlChan, - compositor: &ScriptListener) { + compositor: &ScriptListener, + query_type: ReflowQueryType) { let root = match *self.frame() { None => return, @@ -349,6 +375,7 @@ impl Page { script_chan: script_chan, script_join_chan: join_chan, id: last_reflow_id.get(), + query_type: query_type, }; let LayoutChan(ref chan) = self.layout_chan; diff --git a/components/script/script_task.rs b/components/script/script_task.rs index deb6aaded80..7ef0d095545 100644 --- a/components/script/script_task.rs +++ b/components/script/script_task.rs @@ -29,7 +29,7 @@ use dom::window::{Window, WindowHelpers}; use dom::worker::{Worker, TrustedWorkerAddress}; use dom::xmlhttprequest::{TrustedXHRAddress, XMLHttpRequest, XHRProgress}; use parse::html::{InputString, InputUrl, parse_html}; -use layout_interface::{ScriptLayoutChan, LayoutChan, ReflowForDisplay}; +use layout_interface::{ScriptLayoutChan, LayoutChan, NoQuery, ReflowForDisplay}; use layout_interface; use page::{Page, IterablePage, Frame}; use timers::TimerId; @@ -815,7 +815,7 @@ impl ScriptTask { let document_as_node = NodeCast::from_ref(document_js_ref); document.content_changed(document_as_node); } - window.flush_layout(ReflowForDisplay); + window.flush_layout(); { // No more reflow required @@ -870,7 +870,7 @@ impl ScriptTask { } page.damage(); - page.reflow(ReflowForDisplay, self.control_chan.clone(), &*self.compositor); + page.reflow(ReflowForDisplay, self.control_chan.clone(), &*self.compositor, NoQuery); } /// This is the main entry point for receiving and dispatching DOM events. @@ -969,7 +969,7 @@ impl ScriptTask { let eventtarget: JSRef = EventTargetCast::from_ref(node); let _ = eventtarget.dispatch_event_with_target(None, *event); - window.flush_layout(ReflowForDisplay); + window.flush_layout(); } None => {} }