From bffaad118e2e73c3fbeee4a160f2b0d7d3ff4952 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Fri, 10 Oct 2014 15:13:12 -0700 Subject: [PATCH] layout: Rewrite clipping to be per-display-item instead of having a separate `ClipDisplayItem`. We push down clipping areas during absolute position calculation. This makes display items into a flat list, improving cache locality. It dramatically simplifies the code all around. Because we need to push down clip rects even for absolutely-positioned children of non-absolutely-positioned flows, this patch alters the parallel traversal to compute absolute positions for absolutely-positioned children at the same time it computes absolute positions for other children. This doesn't seem to break anything either in theory (since the overall order remains correct) or in practice. It simplifies the parallel traversal code quite a bit. See the relevant Gecko bug: https://bugzilla.mozilla.org/show_bug.cgi?id=615734 --- components/gfx/display_list/mod.rs | 205 +++++------------- components/gfx/display_list/optimizer.rs | 31 +-- components/gfx/render_task.rs | 12 +- components/layout/block.rs | 38 ++-- components/layout/construct.rs | 1 - components/layout/flow.rs | 34 +-- components/layout/fragment.rs | 252 ++++++++++------------- components/layout/inline.rs | 49 +++-- components/layout/layout_task.rs | 90 +++----- components/layout/parallel.rs | 56 +---- components/layout/traversal.rs | 8 +- components/util/geometry.rs | 23 +++ 12 files changed, 312 insertions(+), 487 deletions(-) diff --git a/components/gfx/display_list/mod.rs b/components/gfx/display_list/mod.rs index 0614fc24a76..afa80ca477a 100644 --- a/components/gfx/display_list/mod.rs +++ b/components/gfx/display_list/mod.rs @@ -196,93 +196,35 @@ impl StackingContext { }; for item in list.into_iter() { - match item { - ClipDisplayItemClass(box ClipDisplayItem { - base: base, - children: sublist - }) => { - let sub_stacking_context = StackingContext::new(sublist); - stacking_context.merge_with_clip(sub_stacking_context, &base.bounds, base.node) + match item.base().level { + BackgroundAndBordersStackingLevel => { + stacking_context.background_and_borders.push(item) } - item => { - match item.base().level { - BackgroundAndBordersStackingLevel => { - stacking_context.background_and_borders.push(item) - } - BlockBackgroundsAndBordersStackingLevel => { - stacking_context.block_backgrounds_and_borders.push(item) - } - FloatStackingLevel => stacking_context.floats.push(item), - ContentStackingLevel => stacking_context.content.push(item), - PositionedDescendantStackingLevel(z_index) => { - match stacking_context.positioned_descendants - .iter_mut() - .find(|& &(z, _)| z_index == z) { - Some(&(_, ref mut my_list)) => { - my_list.push(item); - continue - } - None => {} - } - - let mut new_list = DisplayList::new(); - new_list.list.push(item); - stacking_context.positioned_descendants.push((z_index, new_list)) + BlockBackgroundsAndBordersStackingLevel => { + stacking_context.block_backgrounds_and_borders.push(item) + } + FloatStackingLevel => stacking_context.floats.push(item), + ContentStackingLevel => stacking_context.content.push(item), + PositionedDescendantStackingLevel(z_index) => { + match stacking_context.positioned_descendants + .iter_mut() + .find(|& &(z, _)| z_index == z) { + Some(&(_, ref mut my_list)) => { + my_list.push(item); + continue } + None => {} } + + let mut new_list = DisplayList::new(); + new_list.list.push(item); + stacking_context.positioned_descendants.push((z_index, new_list)) } } } stacking_context } - - /// Merges another stacking context into this one, with the given clipping rectangle and DOM - /// node that supplies it. - fn merge_with_clip(&mut self, - other: StackingContext, - clip_rect: &Rect, - clipping_dom_node: OpaqueNode) { - let StackingContext { - background_and_borders, - block_backgrounds_and_borders, - floats, - content, - positioned_descendants: positioned_descendants - } = other; - - let push = |destination: &mut DisplayList, source: DisplayList, level| { - if !source.is_empty() { - let base = BaseDisplayItem::new(*clip_rect, clipping_dom_node, level); - destination.push(ClipDisplayItemClass(box ClipDisplayItem::new(base, source))) - } - }; - - push(&mut self.background_and_borders, - background_and_borders, - BackgroundAndBordersStackingLevel); - push(&mut self.block_backgrounds_and_borders, - block_backgrounds_and_borders, - BlockBackgroundsAndBordersStackingLevel); - push(&mut self.floats, floats, FloatStackingLevel); - push(&mut self.content, content, ContentStackingLevel); - - for (z_index, list) in positioned_descendants.into_iter() { - match self.positioned_descendants - .iter_mut() - .find(|& &(existing_z_index, _)| z_index == existing_z_index) { - Some(&(_, ref mut existing_list)) => { - push(existing_list, list, PositionedDescendantStackingLevel(z_index)); - continue - } - None => {} - } - - let mut new_list = DisplayList::new(); - push(&mut new_list, list, PositionedDescendantStackingLevel(z_index)); - self.positioned_descendants.push((z_index, new_list)); - } - } } /// Which level to place backgrounds and borders in. @@ -342,11 +284,13 @@ impl DisplayList { /// Draws the display list into the given render context. The display list must be flattened /// first for correct painting. - pub fn draw_into_context(&self, render_context: &mut RenderContext, - current_transform: &Matrix2D) { + pub fn draw_into_context(&self, + render_context: &mut RenderContext, + current_transform: &Matrix2D, + current_clip_rect: &Rect) { debug!("Beginning display list."); for item in self.list.iter() { - item.draw_into_context(render_context, current_transform) + item.draw_into_context(render_context, current_transform, current_clip_rect) } debug!("Ending display list."); } @@ -356,12 +300,6 @@ impl DisplayList { ParentDisplayItemIterator(self.list.iter()) } - /// Returns true if this list is empty and false otherwise. - #[inline] - fn is_empty(&self) -> bool { - self.list.is_empty() - } - /// Flattens a display list into a display list with a single stacking level according to the /// steps in CSS 2.1 ยง E.2. /// @@ -421,10 +359,6 @@ impl DisplayList { fn set_stacking_level(&mut self, new_level: StackingLevel) { for item in self.list.iter_mut() { item.mut_base().level = new_level; - match item.mut_sublist() { - None => {} - Some(sublist) => sublist.set_stacking_level(new_level), - } } } } @@ -437,7 +371,6 @@ pub enum DisplayItem { ImageDisplayItemClass(Box), BorderDisplayItemClass(Box), LineDisplayItemClass(Box), - ClipDisplayItemClass(Box), /// A pseudo-display item that exists only so that queries like `ContentBoxQuery` and /// `ContentBoxesQuery` can be answered. @@ -450,9 +383,7 @@ pub enum DisplayItem { /// Information common to all display items. #[deriving(Clone)] pub struct BaseDisplayItem { - /// The boundaries of the display item. - /// - /// TODO: Which coordinate system should this use? + /// The boundaries of the display item, in layer coordinates. pub bounds: Rect, /// The originating DOM node. @@ -460,14 +391,22 @@ pub struct BaseDisplayItem { /// The stacking level in which this display item lives. pub level: StackingLevel, + + /// The rectangle to clip to. + /// + /// TODO(pcwalton): Eventually, to handle `border-radius`, this will (at least) need to grow + /// the ability to describe rounded rectangles. + pub clip_rect: Rect, } impl BaseDisplayItem { - pub fn new(bounds: Rect, node: OpaqueNode, level: StackingLevel) -> BaseDisplayItem { + pub fn new(bounds: Rect, node: OpaqueNode, level: StackingLevel, clip_rect: Rect) + -> BaseDisplayItem { BaseDisplayItem { bounds: bounds, node: node, level: level, + clip_rect: clip_rect, } } } @@ -544,25 +483,6 @@ pub struct LineDisplayItem { pub style: border_style::T } -/// Clips a list of child display items to this display item's boundaries. -#[deriving(Clone)] -pub struct ClipDisplayItem { - /// The base information. - pub base: BaseDisplayItem, - - /// The child nodes. - pub children: DisplayList, -} - -impl ClipDisplayItem { - pub fn new(base: BaseDisplayItem, children: DisplayList) -> ClipDisplayItem { - ClipDisplayItem { - base: base, - children: children, - } - } -} - pub enum DisplayItemIterator<'a> { EmptyDisplayItemIterator, ParentDisplayItemIterator(dlist::Items<'a,DisplayItem>), @@ -580,24 +500,24 @@ impl<'a> Iterator<&'a DisplayItem> for DisplayItemIterator<'a> { impl DisplayItem { /// Renders this display item into the given render context. - fn draw_into_context(&self, render_context: &mut RenderContext, - current_transform: &Matrix2D) { + fn draw_into_context(&self, + render_context: &mut RenderContext, + current_transform: &Matrix2D, + current_clip_rect: &Rect) { // This should have been flattened to the content stacking level first. assert!(self.base().level == ContentStackingLevel); + let clip_rect = &self.base().clip_rect; + let need_to_clip = current_clip_rect != clip_rect; + if need_to_clip { + render_context.draw_push_clip(clip_rect); + } + match *self { SolidColorDisplayItemClass(ref solid_color) => { render_context.draw_solid_color(&solid_color.base.bounds, solid_color.color) } - ClipDisplayItemClass(ref clip) => { - render_context.draw_push_clip(&clip.base.bounds); - for item in clip.children.iter() { - (*item).draw_into_context(render_context, current_transform); - } - render_context.draw_pop_clip(); - } - TextDisplayItemClass(ref text) => { debug!("Drawing text at {}.", text.base.bounds); @@ -688,6 +608,10 @@ impl DisplayItem { PseudoDisplayItemClass(_) => {} } + + if need_to_clip { + render_context.draw_pop_clip(); + } } pub fn base<'a>(&'a self) -> &'a BaseDisplayItem { @@ -697,7 +621,6 @@ impl DisplayItem { ImageDisplayItemClass(ref image_item) => &image_item.base, BorderDisplayItemClass(ref border) => &border.base, LineDisplayItemClass(ref line) => &line.base, - ClipDisplayItemClass(ref clip) => &clip.base, PseudoDisplayItemClass(ref base) => &**base, } } @@ -709,7 +632,6 @@ impl DisplayItem { ImageDisplayItemClass(ref mut image_item) => &mut image_item.base, BorderDisplayItemClass(ref mut border) => &mut border.base, LineDisplayItemClass(ref mut line) => &mut line.base, - ClipDisplayItemClass(ref mut clip) => &mut clip.base, PseudoDisplayItemClass(ref mut base) => &mut **base, } } @@ -718,40 +640,12 @@ impl DisplayItem { self.base().bounds } - pub fn children<'a>(&'a self) -> DisplayItemIterator<'a> { - match *self { - ClipDisplayItemClass(ref clip) => ParentDisplayItemIterator(clip.children.list.iter()), - SolidColorDisplayItemClass(..) | - TextDisplayItemClass(..) | - ImageDisplayItemClass(..) | - BorderDisplayItemClass(..) | - LineDisplayItemClass(..) | - PseudoDisplayItemClass(..) => EmptyDisplayItemIterator, - } - } - - /// Returns a mutable reference to the sublist contained within this display list item, if any. - fn mut_sublist<'a>(&'a mut self) -> Option<&'a mut DisplayList> { - match *self { - ClipDisplayItemClass(ref mut clip) => Some(&mut clip.children), - SolidColorDisplayItemClass(..) | - TextDisplayItemClass(..) | - ImageDisplayItemClass(..) | - BorderDisplayItemClass(..) | - LineDisplayItemClass(..) | - PseudoDisplayItemClass(..) => None, - } - } - pub fn debug_with_level(&self, level: uint) { let mut indent = String::new(); for _ in range(0, level) { indent.push_str("| ") } debug!("{}+ {}", indent, self); - for child in self.children() { - child.debug_with_level(level + 1); - } } } @@ -764,7 +658,6 @@ impl fmt::Show for DisplayItem { ImageDisplayItemClass(_) => "Image", BorderDisplayItemClass(_) => "Border", LineDisplayItemClass(_) => "Line", - ClipDisplayItemClass(_) => "Clip", PseudoDisplayItemClass(_) => "Pseudo", }, self.base().bounds, diff --git a/components/gfx/display_list/optimizer.rs b/components/gfx/display_list/optimizer.rs index 5e32238704c..e508e351010 100644 --- a/components/gfx/display_list/optimizer.rs +++ b/components/gfx/display_list/optimizer.rs @@ -2,9 +2,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use display_list::{BorderDisplayItemClass, ClipDisplayItem, ClipDisplayItemClass, DisplayItem}; -use display_list::{DisplayList, ImageDisplayItemClass, LineDisplayItemClass}; -use display_list::{PseudoDisplayItemClass, SolidColorDisplayItemClass, TextDisplayItemClass}; +use display_list::{DisplayItem, DisplayList}; use collections::dlist::DList; use geom::rect::Rect; @@ -45,28 +43,11 @@ impl DisplayListOptimizer { fn process_display_item(&self, display_item: &DisplayItem) -> Option { // Eliminate display items outside the visible region. - if !self.visible_rect.intersects(&display_item.base().bounds) { - return None - } - - // Recur. - match *display_item { - ClipDisplayItemClass(ref clip) => { - let new_children = self.process_display_list(&clip.children); - if new_children.is_empty() { - return None - } - Some(ClipDisplayItemClass(box ClipDisplayItem { - base: clip.base.clone(), - children: new_children, - })) - } - - BorderDisplayItemClass(_) | ImageDisplayItemClass(_) | LineDisplayItemClass(_) | - PseudoDisplayItemClass(_) | SolidColorDisplayItemClass(_) | - TextDisplayItemClass(_) => { - Some((*display_item).clone()) - } + if !self.visible_rect.intersects(&display_item.base().bounds) || + !self.visible_rect.intersects(&display_item.base().clip_rect) { + None + } else { + Some((*display_item).clone()) } } } diff --git a/components/gfx/render_task.rs b/components/gfx/render_task.rs index 92d9a2de2b0..8ddc5f232b2 100644 --- a/components/gfx/render_task.rs +++ b/components/gfx/render_task.rs @@ -25,13 +25,14 @@ use servo_msg::compositor_msg::{LayerMetadata, RenderListener, RenderingRenderSt use servo_msg::constellation_msg::{ConstellationChan, Failure, FailureMsg, PipelineId}; use servo_msg::constellation_msg::{RendererReadyMsg}; use servo_msg::platform::surface::NativeSurfaceAzureMethods; -use servo_util::geometry; +use servo_util::geometry::{Au, mod}; use servo_util::opts::Opts; use servo_util::smallvec::{SmallVec, SmallVec1}; use servo_util::task::spawn_named_with_send_on_failure; use servo_util::time::{TimeProfilerChan, profile}; use servo_util::time; use std::comm::{Receiver, Sender, channel}; +use std::i32; use sync::Arc; use font_cache_task::FontCacheTask; @@ -356,8 +357,13 @@ impl RenderTask { ctx.clear(); // Draw the display list. - profile(time::RenderingDrawingCategory, None, self.time_profiler_chan.clone(), || { - display_list.draw_into_context(&mut ctx, &matrix); + profile(time::RenderingDrawingCategory, + None, + self.time_profiler_chan.clone(), + || { + let clip_rect = Rect(Point2D(Au(i32::MIN), Au(i32::MIN)), + Size2D(Au(i32::MAX), Au(i32::MAX))); + display_list.draw_into_context(&mut ctx, &matrix, &clip_rect); ctx.draw_target.flush(); }); } diff --git a/components/layout/block.rs b/components/layout/block.rs index 5fe80ecc8ab..53eecad09b6 100644 --- a/components/layout/block.rs +++ b/components/layout/block.rs @@ -37,7 +37,7 @@ use gfx::display_list::{RootOfStackingContextLevel}; use gfx::render_task::RenderLayer; use serialize::{Encoder, Encodable}; use servo_msg::compositor_msg::{FixedPosition, LayerId, Scrollable}; -use servo_util::geometry::{Au, MAX_AU}; +use servo_util::geometry::{Au, MAX_AU, MAX_RECT}; use servo_util::logical_geometry::{LogicalPoint, LogicalRect, LogicalSize}; use std::cmp::{max, min}; use std::fmt; @@ -1078,18 +1078,19 @@ impl BlockFlow { fn build_display_list_block_common(&mut self, layout_context: &LayoutContext, background_border_level: BackgroundAndBorderLevel) { - let rel_offset = + let relative_offset = self.fragment.relative_position(&self.base - .absolute_position_info - .relative_containing_block_size); + .absolute_position_info + .relative_containing_block_size); // Add the box that starts the block context. let mut display_list = DisplayList::new(); - let mut accumulator = self.fragment.build_display_list( - &mut display_list, - layout_context, - self.base.abs_position.add_size(&rel_offset.to_physical(self.base.writing_mode)), - background_border_level); + self.fragment.build_display_list(&mut display_list, + layout_context, + self.base.abs_position.add_size( + &relative_offset.to_physical(self.base.writing_mode)), + background_border_level, + &self.base.clip_rect); let mut child_layers = DList::new(); for kid in self.base.child_iter() { @@ -1098,19 +1099,21 @@ impl BlockFlow { continue } - accumulator.push_child(&mut display_list, kid); + display_list.push_all_move(mem::replace(&mut flow::mut_base(kid).display_list, + DisplayList::new())); child_layers.append(mem::replace(&mut flow::mut_base(kid).layers, DList::new())) } // Process absolute descendant links. for abs_descendant_link in self.base.abs_descendants.iter() { // TODO(pradeep): Send in our absolute position directly. - accumulator.push_child(&mut display_list, abs_descendant_link); + display_list.push_all_move(mem::replace( + &mut flow::mut_base(abs_descendant_link).display_list, + DisplayList::new())); child_layers.append(mem::replace(&mut flow::mut_base(abs_descendant_link).layers, DList::new())); } - accumulator.finish(&mut display_list); self.base.display_list = display_list; self.base.layers = child_layers } @@ -1696,6 +1699,10 @@ impl Flow for BlockFlow { // FIXME(#2795): Get the real container size let container_size = Size2D::zero(); + if self.is_root() { + self.base.clip_rect = MAX_RECT; + } + if self.is_absolutely_positioned() { let position_start = self.base.position.start.to_physical( self.base.writing_mode, container_size); @@ -1737,8 +1744,11 @@ impl Flow for BlockFlow { absolute_position_info.layers_needed_for_positioned_flows = self.base.flags.layers_needed_for_descendants(); - // Process children. + // Compute the clipping rectangle for children. let this_position = self.base.abs_position; + let clip_rect = self.fragment.clip_rect_for_children(self.base.clip_rect, this_position); + + // Process children. let writing_mode = self.base.writing_mode; for kid in self.base.child_iter() { if !kid.is_absolutely_positioned() { @@ -1749,6 +1759,8 @@ impl Flow for BlockFlow { container_size); kid_base.absolute_position_info = absolute_position_info } + + flow::mut_base(kid).clip_rect = clip_rect } // Process absolute descendant links. diff --git a/components/layout/construct.rs b/components/layout/construct.rs index c45c26d781c..ffdf535874e 100644 --- a/components/layout/construct.rs +++ b/components/layout/construct.rs @@ -1204,7 +1204,6 @@ impl FlowConstructionUtils for FlowRef { base.children.push_back(new_child); let _ = base.parallel.children_count.fetch_add(1, Relaxed); - let _ = base.parallel.children_and_absolute_descendant_count.fetch_add(1, Relaxed); } /// Finishes a flow. Once a flow is finished, no more child flows or fragments may be added to diff --git a/components/layout/flow.rs b/components/layout/flow.rs index a8711f62965..4520bee4eff 100644 --- a/components/layout/flow.rs +++ b/components/layout/flow.rs @@ -46,7 +46,7 @@ use table_wrapper::TableWrapperFlow; use wrapper::ThreadSafeLayoutNode; use collections::dlist::DList; -use geom::Point2D; +use geom::{Point2D, Rect, Size2D}; use gfx::display_list::DisplayList; use gfx::render_task::RenderLayer; use serialize::{Encoder, Encodable}; @@ -59,7 +59,7 @@ use std::num::Zero; use std::fmt; use std::iter::Zip; use std::raw; -use std::sync::atomics::{AtomicUint, Relaxed, SeqCst}; +use std::sync::atomics::{AtomicUint, SeqCst}; use std::slice::MutItems; use style::computed_values::{clear, float, position, text_align}; @@ -168,14 +168,14 @@ pub trait Flow: fmt::Show + ToString + Sync { fail!("called col_inline_sizes() on an other flow than table-row/table-rowgroup/table") } - /// If this is a table row flow or table rowgroup flow or table flow, returns column min inline-sizes. - /// Fails otherwise. + /// If this is a table row flow or table rowgroup flow or table flow, returns column min + /// inline-sizes. Fails otherwise. fn col_min_inline_sizes<'a>(&'a self) -> &'a Vec { fail!("called col_min_inline_sizes() on an other flow than table-row/table-rowgroup/table") } - /// If this is a table row flow or table rowgroup flow or table flow, returns column min inline-sizes. - /// Fails otherwise. + /// If this is a table row flow or table rowgroup flow or table flow, returns column min + /// inline-sizes. Fails otherwise. fn col_pref_inline_sizes<'a>(&'a self) -> &'a Vec { fail!("called col_pref_inline_sizes() on an other flow than table-row/table-rowgroup/table") } @@ -230,8 +230,8 @@ pub trait Flow: fmt::Show + ToString + Sync { } fn compute_collapsible_block_start_margin(&mut self, - _layout_context: &mut LayoutContext, - _margin_collapse_info: &mut MarginCollapseInfo) { + _layout_context: &mut LayoutContext, + _margin_collapse_info: &mut MarginCollapseInfo) { // The default implementation is a no-op. } @@ -679,8 +679,10 @@ pub type DescendantOffsetIter<'a> = Zip, MutItems<'a, Au>>; pub struct AbsolutePositionInfo { /// The size of the containing block for relatively-positioned descendants. pub relative_containing_block_size: LogicalSize, + /// The position of the absolute containing block. pub absolute_containing_block_position: Point2D, + /// Whether the absolute containing block forces positioned descendants to be layerized. /// /// FIXME(pcwalton): Move into `FlowFlags`. @@ -770,6 +772,12 @@ pub struct BaseFlow { /// FIXME(pcwalton): Merge with `absolute_static_i_offset` and `fixed_static_i_offset` above? pub absolute_position_info: AbsolutePositionInfo, + /// The clipping rectangle for this flow and its descendants, in layer coordinates. + /// + /// TODO(pcwalton): When we have `border-radius` this will need to at least support rounded + /// rectangles. + pub clip_rect: Rect, + /// The unflattened display items for this flow. pub display_list: DisplayList, @@ -785,11 +793,10 @@ pub struct BaseFlow { impl fmt::Show for BaseFlow { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, - "@ {}, CC {}, ADC {}, CADC {}", + "@ {}, CC {}, ADC {}", self.position, self.parallel.children_count.load(SeqCst), - self.abs_descendants.len(), - self.parallel.children_and_absolute_descendant_count.load(SeqCst)) + self.abs_descendants.len()) } } @@ -851,6 +858,7 @@ impl BaseFlow { display_list: DisplayList::new(), layers: DList::new(), absolute_position_info: AbsolutePositionInfo::new(writing_mode), + clip_rect: Rect(Zero::zero(), Size2D(Au(0), Au(0))), flags: FlowFlags::new(), writing_mode: writing_mode, @@ -1210,10 +1218,6 @@ impl MutableOwnedFlowUtils for FlowRef { let block = self.get_mut().as_block(); block.base.abs_descendants = abs_descendants; - block.base - .parallel - .children_and_absolute_descendant_count - .fetch_add(block.base.abs_descendants.len() as int, Relaxed); for descendant_link in block.base.abs_descendants.iter() { let base = mut_base(descendant_link); diff --git a/components/layout/fragment.rs b/components/layout/fragment.rs index 87b9dded311..4853ded381b 100644 --- a/components/layout/fragment.rs +++ b/components/layout/fragment.rs @@ -11,7 +11,6 @@ use construct::FlowConstructor; use context::LayoutContext; use floats::{ClearBoth, ClearLeft, ClearRight, ClearType}; use flow::Flow; -use flow; use flow_ref::FlowRef; use inline::{InlineFragmentContext, InlineMetrics}; use layout_debug; @@ -25,13 +24,12 @@ use geom::{Point2D, Rect, Size2D, SideOffsets2D}; use geom::approxeq::ApproxEq; use gfx::color::rgb; use gfx::display_list::{BackgroundAndBorderLevel, BaseDisplayItem, BorderDisplayItem}; -use gfx::display_list::{BorderDisplayItemClass, ClipDisplayItem, ClipDisplayItemClass}; -use gfx::display_list::{ContentStackingLevel, DisplayItem, DisplayList, ImageDisplayItem}; -use gfx::display_list::{ImageDisplayItemClass, LineDisplayItem}; +use gfx::display_list::{BorderDisplayItemClass, ContentStackingLevel, DisplayList}; +use gfx::display_list::{ImageDisplayItem, ImageDisplayItemClass, LineDisplayItem}; use gfx::display_list::{LineDisplayItemClass, OpaqueNode, PseudoDisplayItemClass}; -use gfx::display_list::{SolidColorDisplayItem, SolidColorDisplayItemClass, StackingLevel}; -use gfx::display_list::{TextDisplayItem, TextDisplayItemClass}; -use gfx::display_list::{Upright, SidewaysLeft, SidewaysRight}; +use gfx::display_list::{SidewaysLeft, SidewaysRight, SolidColorDisplayItem}; +use gfx::display_list::{SolidColorDisplayItemClass, StackingLevel, TextDisplayItem}; +use gfx::display_list::{TextDisplayItemClass, Upright}; use gfx::font::FontStyle; use gfx::text::glyph::CharIndex; use gfx::text::text_run::TextRun; @@ -40,7 +38,7 @@ use serialize::{Encodable, Encoder}; use servo_msg::constellation_msg::{ConstellationChan, FrameRectMsg, PipelineId, SubpageId}; use servo_net::image::holder::ImageHolder; use servo_net::local_image_cache::LocalImageCache; -use servo_util::geometry::Au; +use servo_util::geometry::{Au, ZERO_RECT}; use servo_util::geometry; use servo_util::logical_geometry::{LogicalRect, LogicalSize, LogicalMargin, WritingMode}; use servo_util::range::*; @@ -49,7 +47,6 @@ use servo_util::str::is_whitespace; use std::cmp::{max, min}; use std::fmt; use std::from_str::FromStr; -use std::mem; use std::num::Zero; use style::{ComputedValues, TElement, TNode, cascade_anonymous, RGBA}; use style::computed_values::{LengthOrPercentage, LengthOrPercentageOrAuto}; @@ -60,24 +57,26 @@ use style::computed_values::{text_decoration, vertical_align, visibility, white_ use sync::{Arc, Mutex}; use url::Url; -/// Fragments (`struct Fragment`) are the leaves of the layout tree. They cannot position themselves. In -/// general, fragments do not have a simple correspondence with CSS fragments in the specification: +/// Fragments (`struct Fragment`) are the leaves of the layout tree. They cannot position +/// themselves. In general, fragments do not have a simple correspondence with CSS fragments in the +/// specification: /// /// * Several fragments may correspond to the same CSS box or DOM node. For example, a CSS text box /// broken across two lines is represented by two fragments. /// -/// * Some CSS fragments are not created at all, such as some anonymous block fragments induced by inline -/// fragments with block-level sibling fragments. In that case, Servo uses an `InlineFlow` with -/// `BlockFlow` siblings; the `InlineFlow` is block-level, but not a block container. It is -/// positioned as if it were a block fragment, but its children are positioned according to inline -/// flow. +/// * Some CSS fragments are not created at all, such as some anonymous block fragments induced by +/// inline fragments with block-level sibling fragments. In that case, Servo uses an `InlineFlow` +/// with `BlockFlow` siblings; the `InlineFlow` is block-level, but not a block container. It is +/// positioned as if it were a block fragment, but its children are positioned according to +/// inline flow. /// /// A `GenericFragment` is an empty fragment that contributes only borders, margins, padding, and /// backgrounds. It is analogous to a CSS nonreplaced content box. /// -/// A fragment's type influences how its styles are interpreted during layout. For example, replaced -/// content such as images are resized differently from tables, text, or other content. Different -/// types of fragments may also contain custom data; for example, text fragments contain text. +/// A fragment's type influences how its styles are interpreted during layout. For example, +/// replaced content such as images are resized differently from tables, text, or other content. +/// Different types of fragments may also contain custom data; for example, text fragments contain +/// text. /// /// FIXME(#2260, pcwalton): This can be slimmed down some. #[deriving(Clone)] @@ -787,7 +786,8 @@ impl Fragment { list: &mut DisplayList, layout_context: &LayoutContext, level: StackingLevel, - absolute_bounds: &Rect) { + absolute_bounds: &Rect, + clip_rect: &Rect) { // FIXME: This causes a lot of background colors to be displayed when they are clearly not // needed. We could use display list optimization to clean this up, but it still seems // inefficient. What we really want is something like "nearest ancestor element that @@ -795,7 +795,7 @@ impl Fragment { let background_color = style.resolve_color(style.get_background().background_color); if !background_color.alpha.approx_eq(&0.0) { let display_item = box SolidColorDisplayItem { - base: BaseDisplayItem::new(*absolute_bounds, self.node, level), + base: BaseDisplayItem::new(*absolute_bounds, self.node, level, *clip_rect), color: background_color.to_gfx_color(), }; @@ -828,12 +828,10 @@ impl Fragment { let image_height = Au::from_px(image.height as int); let mut bounds = *absolute_bounds; - // Add clip item. + // Clip. + // // TODO: Check the bounds to see if a clip item is actually required. - let mut clip_display_item = box ClipDisplayItem { - base: BaseDisplayItem::new(bounds, self.node, level), - children: DisplayList::new(), - }; + let clip_rect = clip_rect.intersection(&bounds).unwrap_or(ZERO_RECT); // Use background-attachment to get the initial virtual origin let (virtual_origin_x, virtual_origin_y) = match background.background_attachment { @@ -884,14 +882,12 @@ impl Fragment { // Create the image display item. let image_display_item = ImageDisplayItemClass(box ImageDisplayItem { - base: BaseDisplayItem::new(bounds, self.node, level), + base: BaseDisplayItem::new(bounds, self.node, level, clip_rect), image: image.clone(), stretch_size: Size2D(Au::from_px(image.width as int), Au::from_px(image.height as int)), }); - - clip_display_item.children.push(image_display_item); - list.push(ClipDisplayItemClass(clip_display_item)) + list.push(image_display_item) } /// Adds the display items necessary to paint the borders of this fragment to a display list if @@ -900,7 +896,8 @@ impl Fragment { style: &ComputedValues, list: &mut DisplayList, abs_bounds: &Rect, - level: StackingLevel) { + level: StackingLevel, + clip_rect: &Rect) { let border = style.logical_border_width(); if border.is_zero() { return @@ -913,7 +910,7 @@ impl Fragment { // Append the border to the display list. let border_display_item = box BorderDisplayItem { - base: BaseDisplayItem::new(*abs_bounds, self.node, level), + base: BaseDisplayItem::new(*abs_bounds, self.node, level, *clip_rect), border: border.to_physical(style.writing_mode), color: SideOffsets2D::new(top_color.to_gfx_color(), right_color.to_gfx_color(), @@ -929,9 +926,10 @@ impl Fragment { } fn build_debug_borders_around_text_fragments(&self, - display_list: &mut DisplayList, - flow_origin: Point2D, - text_fragment: &ScannedTextFragmentInfo) { + display_list: &mut DisplayList, + flow_origin: Point2D, + text_fragment: &ScannedTextFragmentInfo, + clip_rect: &Rect) { // FIXME(#2795): Get the real container size let container_size = Size2D::zero(); // Fragment position wrt to the owning flow. @@ -942,7 +940,10 @@ impl Fragment { // Compute the text fragment bounds and draw a border surrounding them. let border_display_item = box BorderDisplayItem { - base: BaseDisplayItem::new(absolute_fragment_bounds, self.node, ContentStackingLevel), + base: BaseDisplayItem::new(absolute_fragment_bounds, + self.node, + ContentStackingLevel, + *clip_rect), border: SideOffsets2D::new_all_same(Au::from_px(1)), color: SideOffsets2D::new_all_same(rgb(0, 0, 200)), style: SideOffsets2D::new_all_same(border_style::solid) @@ -958,7 +959,7 @@ impl Fragment { baseline.origin = baseline.origin + flow_origin; let line_display_item = box LineDisplayItem { - base: BaseDisplayItem::new(baseline, self.node, ContentStackingLevel), + base: BaseDisplayItem::new(baseline, self.node, ContentStackingLevel, *clip_rect), color: rgb(0, 200, 0), style: border_style::dashed, }; @@ -966,8 +967,9 @@ impl Fragment { } fn build_debug_borders_around_fragment(&self, - display_list: &mut DisplayList, - flow_origin: Point2D) { + display_list: &mut DisplayList, + flow_origin: Point2D, + clip_rect: &Rect) { // FIXME(#2795): Get the real container size let container_size = Size2D::zero(); // Fragment position wrt to the owning flow. @@ -978,7 +980,10 @@ impl Fragment { // This prints a debug border around the border of this fragment. let border_display_item = box BorderDisplayItem { - base: BaseDisplayItem::new(absolute_fragment_bounds, self.node, ContentStackingLevel), + base: BaseDisplayItem::new(absolute_fragment_bounds, + self.node, + ContentStackingLevel, + *clip_rect), border: SideOffsets2D::new_all_same(Au::from_px(1)), color: SideOffsets2D::new_all_same(rgb(0, 0, 200)), style: SideOffsets2D::new_all_same(border_style::solid) @@ -994,12 +999,13 @@ impl Fragment { /// * `layout_context`: The layout context. /// * `dirty`: The dirty rectangle in the coordinate system of the owning flow. /// * `flow_origin`: Position of the origin of the owning flow wrt the display list root flow. + /// * `clip_rect`: The rectangle to clip the display items to. pub fn build_display_list(&mut self, display_list: &mut DisplayList, layout_context: &LayoutContext, flow_origin: Point2D, - background_and_border_level: BackgroundAndBorderLevel) - -> ChildDisplayListAccumulator { + background_and_border_level: BackgroundAndBorderLevel, + clip_rect: &Rect) { // FIXME(#2795): Get the real container size let container_size = Size2D::zero(); let rect_to_absolute = |writing_mode: WritingMode, logical_rect: LogicalRect| { @@ -1016,22 +1022,13 @@ impl Fragment { layout_context.shared.dirty, flow_origin); - let may_need_clip = match self.specific { - ScannedTextFragment(_) => false, - _ => true, - }; - let mut accumulator = ChildDisplayListAccumulator::new(self.style(), - absolute_fragment_bounds, - self.node, - ContentStackingLevel, - may_need_clip); if self.style().get_inheritedbox().visibility != visibility::visible { - return accumulator + return } if !absolute_fragment_bounds.intersects(&layout_context.shared.dirty) { debug!("Fragment::build_display_list: Did not intersect..."); - return accumulator + return } debug!("Fragment::build_display_list: intersected. Adding display item..."); @@ -1043,7 +1040,8 @@ impl Fragment { // Add a pseudo-display item for content box queries. This is a very bogus thing to do. let base_display_item = box BaseDisplayItem::new(absolute_fragment_bounds, self.node, - level); + level, + *clip_rect); display_list.push(PseudoDisplayItemClass(base_display_item)); // Add the background to the list, if applicable. @@ -1055,7 +1053,8 @@ impl Fragment { display_list, layout_context, level, - &absolute_fragment_bounds); + &absolute_fragment_bounds, + clip_rect); } } None => {} @@ -1068,7 +1067,8 @@ impl Fragment { display_list, layout_context, level, - &absolute_fragment_bounds); + &absolute_fragment_bounds, + clip_rect); } } @@ -1082,7 +1082,8 @@ impl Fragment { &**style, display_list, &absolute_fragment_bounds, - level); + level, + clip_rect); } } None => {} @@ -1090,10 +1091,12 @@ impl Fragment { match self.specific { ScannedTextFragment(_) => {}, _ => { - self.build_display_list_for_borders_if_applicable(&*self.style, - display_list, - &absolute_fragment_bounds, - level); + self.build_display_list_for_borders_if_applicable( + &*self.style, + display_list, + &absolute_fragment_bounds, + level, + clip_rect); } } } @@ -1125,15 +1128,17 @@ impl Fragment { }; let text_display_item = box TextDisplayItem { - base: BaseDisplayItem::new( - absolute_content_box, self.node, ContentStackingLevel), + base: BaseDisplayItem::new(absolute_content_box, + self.node, + ContentStackingLevel, + *clip_rect), text_run: text_fragment.run.clone(), range: text_fragment.range, text_color: self.style().get_color().color.to_gfx_color(), orientation: orientation, baseline_origin: baseline_origin, }; - accumulator.push(display_list, TextDisplayItemClass(text_display_item)); + display_list.push(TextDisplayItemClass(text_display_item)); // Create display items for text decoration { @@ -1141,14 +1146,17 @@ impl Fragment { match maybe_color { None => {}, Some(color) => { - accumulator.push(display_list, SolidColorDisplayItemClass( - box SolidColorDisplayItem { - base: BaseDisplayItem::new( - rect_to_absolute(self.style.writing_mode, rect()), - self.node, ContentStackingLevel), - color: color.to_gfx_color(), - } - )); + display_list.push(SolidColorDisplayItemClass( + box SolidColorDisplayItem { + base: BaseDisplayItem::new( + rect_to_absolute( + self.style.writing_mode, + rect()), + self.node, + ContentStackingLevel, + *clip_rect), + color: color.to_gfx_color(), + })); } } }; @@ -1181,15 +1189,19 @@ impl Fragment { // FIXME(#2263, pcwalton): This is a bit of an abuse of the logging infrastructure. // We should have a real `SERVO_DEBUG` system. debug!("{:?}", self.build_debug_borders_around_text_fragments(display_list, - flow_origin, - text_fragment)) + flow_origin, + text_fragment, + clip_rect)) } GenericFragment | IframeFragment(..) | TableFragment | TableCellFragment | TableRowFragment | TableWrapperFragment | InlineBlockFragment(_) | InputFragment(_) | InlineAbsoluteHypotheticalFragment(_) => { // FIXME(pcwalton): This is a bit of an abuse of the logging infrastructure. We // should have a real `SERVO_DEBUG` system. - debug!("{:?}", self.build_debug_borders_around_fragment(display_list, flow_origin)) + debug!("{:?}", + self.build_debug_borders_around_fragment(display_list, + flow_origin, + clip_rect)) } ImageFragment(ref mut image_fragment) => { let image_ref = &mut image_fragment.image; @@ -1201,12 +1213,13 @@ impl Fragment { let image_display_item = box ImageDisplayItem { base: BaseDisplayItem::new(absolute_content_box, self.node, - ContentStackingLevel), + ContentStackingLevel, + *clip_rect), image: image.clone(), stretch_size: absolute_content_box.size, }; - accumulator.push(display_list, ImageDisplayItemClass(image_display_item)) + display_list.push(ImageDisplayItemClass(image_display_item)) } None => { // No image data at all? Do nothing. @@ -1220,7 +1233,8 @@ impl Fragment { // FIXME(pcwalton): This is a bit of an abuse of the logging // infrastructure. We should have a real `SERVO_DEBUG` system. - debug!("{:?}", self.build_debug_borders_around_fragment(display_list, flow_origin)) + debug!("{:?}", + self.build_debug_borders_around_fragment(display_list, flow_origin, clip_rect)) // If this is an iframe, then send its position and size up to the constellation. // @@ -1240,8 +1254,6 @@ impl Fragment { } _ => {} } - - accumulator } /// Returns the intrinsic inline-sizes of this fragment. @@ -1800,6 +1812,28 @@ impl Fragment { _ => {} } } + + pub fn clip_rect_for_children(&self, current_clip_rect: Rect, flow_origin: Point2D) + -> Rect { + // Don't clip if we're text. + match self.specific { + ScannedTextFragment(_) => return current_clip_rect, + _ => {} + } + + // Only clip if `overflow` tells us to. + match self.style.get_box().overflow { + overflow::hidden | overflow::auto | overflow::scroll => {} + _ => return current_clip_rect, + } + + // Create a new clip rect. + // + // FIXME(#2795): Get the real container size. + let physical_rect = self.border_box.to_physical(self.style.writing_mode, Size2D::zero()); + current_clip_rect.intersection(&Rect(physical_rect.origin + flow_origin, + physical_rect.size)).unwrap_or(ZERO_RECT) + } } impl fmt::Show for Fragment { @@ -1828,61 +1862,3 @@ impl fmt::Show for Fragment { } } -/// An object that accumulates display lists of child flows, applying a clipping rect if necessary. -pub struct ChildDisplayListAccumulator { - clip_display_item: Option>, -} - -impl ChildDisplayListAccumulator { - /// Creates a `ChildDisplayListAccumulator` from the `overflow` property in the given style. - fn new(style: &ComputedValues, - bounds: Rect, - node: OpaqueNode, - level: StackingLevel, - may_need_clip: bool) - -> ChildDisplayListAccumulator { - ChildDisplayListAccumulator { - clip_display_item: match (may_need_clip, style.get_box().overflow) { - (true, overflow::hidden) | (true, overflow::auto) | (true, overflow::scroll) => { - Some(box ClipDisplayItem { - base: BaseDisplayItem::new(bounds, node, level), - children: DisplayList::new(), - }) - }, - (false, _) | (_, overflow::visible) => None, - } - } - } - - /// Pushes the given display item onto this display list. - pub fn push(&mut self, parent_display_list: &mut DisplayList, item: DisplayItem) { - match self.clip_display_item { - None => parent_display_list.push(item), - Some(ref mut clip_display_item) => clip_display_item.children.push(item), - } - } - - /// Pushes the display items from the given child onto this display list. - pub fn push_child(&mut self, parent_display_list: &mut DisplayList, child: &mut Flow) { - let kid_display_list = mem::replace(&mut flow::mut_base(child).display_list, - DisplayList::new()); - match self.clip_display_item { - None => parent_display_list.push_all_move(kid_display_list), - Some(ref mut clip_display_item) => { - clip_display_item.children.push_all_move(kid_display_list) - } - } - } - - /// Consumes this accumulator and pushes the clipping item, if any, onto the given display - /// list. - pub fn finish(self, display_list: &mut DisplayList) { - let ChildDisplayListAccumulator { - clip_display_item - } = self; - match clip_display_item { - None => {} - Some(clip_display_item) => display_list.push(ClipDisplayItemClass(clip_display_item)), - } - } -} diff --git a/components/layout/inline.rs b/components/layout/inline.rs index 2d5c6bfdfea..5146b485938 100644 --- a/components/layout/inline.rs +++ b/components/layout/inline.rs @@ -9,8 +9,8 @@ use context::LayoutContext; use floats::{FloatLeft, Floats, PlacementInfo}; use flow::{BaseFlow, FlowClass, Flow, InlineFlowClass, MutableFlowUtils}; use flow; -use fragment::{Fragment, InlineBlockFragment, ScannedTextFragment, ScannedTextFragmentInfo}; -use fragment::{SplitInfo}; +use fragment::{Fragment, InlineAbsoluteHypotheticalFragment, InlineBlockFragment}; +use fragment::{ScannedTextFragment, ScannedTextFragmentInfo, SplitInfo}; use layout_debug; use model::IntrinsicISizes; use text; @@ -18,7 +18,7 @@ use wrapper::ThreadSafeLayoutNode; use collections::{Deque, RingBuf}; use geom::Rect; -use gfx::display_list::ContentLevel; +use gfx::display_list::{ContentLevel, DisplayList}; use gfx::font::FontMetrics; use gfx::font_context::FontContext; use geom::Size2D; @@ -704,19 +704,20 @@ impl InlineFlow { let fragment_position = self.base .abs_position .add_size(&rel_offset.to_physical(self.base.writing_mode)); - let mut accumulator = fragment.build_display_list(&mut self.base.display_list, - layout_context, - fragment_position, - ContentLevel); + fragment.build_display_list(&mut self.base.display_list, + layout_context, + fragment_position, + ContentLevel, + &self.base.clip_rect); match fragment.specific { InlineBlockFragment(ref mut block_flow) => { let block_flow = block_flow.flow_ref.get_mut(); - accumulator.push_child(&mut self.base.display_list, block_flow); + self.base.display_list.push_all_move( + mem::replace(&mut flow::mut_base(block_flow).display_list, + DisplayList::new())); } _ => {} } - - accumulator.finish(&mut self.base.display_list); } } @@ -1124,16 +1125,40 @@ impl Flow for InlineFlow { fn compute_absolute_position(&mut self) { for fragment in self.fragments.fragments.iter_mut() { - match fragment.specific { + let absolute_position = match fragment.specific { InlineBlockFragment(ref mut info) => { let block_flow = info.flow_ref.get_mut().as_block(); - // FIXME(#2795): Get the real container size let container_size = Size2D::zero(); block_flow.base.abs_position = self.base.abs_position + fragment.border_box.start.to_physical(self.base.writing_mode, container_size); + block_flow.base.abs_position + } + InlineAbsoluteHypotheticalFragment(ref mut info) => { + let block_flow = info.flow_ref.get_mut().as_block(); + // FIXME(#2795): Get the real container size + let container_size = Size2D::zero(); + block_flow.base.abs_position = + self.base.abs_position + + fragment.border_box.start.to_physical(self.base.writing_mode, + container_size); + block_flow.base.abs_position + + } + _ => continue, + }; + + let clip_rect = fragment.clip_rect_for_children(self.base.clip_rect, + absolute_position); + + match fragment.specific { + InlineBlockFragment(ref mut info) => { + flow::mut_base(info.flow_ref.get_mut()).clip_rect = clip_rect + } + InlineAbsoluteHypotheticalFragment(ref mut info) => { + flow::mut_base(info.flow_ref.get_mut()).clip_rect = clip_rect } _ => {} } diff --git a/components/layout/layout_task.rs b/components/layout/layout_task.rs index 22068ae460b..03ea97d8583 100644 --- a/components/layout/layout_task.rs +++ b/components/layout/layout_task.rs @@ -26,8 +26,8 @@ use encoding::all::UTF_8; use geom::point::Point2D; use geom::rect::Rect; use geom::size::Size2D; -use gfx::display_list::{ClipDisplayItemClass, ContentStackingLevel, DisplayItem}; -use gfx::display_list::{DisplayItemIterator, DisplayList, OpaqueNode}; +use gfx::display_list::{ContentStackingLevel, DisplayItem, DisplayItemIterator, DisplayList}; +use gfx::display_list::{OpaqueNode}; use gfx::render_task::{RenderInitMsg, RenderChan, RenderLayer}; use gfx::{render_task, color}; use layout_traits; @@ -852,7 +852,6 @@ impl LayoutRPC for LayoutRPCImpl { mut iter: DisplayItemIterator, node: OpaqueNode) { for item in iter { - union_boxes_for_node(accumulator, item.children(), node); if item.base().node == node { match *accumulator { None => *accumulator = Some(item.base().bounds), @@ -884,7 +883,6 @@ impl LayoutRPC for LayoutRPCImpl { mut iter: DisplayItemIterator, node: OpaqueNode) { for item in iter { - add_boxes_for_node(accumulator, item.children(), node); if item.base().node == node { accumulator.push(item.base().bounds) } @@ -907,47 +905,25 @@ impl LayoutRPC for LayoutRPCImpl { /// Requests the node containing the point of interest fn hit_test(&self, _: TrustedNodeAddress, point: Point2D) -> Result { - fn hit_test<'a,I:Iterator<&'a DisplayItem>>(x: Au, y: Au, mut iterator: I) - -> Option { + fn hit_test<'a,I>(point: Point2D, mut iterator: I) + -> Option + where I: Iterator<&'a DisplayItem> { for item in iterator { - match *item { - ClipDisplayItemClass(ref cc) => { - if geometry::rect_contains_point(cc.base.bounds, Point2D(x, y)) { - let ret = hit_test(x, y, cc.children.list.iter().rev()); - if !ret.is_none() { - return ret - } - } - continue - } - _ => {} - } - - let bounds = item.bounds(); - - // TODO(tikue): This check should really be performed by a method of - // DisplayItem. - if x < bounds.origin.x + bounds.size.width && - bounds.origin.x <= x && - y < bounds.origin.y + bounds.size.height && - bounds.origin.y <= y { - return Some(HitTestResponse(item.base() - .node - .to_untrusted_node_address())) + // TODO(tikue): This check should really be performed by a method of `DisplayItem`. + if geometry::rect_contains_point(item.base().clip_rect, point) && + geometry::rect_contains_point(item.bounds(), point) { + return Some(HitTestResponse(item.base().node.to_untrusted_node_address())) } } - let ret: Option = None; - ret + None } - let (x, y) = (Au::from_frac_px(point.x as f64), - Au::from_frac_px(point.y as f64)); - + let point = Point2D(Au::from_frac_px(point.x as f64), Au::from_frac_px(point.y as f64)); let resp = { 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) => hit_test(x, y, display_list.list.iter().rev()), + Some(ref display_list) => hit_test(point, display_list.list.iter().rev()), } }; @@ -957,49 +933,29 @@ impl LayoutRPC for LayoutRPCImpl { Err(()) } - fn mouse_over(&self, _: TrustedNodeAddress, point: Point2D) -> Result { - fn mouse_over_test<'a, - I:Iterator<&'a DisplayItem>>( - x: Au, - y: Au, - mut iterator: I, - result: &mut Vec) { + fn mouse_over(&self, _: TrustedNodeAddress, point: Point2D) + -> Result { + fn mouse_over_test<'a,I>(point: Point2D, + mut iterator: I, + result: &mut Vec) + where I: Iterator<&'a DisplayItem> { for item in iterator { - match *item { - ClipDisplayItemClass(ref cc) => { - mouse_over_test(x, y, cc.children.list.iter().rev(), result); - } - _ => { - let bounds = item.bounds(); - - // TODO(tikue): This check should really be performed by a method - // of DisplayItem. - if x < bounds.origin.x + bounds.size.width && - bounds.origin.x <= x && - y < bounds.origin.y + bounds.size.height && - bounds.origin.y <= y { - result.push(item.base() - .node - .to_untrusted_node_address()); - } - } + // TODO(tikue): This check should really be performed by a method of `DisplayItem`. + if geometry::rect_contains_point(item.bounds(), point) { + result.push(item.base().node.to_untrusted_node_address()) } } } let mut mouse_over_list: Vec = vec!(); - let (x, y) = (Au::from_frac_px(point.x as f64), Au::from_frac_px(point.y as f64)); - + let point = Point2D(Au::from_frac_px(point.x as f64), Au::from_frac_px(point.y as f64)); { 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) => { - mouse_over_test(x, - y, - display_list.list.iter().rev(), - &mut mouse_over_list); + mouse_over_test(point, display_list.list.iter().rev(), &mut mouse_over_list); } }; } diff --git a/components/layout/parallel.rs b/components/layout/parallel.rs index 90e19f6db76..fbb95ac8123 100644 --- a/components/layout/parallel.rs +++ b/components/layout/parallel.rs @@ -188,8 +188,6 @@ trait ParallelPostorderDomTraversal : PostorderDomTraversal { pub struct FlowParallelInfo { /// The number of children that still need work done. pub children_count: AtomicInt, - /// The number of children and absolute descendants that still need work done. - pub children_and_absolute_descendant_count: AtomicInt, /// The address of the parent flow. pub parent: UnsafeFlow, } @@ -198,7 +196,6 @@ impl FlowParallelInfo { pub fn new() -> FlowParallelInfo { FlowParallelInfo { children_count: AtomicInt::new(0), - children_and_absolute_descendant_count: AtomicInt::new(0), parent: null_unsafe_flow(), } } @@ -382,39 +379,12 @@ fn compute_absolute_position(unsafe_flow: UnsafeFlow, // Compute the absolute position for the flow. flow.get_mut().compute_absolute_position(); - // If we are the containing block, count the number of absolutely-positioned children, so - // that we don't double-count them in the `children_and_absolute_descendant_count` - // reference count. - let mut absolutely_positioned_child_count = 0u; + // Enqueue all children. for kid in flow::child_iter(flow.get_mut()) { - if kid.is_absolutely_positioned() { - absolutely_positioned_child_count += 1; - } - } - - drop(flow::mut_base(flow.get_mut()).parallel - .children_and_absolute_descendant_count - .fetch_sub(absolutely_positioned_child_count as int, - SeqCst)); - - // Enqueue all non-absolutely-positioned children. - for kid in flow::child_iter(flow.get_mut()) { - if !kid.is_absolutely_positioned() { - had_descendants = true; - proxy.push(WorkUnit { - fun: compute_absolute_position, - data: borrowed_flow_to_unsafe_flow(kid), - }); - } - } - - // Possibly enqueue absolute descendants. - for absolute_descendant_link in flow::mut_base(flow.get_mut()).abs_descendants.iter() { had_descendants = true; - let descendant = absolute_descendant_link; proxy.push(WorkUnit { fun: compute_absolute_position, - data: borrowed_flow_to_unsafe_flow(descendant), + data: borrowed_flow_to_unsafe_flow(kid), }); } @@ -441,26 +411,12 @@ fn build_display_list(mut unsafe_flow: UnsafeFlow, { let base = flow::mut_base(flow.get_mut()); - // Reset the count of children and absolute descendants for the next layout - // traversal. - let children_and_absolute_descendant_count = base.children.len() + - base.abs_descendants.len(); - base.parallel - .children_and_absolute_descendant_count - .store(children_and_absolute_descendant_count as int, Relaxed); + // Reset the count of children for the next layout traversal. + base.parallel.children_count.store(base.children.len() as int, Relaxed); } // Possibly enqueue the parent. - let unsafe_parent = if flow.get().is_absolutely_positioned() { - match *flow::mut_base(flow.get_mut()).absolute_cb.get() { - None => fail!("no absolute containing block for absolutely positioned?!"), - Some(ref mut absolute_cb) => { - mut_borrowed_flow_to_unsafe_flow(absolute_cb.get_mut()) - } - } - } else { - flow::mut_base(flow.get_mut()).parallel.parent - }; + let unsafe_parent = flow::mut_base(flow.get_mut()).parallel.parent; if unsafe_parent == null_unsafe_flow() { // We're done! break @@ -472,7 +428,7 @@ fn build_display_list(mut unsafe_flow: UnsafeFlow, let parent: &mut FlowRef = mem::transmute(&unsafe_parent); let parent_base = flow::mut_base(parent.get_mut()); if parent_base.parallel - .children_and_absolute_descendant_count + .children_count .fetch_sub(1, SeqCst) == 1 { // We were the last child of our parent. Build display lists for our parent. unsafe_flow = unsafe_parent diff --git a/components/layout/traversal.rs b/components/layout/traversal.rs index d217e785321..f7726c19a59 100644 --- a/components/layout/traversal.rs +++ b/components/layout/traversal.rs @@ -323,13 +323,7 @@ impl<'a> BuildDisplayList<'a> { flow.compute_absolute_position(); for kid in flow::mut_base(flow).child_iter() { - if !kid.is_absolutely_positioned() { - self.process(kid) - } - } - - for absolute_descendant_link in flow::mut_base(flow).abs_descendants.iter() { - self.process(absolute_descendant_link) + self.process(kid) } flow.build_display_list(self.layout_context) diff --git a/components/util/geometry.rs b/components/util/geometry.rs index d212992f6da..64d779d1f67 100644 --- a/components/util/geometry.rs +++ b/components/util/geometry.rs @@ -73,6 +73,29 @@ impl Default for Au { } } +pub static ZERO_RECT: Rect = Rect { + origin: Point2D { + x: Au(0), + y: Au(0), + }, + size: Size2D { + width: Au(0), + height: Au(0), + } +}; + +pub static MAX_RECT: Rect = Rect { + origin: Point2D { + x: Au(i32::MIN / 2), + y: Au(i32::MIN / 2), + }, + size: Size2D { + width: MAX_AU, + height: MAX_AU, + } +}; + +pub static MIN_AU: Au = Au(i32::MIN); pub static MAX_AU: Au = Au(i32::MAX); impl> Encodable for Au {