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 => {} }