From 7ccbb6f29d3442ba45555d1f3650b967df0a38de Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Sun, 16 Nov 2014 16:39:27 -0700 Subject: [PATCH] servo: Merge #3990 - gfx: Rewrite display list construction to make stacking-contexts more first-class (from pcwalton:stacking-contexts); r=glennw This implements the scheme described here: https://groups.google.com/forum/#!topic/mozilla.dev.servo/sZVPSfPVfkg This commit changes Servo to generate one display list per stacking context instead of one display list per layer. This is purely a refactoring; there are no functional changes. Performance is essentially the same as before. However, there should be numerous future benefits that this is intended to allow for: * It makes the code simpler to understand because the "new layer needed" vs. "no new layer needed" code paths are more consolidated. * It makes it easy to support CSS properties that did not fit into our previous flat display list model (without unconditionally layerizing them): o `opacity` should be easy to support because the stacking context provides the higher-level grouping of display items to which opacity is to be applied. o `transform` can be easily supported because the stacking context provides a place to stash the transformation matrix. This has the side benefit of nicely separating the transformation matrix from the clipping regions. * The `flatten` logic is now O(1) instead of O(n) and now only needs to be invoked for pseudo-stacking contexts (right now: just floats), instead of for every stacking context. * Layers are now a proper tree instead of a flat list as far as layout is concerned, bringing us closer to a production-quality compositing/layers framework. * This commit opens the door to incremental display list construction at the level of stacking contexts. Future performance improvements could come from optimizing allocation of display list items, and, of course, incremental display list construction. r? @glennw f? @mrobinson @cgaebel Source-Repo: https://github.com/servo/servo Source-Revision: 397d8138e7b27541faf03d9635d7648416da4a75 --- servo/components/gfx/display_list/mod.rs | 482 ++++++++++-------- .../components/gfx/display_list/optimizer.rs | 64 ++- servo/components/gfx/lib.rs | 1 + servo/components/gfx/render_context.rs | 4 +- servo/components/gfx/render_task.rs | 144 +++--- servo/components/layout/block.rs | 93 ++-- .../components/layout/display_list_builder.rs | 305 +++++++---- servo/components/layout/flow.rs | 59 ++- servo/components/layout/fragment.rs | 26 +- servo/components/layout/inline.rs | 47 +- servo/components/layout/layout_task.rs | 105 ++-- servo/components/util/dlist.rs | 27 + servo/components/util/geometry.rs | 5 + 13 files changed, 812 insertions(+), 550 deletions(-) diff --git a/servo/components/gfx/display_list/mod.rs b/servo/components/gfx/display_list/mod.rs index 55782463436b..88413b350a82 100644 --- a/servo/components/gfx/display_list/mod.rs +++ b/servo/components/gfx/display_list/mod.rs @@ -15,7 +15,8 @@ //! low-level drawing primitives. use color::Color; -use render_context::RenderContext; +use display_list::optimizer::DisplayListOptimizer; +use render_context::{RenderContext, ToAzureRect}; use text::glyph::CharIndex; use text::TextRun; @@ -23,11 +24,16 @@ use azure::azure::AzFloat; use collections::dlist::{mod, DList}; use geom::{Point2D, Rect, SideOffsets2D, Size2D, Matrix2D}; use libc::uintptr_t; +use render_task::RenderLayer; +use script_traits::UntrustedNodeAddress; +use servo_msg::compositor_msg::LayerId; use servo_net::image::base::Image; use servo_util::dlist as servo_dlist; -use servo_util::geometry::Au; +use servo_util::geometry::{mod, Au}; use servo_util::range::Range; +use servo_util::smallvec::{SmallVec, SmallVec8}; use std::fmt; +use std::mem; use std::slice::Items; use style::computed_values::border_style; use sync::Arc; @@ -55,240 +61,308 @@ impl OpaqueNode { } } -/// "Steps" as defined by CSS 2.1 § E.2. -#[deriving(Clone, PartialEq, Show)] -pub enum StackingLevel { - /// The border and backgrounds for the root of this stacking context: steps 1 and 2. - BackgroundAndBordersStackingLevel, - /// Borders and backgrounds for block-level descendants: step 4. - BlockBackgroundsAndBordersStackingLevel, - /// Floats: step 5. These are treated as pseudo-stacking contexts. - FloatStackingLevel, - /// All other content. - ContentStackingLevel, - /// Positioned descendant stacking contexts, along with their `z-index` levels. - /// - /// TODO(pcwalton): `z-index` should be the actual CSS property value in order to handle - /// `auto`, not just an integer. - PositionedDescendantStackingLevel(i32) -} - -impl StackingLevel { - #[inline] - pub fn from_background_and_border_level(level: BackgroundAndBorderLevel) -> StackingLevel { - match level { - RootOfStackingContextLevel => BackgroundAndBordersStackingLevel, - BlockLevel => BlockBackgroundsAndBordersStackingLevel, - ContentLevel => ContentStackingLevel, - } - } -} - -struct StackingContext { - /// The border and backgrounds for the root of this stacking context: steps 1 and 2. - pub background_and_borders: DisplayList, - /// Borders and backgrounds for block-level descendants: step 4. - pub block_backgrounds_and_borders: DisplayList, - /// Floats: step 5. These are treated as pseudo-stacking contexts. - pub floats: DisplayList, - /// All other content. - pub content: DisplayList, - /// Positioned descendant stacking contexts, along with their `z-index` levels. - pub positioned_descendants: Vec<(i32, DisplayList)>, -} - -impl StackingContext { - /// Creates a new empty stacking context. - #[inline] - fn new() -> StackingContext { - StackingContext { - background_and_borders: DisplayList::new(), - block_backgrounds_and_borders: DisplayList::new(), - floats: DisplayList::new(), - content: DisplayList::new(), - positioned_descendants: Vec::new(), - } - } - - /// Initializes a stacking context from a display list, consuming that display list in the - /// process. - fn init_from_list(&mut self, list: &mut DisplayList) { - while !list.list.is_empty() { - let mut head = DisplayList::from_list(servo_dlist::split(&mut list.list)); - match head.front().unwrap().base().level { - BackgroundAndBordersStackingLevel => { - self.background_and_borders.append_from(&mut head) - } - BlockBackgroundsAndBordersStackingLevel => { - self.block_backgrounds_and_borders.append_from(&mut head) - } - FloatStackingLevel => self.floats.append_from(&mut head), - ContentStackingLevel => self.content.append_from(&mut head), - PositionedDescendantStackingLevel(z_index) => { - match self.positioned_descendants.iter_mut().find(|& &(z, _)| z_index == z) { - Some(&(_, ref mut my_list)) => { - my_list.append_from(&mut head); - continue - } - None => {} - } - - self.positioned_descendants.push((z_index, head)) - } - } - } - } -} - -/// Which level to place backgrounds and borders in. -pub enum BackgroundAndBorderLevel { - RootOfStackingContextLevel, - BlockLevel, - ContentLevel, -} - -/// A list of rendering operations to be performed. -#[deriving(Clone, Show)] +/// Display items that make up a stacking context. "Steps" here refer to the steps in CSS 2.1 +/// Appendix E. +/// +/// TODO(pcwalton): We could reduce the size of this structure with a more "skip list"-like +/// structure, omitting several pointers and lengths. pub struct DisplayList { - pub list: DList, -} - -pub enum DisplayListIterator<'a> { - EmptyDisplayListIterator, - ParentDisplayListIterator(Items<'a,DisplayList>), -} - -impl<'a> Iterator<&'a DisplayList> for DisplayListIterator<'a> { - #[inline] - fn next(&mut self) -> Option<&'a DisplayList> { - match *self { - EmptyDisplayListIterator => None, - ParentDisplayListIterator(ref mut subiterator) => subiterator.next(), - } - } + /// The border and backgrounds for the root of this stacking context: steps 1 and 2. + pub background_and_borders: DList, + /// Borders and backgrounds for block-level descendants: step 4. + pub block_backgrounds_and_borders: DList, + /// Floats: step 5. These are treated as pseudo-stacking contexts. + pub floats: DList, + /// All other content. + pub content: DList, + /// Child stacking contexts. + pub children: DList>, } impl DisplayList { - /// Creates a new display list. + /// Creates a new, empty display list. #[inline] pub fn new() -> DisplayList { DisplayList { - list: DList::new(), + background_and_borders: DList::new(), + block_backgrounds_and_borders: DList::new(), + floats: DList::new(), + content: DList::new(), + children: DList::new(), } } - /// Creates a new display list from the given list of display items. - fn from_list(list: DList) -> DisplayList { - DisplayList { - list: list, - } - } - - /// Appends the given item to the display list. - #[inline] - pub fn push(&mut self, item: DisplayItem) { - self.list.push_back(item); - } - - /// Appends the items in the given display list to this one, removing them in the process. + /// Appends all display items from `other` into `self`, preserving stacking order and emptying + /// `other` in the process. #[inline] pub fn append_from(&mut self, other: &mut DisplayList) { - servo_dlist::append_from(&mut self.list, &mut other.list) + servo_dlist::append_from(&mut self.background_and_borders, + &mut other.background_and_borders); + servo_dlist::append_from(&mut self.block_backgrounds_and_borders, + &mut other.block_backgrounds_and_borders); + servo_dlist::append_from(&mut self.floats, &mut other.floats); + servo_dlist::append_from(&mut self.content, &mut other.content); + servo_dlist::append_from(&mut self.children, &mut other.children); } - /// Returns the first display item in this list. + /// Merges all display items from all non-float stacking levels to the `float` stacking level. #[inline] - fn front(&self) -> Option<&DisplayItem> { - self.list.front() + pub fn form_float_pseudo_stacking_context(&mut self) { + servo_dlist::prepend_from(&mut self.floats, &mut self.content); + servo_dlist::prepend_from(&mut self.floats, &mut self.block_backgrounds_and_borders); + servo_dlist::prepend_from(&mut self.floats, &mut self.background_and_borders); } - pub fn debug(&self) { - for item in self.list.iter() { - item.debug_with_level(0); + /// Returns a list of all items in this display list concatenated together. This is extremely + /// inefficient and should only be used for debugging. + pub fn all_display_items(&self) -> Vec { + let mut result = Vec::new(); + for display_item in self.background_and_borders.iter() { + result.push((*display_item).clone()) } - } - - /// 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, - current_clip_stack: &mut Vec>) { - debug!("Beginning display list."); - for item in self.list.iter() { - item.draw_into_context(render_context, current_transform, current_clip_stack) + for display_item in self.block_backgrounds_and_borders.iter() { + result.push((*display_item).clone()) } - debug!("Ending display list."); + for display_item in self.floats.iter() { + result.push((*display_item).clone()) + } + for display_item in self.content.iter() { + result.push((*display_item).clone()) + } + result } +} - /// Returns a preorder iterator over the given display list. - #[inline] - pub fn iter<'a>(&'a self) -> DisplayItemIterator<'a> { - ParentDisplayItemIterator(self.list.iter()) - } +/// Represents one CSS stacking context, which may or may not have a hardware layer. +pub struct StackingContext { + /// The display items that make up this stacking context. + pub display_list: Box, + /// The layer for this stacking context, if there is one. + pub layer: Option>, + /// The position and size of this stacking context. + pub bounds: Rect, + /// The clipping rect for this stacking context, in the coordinate system of the *parent* + /// stacking context. + pub clip_rect: Rect, + /// The `z-index` for this stacking context. + pub z_index: i32, +} - /// Flattens a display list into a display list with a single stacking level according to the - /// steps in CSS 2.1 § E.2. +impl StackingContext { + /// Creates a new stacking context. /// - /// This must be called before `draw_into_context()` is for correct results. - pub fn flatten(&mut self, resulting_level: StackingLevel) { - // Fast paths: - if self.list.len() == 0 { - return - } - if self.list.len() == 1 { - self.set_stacking_level(resulting_level); - return + /// TODO(pcwalton): Stacking contexts should not always be clipped to their bounds, to handle + /// overflow properly. + #[inline] + pub fn new(display_list: Box, + bounds: Rect, + z_index: i32, + layer: Option>) + -> StackingContext { + StackingContext { + display_list: display_list, + layer: layer, + bounds: bounds, + clip_rect: bounds, + z_index: z_index, } + } - let mut stacking_context = StackingContext::new(); - stacking_context.init_from_list(self); - debug_assert!(self.list.is_empty()); - - // Steps 1 and 2: Borders and background for the root. - self.append_from(&mut stacking_context.background_and_borders); + /// Draws the stacking context in the proper order according to the steps in CSS 2.1 § E.2. + pub fn optimize_and_draw_into_context(&self, + render_context: &mut RenderContext, + tile_bounds: &Rect, + current_transform: &Matrix2D, + current_clip_stack: &mut Vec>) { + // Optimize the display list to throw out out-of-bounds display items and so forth. + let display_list = DisplayListOptimizer::new(tile_bounds).optimize(&*self.display_list); // Sort positioned children according to z-index. - stacking_context.positioned_descendants.sort_by(|&(z_index_a, _), &(z_index_b, _)| { - z_index_a.cmp(&z_index_b) - }); + let mut positioned_children = SmallVec8::new(); + for kid in display_list.children.iter() { + positioned_children.push((*kid).clone()); + } + positioned_children.as_slice_mut().sort_by(|this, other| this.z_index.cmp(&other.z_index)); + + // Steps 1 and 2: Borders and background for the root. + for display_item in display_list.background_and_borders.iter() { + display_item.draw_into_context(render_context, current_transform, current_clip_stack) + } // Step 3: Positioned descendants with negative z-indices. - for &(ref mut z_index, ref mut list) in stacking_context.positioned_descendants.iter_mut() { - if *z_index < 0 { - self.append_from(list) + for positioned_kid in positioned_children.iter() { + if positioned_kid.z_index >= 0 { + break + } + if positioned_kid.layer.is_none() { + let new_transform = + current_transform.translate(positioned_kid.bounds.origin.x.to_nearest_px() + as AzFloat, + positioned_kid.bounds.origin.y.to_nearest_px() + as AzFloat); + let new_tile_rect = + self.compute_tile_rect_for_child_stacking_context(tile_bounds, + &**positioned_kid); + positioned_kid.optimize_and_draw_into_context(render_context, + &new_tile_rect, + &new_transform, + current_clip_stack); } } // Step 4: Block backgrounds and borders. - self.append_from(&mut stacking_context.block_backgrounds_and_borders); + for display_item in display_list.block_backgrounds_and_borders.iter() { + display_item.draw_into_context(render_context, current_transform, current_clip_stack) + } // Step 5: Floats. - self.append_from(&mut stacking_context.floats); + for display_item in display_list.floats.iter() { + display_item.draw_into_context(render_context, current_transform, current_clip_stack) + } // TODO(pcwalton): Step 6: Inlines that generate stacking contexts. // Step 7: Content. - self.append_from(&mut stacking_context.content); + for display_item in display_list.content.iter() { + display_item.draw_into_context(render_context, current_transform, current_clip_stack) + } // Steps 8 and 9: Positioned descendants with nonnegative z-indices. - for &(ref mut z_index, ref mut list) in stacking_context.positioned_descendants.iter_mut() { - if *z_index >= 0 { - self.append_from(list) + for positioned_kid in positioned_children.iter() { + if positioned_kid.z_index < 0 { + continue + } + + if positioned_kid.layer.is_none() { + let new_transform = + current_transform.translate(positioned_kid.bounds.origin.x.to_nearest_px() + as AzFloat, + positioned_kid.bounds.origin.y.to_nearest_px() + as AzFloat); + let new_tile_rect = + self.compute_tile_rect_for_child_stacking_context(tile_bounds, + &**positioned_kid); + positioned_kid.optimize_and_draw_into_context(render_context, + &new_tile_rect, + &new_transform, + current_clip_stack); } } // TODO(pcwalton): Step 10: Outlines. - - self.set_stacking_level(resulting_level); } - /// Sets the stacking level for this display list and all its subitems. - fn set_stacking_level(&mut self, new_level: StackingLevel) { - for item in self.list.iter_mut() { - item.mut_base().level = new_level; + /// Translate the given tile rect into the coordinate system of a child stacking context. + fn compute_tile_rect_for_child_stacking_context(&self, + tile_bounds: &Rect, + child_stacking_context: &StackingContext) + -> Rect { + static ZERO_AZURE_RECT: Rect = Rect { + origin: Point2D { + x: 0.0, + y: 0.0, + }, + size: Size2D { + width: 0.0, + height: 0.0 + } + }; + + let child_stacking_context_bounds = child_stacking_context.bounds.to_azure_rect(); + let tile_subrect = tile_bounds.intersection(&child_stacking_context_bounds) + .unwrap_or(ZERO_AZURE_RECT); + let offset = tile_subrect.origin - child_stacking_context_bounds.origin; + Rect(offset, tile_subrect.size) + } + + /// Places all nodes containing the point of interest into `result`, topmost first. If + /// `topmost_only` is true, stops after placing one node into the list. `result` must be empty + /// upon entry to this function. + pub fn hit_test(&self, + point: Point2D, + result: &mut Vec, + topmost_only: bool) { + fn hit_test_in_list<'a,I>(point: Point2D, + result: &mut Vec, + topmost_only: bool, + mut iterator: I) + where I: Iterator<&'a DisplayItem> { + for item in iterator { + if geometry::rect_contains_point(item.base().clip_rect, point) && + geometry::rect_contains_point(item.bounds(), point) { + result.push(item.base().node.to_untrusted_node_address()); + if topmost_only { + return + } + } + } + } + + debug_assert!(!topmost_only || result.is_empty()); + + // Iterate through display items in reverse stacking order. Steps here refer to the + // painting steps in CSS 2.1 Appendix E. + // + // Steps 9 and 8: Positioned descendants with nonnegative z-indices. + for kid in self.display_list.children.iter().rev() { + if kid.z_index < 0 { + continue + } + kid.hit_test(point, result, topmost_only); + if topmost_only && !result.is_empty() { + return + } + } + + // Steps 7, 5, and 4: Content, floats, and block backgrounds and borders. + // + // TODO(pcwalton): Step 6: Inlines that generate stacking contexts. + for display_list in [ + &self.display_list.content, + &self.display_list.floats, + &self.display_list.block_backgrounds_and_borders, + ].iter() { + hit_test_in_list(point, result, topmost_only, display_list.iter().rev()); + if topmost_only && !result.is_empty() { + return + } + } + + // Step 3: Positioned descendants with negative z-indices. + for kid in self.display_list.children.iter().rev() { + if kid.z_index >= 0 { + continue + } + kid.hit_test(point, result, topmost_only); + if topmost_only && !result.is_empty() { + return + } + } + + // Steps 2 and 1: Borders and background for the root. + hit_test_in_list(point, + result, + topmost_only, + self.display_list.background_and_borders.iter().rev()) + } +} + +/// Returns the stacking context in the given tree of stacking contexts with a specific layer ID. +pub fn find_stacking_context_with_layer_id(this: &Arc, layer_id: LayerId) + -> Option> { + match this.layer { + Some(ref layer) if layer.id == layer_id => return Some((*this).clone()), + Some(_) | None => {} + } + + for kid in this.display_list.children.iter() { + match find_stacking_context_with_layer_id(kid, layer_id) { + Some(stacking_context) => return Some(stacking_context), + None => {} } } + + None } /// One drawing command in the list. @@ -318,9 +392,6 @@ pub struct BaseDisplayItem { /// The originating DOM node. pub node: OpaqueNode, - /// 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 @@ -330,12 +401,10 @@ pub struct BaseDisplayItem { impl BaseDisplayItem { #[inline(always)] - pub fn new(bounds: Rect, node: OpaqueNode, level: StackingLevel, clip_rect: Rect) - -> BaseDisplayItem { + pub fn new(bounds: Rect, node: OpaqueNode, clip_rect: Rect) -> BaseDisplayItem { BaseDisplayItem { bounds: bounds, node: node, - level: level, clip_rect: clip_rect, } } @@ -450,9 +519,6 @@ impl DisplayItem { render_context: &mut RenderContext, current_transform: &Matrix2D, current_clip_stack: &mut Vec>) { - // This should have been flattened to the content stacking level first. - assert!(self.base().level == ContentStackingLevel); - // TODO(pcwalton): This will need some tweaking to deal with more complex clipping regions. let clip_rect = &self.base().clip_rect; if current_clip_stack.len() == 0 || current_clip_stack.last().unwrap() != clip_rect { @@ -464,6 +530,8 @@ impl DisplayItem { current_clip_stack.push(*clip_rect); } + render_context.draw_target.set_transform(current_transform); + match *self { SolidColorDisplayItemClass(ref solid_color) => { render_context.draw_solid_color(&solid_color.base.bounds, solid_color.color) @@ -558,7 +626,7 @@ impl DisplayItem { impl fmt::Show for DisplayItem { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{} @ {} ({:x}) [{}]", + write!(f, "{} @ {} ({:x})", match *self { SolidColorDisplayItemClass(_) => "SolidColor", TextDisplayItemClass(_) => "Text", @@ -569,9 +637,25 @@ impl fmt::Show for DisplayItem { PseudoDisplayItemClass(_) => "Pseudo", }, self.base().bounds, - self.base().node.id(), - self.base().level + self.base().node.id() ) } } +pub trait OpaqueNodeMethods { + /// Converts this node to an `UntrustedNodeAddress`. An `UntrustedNodeAddress` is just the type + /// of node that script expects to receive in a hit test. + fn to_untrusted_node_address(&self) -> UntrustedNodeAddress; +} + + +impl OpaqueNodeMethods for OpaqueNode { + fn to_untrusted_node_address(&self) -> UntrustedNodeAddress { + unsafe { + let OpaqueNode(addr) = *self; + let addr: UntrustedNodeAddress = mem::transmute(addr); + addr + } + } +} + diff --git a/servo/components/gfx/display_list/optimizer.rs b/servo/components/gfx/display_list/optimizer.rs index 69cdc469de3c..9ce3fbfc3f10 100644 --- a/servo/components/gfx/display_list/optimizer.rs +++ b/servo/components/gfx/display_list/optimizer.rs @@ -2,52 +2,66 @@ * 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::{DisplayItem, DisplayList}; +//! Transforms a display list to produce a visually-equivalent, but cheaper-to-render, one. + +use display_list::{DisplayItem, DisplayList, StackingContext}; use collections::dlist::DList; use geom::rect::Rect; -use servo_util::geometry::Au; +use servo_util::geometry::{mod, Au}; use sync::Arc; +/// Transforms a display list to produce a visually-equivalent, but cheaper-to-render, one. pub struct DisplayListOptimizer { - display_list: Arc, /// The visible rect in page coordinates. visible_rect: Rect, } impl DisplayListOptimizer { - /// `visible_rect` specifies the visible rect in page coordinates. - pub fn new(display_list: Arc, visible_rect: Rect) -> DisplayListOptimizer { + /// Creates a new display list optimizer object. `visible_rect` specifies the visible rect in + /// page coordinates. + pub fn new(visible_rect: &Rect) -> DisplayListOptimizer { DisplayListOptimizer { - display_list: display_list, - visible_rect: visible_rect, + visible_rect: geometry::f32_rect_to_au_rect(*visible_rect), } } - pub fn optimize(self) -> DisplayList { - self.process_display_list(&*self.display_list) + /// Optimizes the given display list, returning an equivalent, but cheaper-to-paint, one. + pub fn optimize(self, display_list: &DisplayList) -> DisplayList { + let mut result = DisplayList::new(); + self.add_in_bounds_display_items(&mut result.background_and_borders, + display_list.background_and_borders.iter()); + self.add_in_bounds_display_items(&mut result.block_backgrounds_and_borders, + display_list.block_backgrounds_and_borders.iter()); + self.add_in_bounds_display_items(&mut result.floats, display_list.floats.iter()); + self.add_in_bounds_display_items(&mut result.content, display_list.content.iter()); + self.add_in_bounds_stacking_contexts(&mut result.children, display_list.children.iter()); + result } - fn process_display_list(&self, display_list: &DisplayList) -> DisplayList { - let mut result = DList::new(); - for item in display_list.iter() { - match self.process_display_item(item) { - None => {} - Some(display_item) => result.push_back(display_item), + /// Adds display items that intersect the visible rect to `result_list`. + fn add_in_bounds_display_items<'a,I>(&self, + result_list: &mut DList, + mut display_items: I) + where I: Iterator<&'a DisplayItem> { + for display_item in display_items { + if self.visible_rect.intersects(&display_item.base().bounds) && + self.visible_rect.intersects(&display_item.base().clip_rect) { + result_list.push_back((*display_item).clone()) } } - DisplayList { - list: result, - } } - 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) || - !self.visible_rect.intersects(&display_item.base().clip_rect) { - None - } else { - Some((*display_item).clone()) + /// Adds child stacking contexts whose boundaries intersect the visible rect to `result_list`. + fn add_in_bounds_stacking_contexts<'a,I>(&self, + result_list: &mut DList>, + mut stacking_contexts: I) + where I: Iterator<&'a Arc> { + for stacking_context in stacking_contexts { + if self.visible_rect.intersects(&stacking_context.bounds) && + self.visible_rect.intersects(&stacking_context.clip_rect) { + result_list.push_back((*stacking_context).clone()) + } } } } diff --git a/servo/components/gfx/lib.rs b/servo/components/gfx/lib.rs index c267e139fe11..701390e072ca 100644 --- a/servo/components/gfx/lib.rs +++ b/servo/components/gfx/lib.rs @@ -20,6 +20,7 @@ extern crate native; extern crate rustrt; extern crate stb_image; extern crate png; +extern crate script_traits; extern crate serialize; extern crate unicode; #[phase(plugin)] diff --git a/servo/components/gfx/render_context.rs b/servo/components/gfx/render_context.rs index c8c1df2cc630..ea7771efb9d6 100644 --- a/servo/components/gfx/render_context.rs +++ b/servo/components/gfx/render_context.rs @@ -467,7 +467,7 @@ impl<'a> RenderContext<'a> { } } -trait ToAzurePoint { +pub trait ToAzurePoint { fn to_azure_point(&self) -> Point2D; } @@ -477,7 +477,7 @@ impl ToAzurePoint for Point2D { } } -trait ToAzureRect { +pub trait ToAzureRect { fn to_azure_rect(&self) -> Rect; } diff --git a/servo/components/gfx/render_task.rs b/servo/components/gfx/render_task.rs index e1d190ac5fab..6dc3335faff7 100644 --- a/servo/components/gfx/render_task.rs +++ b/servo/components/gfx/render_task.rs @@ -5,8 +5,7 @@ //! The task that handles all rendering/painting. use buffer_map::BufferMap; -use display_list::optimizer::DisplayListOptimizer; -use display_list::DisplayList; +use display_list::{mod, StackingContext}; use font_cache_task::FontCacheTask; use font_context::FontContext; use render_context::RenderContext; @@ -27,9 +26,9 @@ 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, ZERO_POINT}; use servo_util::opts; -use servo_util::smallvec::{SmallVec, SmallVec1}; +use servo_util::smallvec::SmallVec; use servo_util::task::spawn_named_with_send_on_failure; use servo_util::task_state; use servo_util::time::{TimeProfilerChan, profile}; @@ -39,21 +38,28 @@ use std::mem; use std::task::TaskBuilder; use sync::Arc; -/// Information about a layer that layout sends to the painting task. +/// Information about a hardware graphics layer that layout sends to the painting task. #[deriving(Clone)] pub struct RenderLayer { /// A per-pipeline ID describing this layer that should be stable across reflows. pub id: LayerId, - /// The display list describing the contents of this layer. - pub display_list: Arc, - /// The position of the layer in pixels. - pub position: Rect, /// The color of the background in this layer. Used for unrendered content. pub background_color: Color, /// The scrolling policy of this layer. pub scroll_policy: ScrollPolicy, } +impl RenderLayer { + /// Creates a new `RenderLayer`. + pub fn new(id: LayerId, background_color: Color, scroll_policy: ScrollPolicy) -> RenderLayer { + RenderLayer { + id: id, + background_color: background_color, + scroll_policy: scroll_policy, + } + } +} + pub struct RenderRequest { pub buffer_requests: Vec, pub scale: f32, @@ -62,7 +68,7 @@ pub struct RenderRequest { } pub enum Msg { - RenderInitMsg(SmallVec1), + RenderInitMsg(Arc), RenderMsg(Vec), UnusedBufferMsg(Vec>), PaintPermissionGranted, @@ -102,8 +108,8 @@ pub struct RenderTask { /// The native graphics context. native_graphics_context: Option, - /// The layers to be rendered. - render_layers: SmallVec1, + /// The root stacking context sent to us by the layout thread. + root_stacking_context: Option>, /// Permission to send paint messages to the compositor paint_permission: bool, @@ -129,17 +135,36 @@ macro_rules! native_graphics_context( fn initialize_layers(compositor: &mut C, pipeline_id: PipelineId, epoch: Epoch, - render_layers: &[RenderLayer]) + root_stacking_context: &StackingContext) where C: RenderListener { - let metadata = render_layers.iter().map(|render_layer| { - LayerMetadata { - id: render_layer.id, - position: render_layer.position, - background_color: render_layer.background_color, - scroll_policy: render_layer.scroll_policy, - } - }).collect(); + let mut metadata = Vec::new(); + build(&mut metadata, root_stacking_context, &ZERO_POINT); compositor.initialize_layers_for_pipeline(pipeline_id, metadata, epoch); + + fn build(metadata: &mut Vec, + stacking_context: &StackingContext, + page_position: &Point2D) { + let page_position = stacking_context.bounds.origin + *page_position; + match stacking_context.layer { + None => {} + Some(ref render_layer) => { + metadata.push(LayerMetadata { + id: render_layer.id, + position: + Rect(Point2D(page_position.x.to_nearest_px() as uint, + page_position.y.to_nearest_px() as uint), + Size2D(stacking_context.bounds.size.width.to_nearest_px() as uint, + stacking_context.bounds.size.height.to_nearest_px() as uint)), + background_color: render_layer.background_color, + scroll_policy: render_layer.scroll_policy, + }) + } + } + + for kid in stacking_context.display_list.children.iter() { + build(metadata, &**kid, &page_position) + } + } } impl RenderTask where C: RenderListener + Send { @@ -170,11 +195,8 @@ impl RenderTask where C: RenderListener + Send { compositor: compositor, constellation_chan: constellation_chan, time_profiler_chan: time_profiler_chan, - native_graphics_context: native_graphics_context, - - render_layers: SmallVec1::new(), - + root_stacking_context: None, paint_permission: false, epoch: Epoch(0), buffer_map: BufferMap::new(10000000), @@ -205,9 +227,9 @@ impl RenderTask where C: RenderListener + Send { loop { match self.port.recv() { - RenderInitMsg(render_layers) => { + RenderInitMsg(stacking_context) => { self.epoch.next(); - self.render_layers = render_layers; + self.root_stacking_context = Some(stacking_context.clone()); if !self.paint_permission { debug!("render_task: render ready msg"); @@ -219,7 +241,7 @@ impl RenderTask where C: RenderListener + Send { initialize_layers(&mut self.compositor, self.id, self.epoch, - self.render_layers.as_slice()); + &*stacking_context); } RenderMsg(requests) => { if !self.paint_permission { @@ -254,15 +276,15 @@ impl RenderTask where C: RenderListener + Send { PaintPermissionGranted => { self.paint_permission = true; - // Here we assume that the main layer—the layer responsible for the page size— - // is the first layer. This is a pretty fragile assumption. It will be fixed - // once we use the layers-based scrolling infrastructure for all scrolling. - if self.render_layers.len() > 1 { - self.epoch.next(); - initialize_layers(&mut self.compositor, - self.id, - self.epoch, - self.render_layers.as_slice()); + match self.root_stacking_context { + None => {} + Some(ref stacking_context) => { + self.epoch.next(); + initialize_layers(&mut self.compositor, + self.id, + self.epoch, + &**stacking_context); + } } } PaintPermissionRevoked => { @@ -327,9 +349,15 @@ impl RenderTask where C: RenderListener + Send { scale: f32, layer_id: LayerId) { time::profile(time::PaintingCategory, None, self.time_profiler_chan.clone(), || { - // Bail out if there is no appropriate render layer. - let render_layer = match self.render_layers.iter().find(|layer| layer.id == layer_id) { - Some(render_layer) => (*render_layer).clone(), + // Bail out if there is no appropriate stacking context. + let stacking_context = match self.root_stacking_context { + Some(ref stacking_context) => { + match display_list::find_stacking_context_with_layer_id(stacking_context, + layer_id) { + Some(stacking_context) => stacking_context, + None => return, + } + } None => return, }; @@ -342,7 +370,7 @@ impl RenderTask where C: RenderListener + Send { let layer_buffer = self.find_or_create_layer_buffer_for_tile(&tile, scale); self.worker_threads[thread_id].paint_tile(tile, layer_buffer, - render_layer.clone(), + stacking_context.clone(), scale); } let new_buffers = Vec::from_fn(tile_count, |i| { @@ -397,9 +425,9 @@ impl WorkerThreadProxy { fn paint_tile(&mut self, tile: BufferRequest, layer_buffer: Option>, - render_layer: RenderLayer, + stacking_context: Arc, scale: f32) { - self.sender.send(PaintTileMsgToWorkerThread(tile, layer_buffer, render_layer, scale)) + self.sender.send(PaintTileMsgToWorkerThread(tile, layer_buffer, stacking_context, scale)) } fn get_painted_tile_buffer(&mut self) -> Box { @@ -443,8 +471,8 @@ impl WorkerThread { loop { match self.receiver.recv() { ExitMsgToWorkerThread => break, - PaintTileMsgToWorkerThread(tile, layer_buffer, render_layer, scale) => { - let draw_target = self.optimize_and_paint_tile(&tile, render_layer, scale); + PaintTileMsgToWorkerThread(tile, layer_buffer, stacking_context, scale) => { + let draw_target = self.optimize_and_paint_tile(&tile, stacking_context, scale); let buffer = self.create_layer_buffer_for_painted_tile(&tile, layer_buffer, draw_target, @@ -457,21 +485,9 @@ impl WorkerThread { fn optimize_and_paint_tile(&mut self, tile: &BufferRequest, - render_layer: RenderLayer, + stacking_context: Arc, scale: f32) -> DrawTarget { - // page_rect is in coordinates relative to the layer origin, but all display list - // components are relative to the page origin. We make page_rect relative to - // the page origin before passing it to the optimizer. - let page_rect = tile.page_rect.translate(&Point2D(render_layer.position.origin.x as f32, - render_layer.position.origin.y as f32)); - let page_rect_au = geometry::f32_rect_to_au_rect(page_rect); - - // Optimize the display list for this tile. - let optimizer = DisplayListOptimizer::new(render_layer.display_list.clone(), - page_rect_au); - let display_list = optimizer.optimize(); - let size = Size2D(tile.screen_rect.size.width as i32, tile.screen_rect.size.height as i32); let draw_target = if !opts::get().gpu_painting { DrawTarget::new(SkiaBackend, size, B8G8R8A8) @@ -496,10 +512,11 @@ impl WorkerThread { }; // Apply the translation to render the tile we want. + let tile_bounds = tile.page_rect; let matrix: Matrix2D = Matrix2D::identity(); let matrix = matrix.scale(scale as AzFloat, scale as AzFloat); - let matrix = matrix.translate(-page_rect.origin.x as AzFloat, - -page_rect.origin.y as AzFloat); + let matrix = matrix.translate(-tile_bounds.origin.x as AzFloat, + -tile_bounds.origin.y as AzFloat); render_context.draw_target.set_transform(&matrix); @@ -509,7 +526,10 @@ impl WorkerThread { // Draw the display list. profile(time::PaintingPerTileCategory, None, self.time_profiler_sender.clone(), || { let mut clip_stack = Vec::new(); - display_list.draw_into_context(&mut render_context, &matrix, &mut clip_stack); + stacking_context.optimize_and_draw_into_context(&mut render_context, + &tile.page_rect, + &matrix, + &mut clip_stack); render_context.draw_target.flush(); }); } @@ -564,7 +584,7 @@ impl WorkerThread { enum MsgToWorkerThread { ExitMsgToWorkerThread, - PaintTileMsgToWorkerThread(BufferRequest, Option>, RenderLayer, f32), + PaintTileMsgToWorkerThread(BufferRequest, Option>, Arc, f32), } enum MsgFromWorkerThread { diff --git a/servo/components/layout/block.rs b/servo/components/layout/block.rs index 9b004bd798ce..dbfde997841e 100644 --- a/servo/components/layout/block.rs +++ b/servo/components/layout/block.rs @@ -30,9 +30,9 @@ use construct::FlowConstructor; use context::LayoutContext; use css::node_style::StyledNode; -use display_list_builder::{BlockFlowDisplayListBuilding, FragmentDisplayListBuilding}; +use display_list_builder::{BlockFlowDisplayListBuilding, BlockLevel, FragmentDisplayListBuilding}; use floats::{ClearBoth, ClearLeft, ClearRight, FloatKind, FloatLeft, Floats, PlacementInfo}; -use flow::{BaseFlow, BlockFlowClass, FlowClass, Flow, ImmutableFlowUtils}; +use flow::{AbsolutePositionInfo, BaseFlow, BlockFlowClass, FlowClass, Flow, ImmutableFlowUtils}; use flow::{MutableFlowUtils, PreorderFlowTraversal, PostorderFlowTraversal, mut_base}; use flow; use fragment::{Fragment, ImageFragment, InlineBlockFragment, FragmentBoundsIterator}; @@ -45,10 +45,9 @@ use table::ColumnInlineSize; use wrapper::ThreadSafeLayoutNode; use geom::Size2D; -use gfx::display_list::BlockLevel; use serialize::{Encoder, Encodable}; use servo_msg::compositor_msg::LayerId; -use servo_util::geometry::{Au, MAX_AU}; +use servo_util::geometry::{Au, MAX_AU, MAX_RECT, ZERO_POINT}; use servo_util::logical_geometry::{LogicalPoint, LogicalRect, LogicalSize}; use servo_util::opts; use std::cmp::{max, min}; @@ -1665,24 +1664,26 @@ 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.base.flags.is_absolutely_positioned() { let position_start = self.base.position.start.to_physical(self.base.writing_mode, container_size); - self.base.absolute_position_info.absolute_containing_block_position = - if self.is_fixed() { - // The viewport is initially at (0, 0). - position_start - } else { - // Absolute position of the containing block + position of absolute - // flow w.r.t. the containing block. - self.base.absolute_position_info.absolute_containing_block_position - + position_start - }; - // Set the absolute position, which will be passed down later as part - // of containing block details for absolute descendants. - self.base.abs_position = - self.base.absolute_position_info.absolute_containing_block_position; + // Compute our position relative to the nearest ancestor stacking context. This will be + // passed down later as part of containing block details for absolute descendants. + self.base.stacking_relative_position = if self.is_fixed() { + // The viewport is initially at (0, 0). + position_start + } else { + // Absolute position of the containing block + position of absolute + // flow w.r.t. the containing block. + self.base + .absolute_position_info + .stacking_relative_position_of_absolute_containing_block + position_start + } } // For relatively-positioned descendants, the containing block formed by a block is just @@ -1693,32 +1694,52 @@ impl Flow for BlockFlow { .absolute_position_info .relative_containing_block_size); if self.is_positioned() { - self.base.absolute_position_info.absolute_containing_block_position = - self.base.abs_position - + (self.generated_containing_block_rect().start - + relative_offset).to_physical(self.base.writing_mode, container_size) + self.base + .absolute_position_info + .stacking_relative_position_of_absolute_containing_block = + self.base.stacking_relative_position + + (self.generated_containing_block_rect().start + + relative_offset).to_physical(self.base.writing_mode, container_size) } // Compute absolute position info for children. - let mut absolute_position_info = self.base.absolute_position_info; - absolute_position_info.relative_containing_block_size = self.fragment.content_box().size; - absolute_position_info.layers_needed_for_positioned_flows = - self.base.flags.layers_needed_for_descendants(); + let absolute_position_info_for_children = AbsolutePositionInfo { + stacking_relative_position_of_absolute_containing_block: + if self.fragment.establishes_stacking_context() { + let logical_border_width = self.fragment.style().logical_border_width(); + LogicalPoint::new(self.base.writing_mode, + logical_border_width.inline_start, + logical_border_width.block_start).to_physical( + self.base.writing_mode, + container_size) + } else { + self.base + .absolute_position_info + .stacking_relative_position_of_absolute_containing_block + }, + relative_containing_block_size: self.fragment.content_box().size, + layers_needed_for_positioned_flows: self.base.flags.layers_needed_for_descendants(), + }; - // 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); + // Compute the origin and clipping rectangle for children. + let origin_for_children = if self.fragment.establishes_stacking_context() { + ZERO_POINT + } else { + self.base.stacking_relative_position + }; + let clip_rect = self.fragment.clip_rect_for_children(self.base.clip_rect, + origin_for_children); // Process children. let writing_mode = self.base.writing_mode; for kid in self.base.child_iter() { if !flow::base(kid).flags.is_absolutely_positioned() { let kid_base = flow::mut_base(kid); - kid_base.abs_position = - this_position + + kid_base.stacking_relative_position = + origin_for_children + (kid_base.position.start + relative_offset).to_physical(writing_mode, container_size); - kid_base.absolute_position_info = absolute_position_info + kid_base.absolute_position_info = absolute_position_info_for_children } flow::mut_base(kid).clip_rect = clip_rect @@ -1726,7 +1747,8 @@ impl Flow for BlockFlow { // Process absolute descendant links. for absolute_descendant in self.base.abs_descendants.iter() { - flow::mut_base(absolute_descendant).absolute_position_info = absolute_position_info + flow::mut_base(absolute_descendant).absolute_position_info = + absolute_position_info_for_children } } @@ -1816,9 +1838,10 @@ impl Flow for BlockFlow { 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); + let fragment_origin = + self.base.stacking_relative_position_of_child_fragment(&self.fragment); iterator.process(&self.fragment, - self.fragment.abs_bounds_from_origin(&fragment_origin)); + self.fragment.stacking_relative_bounds(&fragment_origin)); } } } diff --git a/servo/components/layout/display_list_builder.rs b/servo/components/layout/display_list_builder.rs index 2948d678050b..069493058b06 100644 --- a/servo/components/layout/display_list_builder.rs +++ b/servo/components/layout/display_list_builder.rs @@ -21,27 +21,22 @@ use fragment::{UnscannedTextFragment}; use model; use util::{OpaqueNodeMethods, ToGfxColor}; -use collections::dlist::DList; use geom::approxeq::ApproxEq; use geom::{Point2D, Rect, Size2D, SideOffsets2D}; use gfx::color; -use gfx::display_list::{BackgroundAndBorderLevel, BaseDisplayItem, BorderDisplayItem}; -use gfx::display_list::{BorderDisplayItemClass, ContentStackingLevel, DisplayList}; -use gfx::display_list::{FloatStackingLevel, GradientDisplayItem, GradientDisplayItemClass}; -use gfx::display_list::{GradientStop, ImageDisplayItem, ImageDisplayItemClass, LineDisplayItem}; -use gfx::display_list::{LineDisplayItemClass, PositionedDescendantStackingLevel}; -use gfx::display_list::{PseudoDisplayItemClass, RootOfStackingContextLevel, SidewaysLeft}; -use gfx::display_list::{SidewaysRight, SolidColorDisplayItem, SolidColorDisplayItemClass}; -use gfx::display_list::{StackingLevel, TextDisplayItem, TextDisplayItemClass, Upright}; +use gfx::display_list::{BaseDisplayItem, BorderDisplayItem, BorderDisplayItemClass, DisplayItem}; +use gfx::display_list::{DisplayList, GradientDisplayItem, GradientDisplayItemClass, GradientStop}; +use gfx::display_list::{ImageDisplayItem, ImageDisplayItemClass, LineDisplayItem}; +use gfx::display_list::{LineDisplayItemClass, PseudoDisplayItemClass, SidewaysLeft, SidewaysRight}; +use gfx::display_list::{SolidColorDisplayItem, SolidColorDisplayItemClass, StackingContext}; +use gfx::display_list::{TextDisplayItem, TextDisplayItemClass, Upright}; use gfx::render_task::RenderLayer; use servo_msg::compositor_msg::{FixedPosition, Scrollable}; use servo_msg::constellation_msg::{ConstellationChan, FrameRectMsg}; use servo_net::image::holder::ImageHolder; -use servo_util::dlist; -use servo_util::geometry::{mod, Au, ZERO_RECT}; +use servo_util::geometry::{mod, Au, ZERO_POINT, ZERO_RECT}; use servo_util::logical_geometry::{LogicalRect, WritingMode}; use servo_util::opts; -use std::mem; use style::computed::{AngleAoc, CornerAoc, LP_Length, LP_Percentage, LengthOrPercentage}; use style::computed::{LinearGradient, LinearGradientImage, UrlImage}; use style::computed_values::{background_attachment, background_repeat, border_style, overflow}; @@ -50,12 +45,36 @@ use style::{ComputedValues, Bottom, Left, RGBA, Right, Top}; use sync::Arc; use url::Url; +/// The results of display list building for a single flow. +pub enum DisplayListBuildingResult { + NoDisplayListBuildingResult, + StackingContextResult(Arc), + DisplayListResult(Box), +} + +impl DisplayListBuildingResult { + /// Adds the display list items contained within this display list building result to the given + /// display list, preserving stacking order. If this display list building result does not + /// consist of an entire stacking context, it will be emptied. + pub fn add_to(&mut self, display_list: &mut DisplayList) { + match *self { + NoDisplayListBuildingResult => return, + StackingContextResult(ref mut stacking_context) => { + display_list.children.push_back((*stacking_context).clone()) + } + DisplayListResult(ref mut source_display_list) => { + display_list.append_from(&mut **source_display_list) + } + } + } +} + pub trait FragmentDisplayListBuilding { /// Adds the display items necessary to paint the background of this fragment to the display /// list if necessary. fn build_display_list_for_background_if_applicable(&self, style: &ComputedValues, - list: &mut DisplayList, + display_list: &mut DisplayList, layout_context: &LayoutContext, level: StackingLevel, absolute_bounds: &Rect, @@ -65,7 +84,7 @@ pub trait FragmentDisplayListBuilding { /// display list at the appropriate stacking level. fn build_display_list_for_background_image(&self, style: &ComputedValues, - list: &mut DisplayList, + display_list: &mut DisplayList, layout_context: &LayoutContext, level: StackingLevel, absolute_bounds: &Rect, @@ -75,7 +94,7 @@ pub trait FragmentDisplayListBuilding { /// Adds the display items necessary to paint the background linear gradient of this fragment /// to the display list at the appropriate stacking level. fn build_display_list_for_background_linear_gradient(&self, - list: &mut DisplayList, + display_list: &mut DisplayList, level: StackingLevel, absolute_bounds: &Rect, clip_rect: &Rect, @@ -86,7 +105,7 @@ pub trait FragmentDisplayListBuilding { /// necessary. fn build_display_list_for_borders_if_applicable(&self, style: &ComputedValues, - list: &mut DisplayList, + display_list: &mut DisplayList, abs_bounds: &Rect, level: StackingLevel, clip_rect: &Rect); @@ -102,11 +121,11 @@ pub trait FragmentDisplayListBuilding { flow_origin: Point2D, clip_rect: &Rect); - /// Adds the display items for this fragment to the given stacking context. + /// Adds the display items for this fragment to the given display list. /// /// Arguments: /// - /// * `display_list`: The unflattened display list to add display items to. + /// * `display_list`: The display list to add display items to. /// * `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. @@ -132,7 +151,7 @@ pub trait FragmentDisplayListBuilding { impl FragmentDisplayListBuilding for Fragment { fn build_display_list_for_background_if_applicable(&self, style: &ComputedValues, - list: &mut DisplayList, + display_list: &mut DisplayList, layout_context: &LayoutContext, level: StackingLevel, absolute_bounds: &Rect, @@ -143,10 +162,10 @@ impl FragmentDisplayListBuilding for Fragment { // doesn't have a fragment". let background_color = style.resolve_color(style.get_background().background_color); if !background_color.alpha.approx_eq(&0.0) { - list.push(SolidColorDisplayItemClass(box SolidColorDisplayItem { - base: BaseDisplayItem::new(*absolute_bounds, self.node, level, *clip_rect), + display_list.push(SolidColorDisplayItemClass(box SolidColorDisplayItem { + base: BaseDisplayItem::new(*absolute_bounds, self.node, *clip_rect), color: background_color.to_gfx_color(), - })); + }), level); } // The background image is painted on top of the background color. @@ -156,7 +175,7 @@ impl FragmentDisplayListBuilding for Fragment { match background.background_image { None => {} Some(LinearGradientImage(ref gradient)) => { - self.build_display_list_for_background_linear_gradient(list, + self.build_display_list_for_background_linear_gradient(display_list, level, absolute_bounds, clip_rect, @@ -165,7 +184,7 @@ impl FragmentDisplayListBuilding for Fragment { } Some(UrlImage(ref image_url)) => { self.build_display_list_for_background_image(style, - list, + display_list, layout_context, level, absolute_bounds, @@ -177,7 +196,7 @@ impl FragmentDisplayListBuilding for Fragment { fn build_display_list_for_background_image(&self, style: &ComputedValues, - list: &mut DisplayList, + display_list: &mut DisplayList, layout_context: &LayoutContext, level: StackingLevel, absolute_bounds: &Rect, @@ -255,16 +274,16 @@ impl FragmentDisplayListBuilding for Fragment { }; // Create the image display item. - list.push(ImageDisplayItemClass(box ImageDisplayItem { - base: BaseDisplayItem::new(bounds, self.node, level, clip_rect), + display_list.push(ImageDisplayItemClass(box ImageDisplayItem { + base: BaseDisplayItem::new(bounds, self.node, clip_rect), image: image.clone(), stretch_size: Size2D(Au::from_px(image.width as int), Au::from_px(image.height as int)), - })); + }), level); } fn build_display_list_for_background_linear_gradient(&self, - list: &mut DisplayList, + display_list: &mut DisplayList, level: StackingLevel, absolute_bounds: &Rect, clip_rect: &Rect, @@ -364,18 +383,18 @@ impl FragmentDisplayListBuilding for Fragment { absolute_bounds.origin.y + absolute_bounds.size.height / 2); let gradient_display_item = GradientDisplayItemClass(box GradientDisplayItem { - base: BaseDisplayItem::new(*absolute_bounds, self.node, level, clip_rect), + base: BaseDisplayItem::new(*absolute_bounds, self.node, clip_rect), start_point: center - delta, end_point: center + delta, stops: stops, }); - list.push(gradient_display_item) + display_list.push(gradient_display_item, level) } fn build_display_list_for_borders_if_applicable(&self, style: &ComputedValues, - list: &mut DisplayList, + display_list: &mut DisplayList, abs_bounds: &Rect, level: StackingLevel, clip_rect: &Rect) { @@ -390,8 +409,8 @@ impl FragmentDisplayListBuilding for Fragment { let left_color = style.resolve_color(style.get_border().border_left_color); // Append the border to the display list. - list.push(BorderDisplayItemClass(box BorderDisplayItem { - base: BaseDisplayItem::new(*abs_bounds, self.node, level, *clip_rect), + display_list.push(BorderDisplayItemClass(box BorderDisplayItem { + base: BaseDisplayItem::new(*abs_bounds, self.node, *clip_rect), border: border.to_physical(style.writing_mode), color: SideOffsets2D::new(top_color.to_gfx_color(), right_color.to_gfx_color(), @@ -401,7 +420,7 @@ impl FragmentDisplayListBuilding for Fragment { style.get_border().border_right_style, style.get_border().border_bottom_style, style.get_border().border_left_style) - })); + }), level); } fn build_debug_borders_around_text_fragments(&self, @@ -418,11 +437,8 @@ impl FragmentDisplayListBuilding for Fragment { fragment_bounds.size); // Compute the text fragment bounds and draw a border surrounding them. - display_list.push(BorderDisplayItemClass(box BorderDisplayItem { - base: BaseDisplayItem::new(absolute_fragment_bounds, - self.node, - ContentStackingLevel, - *clip_rect), + display_list.content.push_back(BorderDisplayItemClass(box BorderDisplayItem { + base: BaseDisplayItem::new(absolute_fragment_bounds, self.node, *clip_rect), border: SideOffsets2D::new_all_same(Au::from_px(1)), color: SideOffsets2D::new_all_same(color::rgb(0, 0, 200)), style: SideOffsets2D::new_all_same(border_style::solid) @@ -437,11 +453,11 @@ impl FragmentDisplayListBuilding for Fragment { baseline.origin = baseline.origin + flow_origin; let line_display_item = box LineDisplayItem { - base: BaseDisplayItem::new(baseline, self.node, ContentStackingLevel, *clip_rect), + base: BaseDisplayItem::new(baseline, self.node, *clip_rect), color: color::rgb(0, 200, 0), style: border_style::dashed, }; - display_list.push(LineDisplayItemClass(line_display_item)); + display_list.content.push_back(LineDisplayItemClass(line_display_item)); } fn build_debug_borders_around_fragment(&self, @@ -457,11 +473,8 @@ impl FragmentDisplayListBuilding for Fragment { fragment_bounds.size); // This prints a debug border around the border of this fragment. - display_list.push(BorderDisplayItemClass(box BorderDisplayItem { - base: BaseDisplayItem::new(absolute_fragment_bounds, - self.node, - ContentStackingLevel, - *clip_rect), + display_list.content.push_back(BorderDisplayItemClass(box BorderDisplayItem { + base: BaseDisplayItem::new(absolute_fragment_bounds, self.node, *clip_rect), border: SideOffsets2D::new_all_same(Au::from_px(1)), color: SideOffsets2D::new_all_same(color::rgb(0, 0, 200)), style: SideOffsets2D::new_all_same(border_style::solid) @@ -474,14 +487,24 @@ impl FragmentDisplayListBuilding for Fragment { flow_origin: Point2D, background_and_border_level: BackgroundAndBorderLevel, clip_rect: &Rect) { + // Compute the fragment position relative to the parent stacking context. If the fragment + // itself establishes a stacking context, then the origin of its position will be (0, 0) + // for the purposes of this computation. + let stacking_relative_flow_origin = if self.establishes_stacking_context() { + ZERO_POINT + } else { + flow_origin + }; + let absolute_fragment_bounds = + self.stacking_relative_bounds(&stacking_relative_flow_origin); + // FIXME(#2795): Get the real container size let container_size = Size2D::zero(); let rect_to_absolute = |writing_mode: WritingMode, logical_rect: LogicalRect| { let physical_rect = logical_rect.to_physical(writing_mode, container_size); - Rect(physical_rect.origin + flow_origin, physical_rect.size) + Rect(physical_rect.origin + stacking_relative_flow_origin, physical_rect.size) }; - // Fragment position wrt to the owning flow. - 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, @@ -512,9 +535,8 @@ impl FragmentDisplayListBuilding for 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, *clip_rect); - display_list.push(PseudoDisplayItemClass(base_display_item)); + display_list.push(PseudoDisplayItemClass(base_display_item), level); // Add the background to the list, if applicable. match self.inline_context { @@ -593,17 +615,15 @@ impl FragmentDisplayListBuilding for Fragment { }; let metrics = &text_fragment.run.font_metrics; - let baseline_origin ={ - let mut tmp = content_box.start; - tmp.b = tmp.b + metrics.ascent; - tmp.to_physical(self.style.writing_mode, container_size) + flow_origin + let baseline_origin = { + let mut content_box_start = content_box.start; + content_box_start.b = content_box_start.b + metrics.ascent; + content_box_start.to_physical(self.style.writing_mode, container_size) + + flow_origin }; - display_list.push(TextDisplayItemClass(box TextDisplayItem { - base: BaseDisplayItem::new(absolute_content_box, - self.node, - ContentStackingLevel, - *clip_rect), + display_list.content.push_back(TextDisplayItemClass(box TextDisplayItem { + base: BaseDisplayItem::new(absolute_content_box, self.node, *clip_rect), text_run: text_fragment.run.clone(), range: text_fragment.range, text_color: self.style().get_color().color.to_gfx_color(), @@ -615,19 +635,14 @@ impl FragmentDisplayListBuilding for Fragment { { let line = |maybe_color: Option, rect: || -> LogicalRect| { match maybe_color { - None => {}, + None => {} Some(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(), - })); + let bounds = rect_to_absolute(self.style.writing_mode, rect()); + display_list.content.push_back(SolidColorDisplayItemClass( + box SolidColorDisplayItem { + base: BaseDisplayItem::new(bounds, self.node, *clip_rect), + color: color.to_gfx_color(), + })) } } }; @@ -678,10 +693,9 @@ impl FragmentDisplayListBuilding for Fragment { debug!("(building display list) building image fragment"); // Place the image into the display list. - display_list.push(ImageDisplayItemClass(box ImageDisplayItem { + display_list.content.push_back(ImageDisplayItemClass(box ImageDisplayItem { base: BaseDisplayItem::new(absolute_content_box, self.node, - ContentStackingLevel, *clip_rect), image: image.clone(), stretch_size: absolute_content_box.size, @@ -698,7 +712,9 @@ impl FragmentDisplayListBuilding for Fragment { } if opts::get().show_debug_fragment_borders { - self.build_debug_borders_around_fragment(display_list, flow_origin, clip_rect) + 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. @@ -766,6 +782,10 @@ impl FragmentDisplayListBuilding for Fragment { } pub trait BlockFlowDisplayListBuilding { + fn build_display_list_for_block_base(&mut self, + display_list: &mut DisplayList, + layout_context: &LayoutContext, + background_border_level: BackgroundAndBorderLevel); fn build_display_list_for_block(&mut self, layout_context: &LayoutContext, background_border_level: BackgroundAndBorderLevel); @@ -775,78 +795,93 @@ pub trait BlockFlowDisplayListBuilding { } impl BlockFlowDisplayListBuilding for BlockFlow { - fn build_display_list_for_block(&mut self, - layout_context: &LayoutContext, - background_border_level: BackgroundAndBorderLevel) { - + fn build_display_list_for_block_base(&mut self, + display_list: &mut DisplayList, + layout_context: &LayoutContext, + background_border_level: BackgroundAndBorderLevel) { // Add the box that starts the block context. - let absolute_fragment_origin = self.base.child_fragment_absolute_position(&self.fragment); - self.fragment.build_display_list(&mut self.base.display_list, + let stacking_relative_fragment_origin = + self.base.stacking_relative_position_of_child_fragment(&self.fragment); + self.fragment.build_display_list(display_list, layout_context, - absolute_fragment_origin, + stacking_relative_fragment_origin, background_border_level, &self.base.clip_rect); - self.base.layers = DList::new(); for kid in self.base.children.iter_mut() { if flow::base(kid).flags.is_absolutely_positioned() { // All absolute flows will be handled by their containing block. continue } - self.base.display_list.append_from(&mut flow::mut_base(kid).display_list); - dlist::append_from(&mut self.base.layers, &mut flow::mut_base(kid).layers) + flow::mut_base(kid).display_list_building_result.add_to(display_list); } // Process absolute descendant links. for abs_descendant_link in self.base.abs_descendants.iter() { // TODO(pradeep): Send in our absolute position directly. - self.base - .display_list - .append_from(&mut flow::mut_base(abs_descendant_link).display_list); - dlist::append_from(&mut self.base.layers, - &mut flow::mut_base(abs_descendant_link).layers) + flow::mut_base(abs_descendant_link).display_list_building_result.add_to(display_list); } } + fn build_display_list_for_block(&mut self, + layout_context: &LayoutContext, + background_border_level: BackgroundAndBorderLevel) { + let mut display_list = box DisplayList::new(); + self.build_display_list_for_block_base(&mut *display_list, + layout_context, + background_border_level); + self.base.display_list_building_result = DisplayListResult(display_list); + } + fn build_display_list_for_absolutely_positioned_block(&mut self, layout_context: &LayoutContext) { - self.build_display_list_for_block(layout_context, RootOfStackingContextLevel); + let mut display_list = box DisplayList::new(); + self.build_display_list_for_block_base(&mut *display_list, + layout_context, + RootOfStackingContextLevel); + + let bounds = Rect(self.base.stacking_relative_position, + self.base.overflow.size.to_physical(self.base.writing_mode)); + let z_index = self.fragment.style().get_box().z_index.number_or_zero(); if !self.base.absolute_position_info.layers_needed_for_positioned_flows && !self.base.flags.needs_layer() { // We didn't need a layer. - let z_index = self.fragment.style().get_box().z_index.number_or_zero(); - self.base.display_list.flatten(PositionedDescendantStackingLevel(z_index)); + self.base.display_list_building_result = + StackingContextResult(Arc::new(StackingContext::new(display_list, + bounds, + z_index, + None))); return } // If we got here, then we need a new layer. - let layer_rect = self.base.position.union(&self.base.overflow); - let size = Size2D(layer_rect.size.inline.to_nearest_px() as uint, - layer_rect.size.block.to_nearest_px() as uint); - let origin = Point2D(self.base.abs_position.x.to_nearest_px() as uint, - self.base.abs_position.y.to_nearest_px() as uint); - let scroll_policy = if self.is_fixed() { FixedPosition } else { Scrollable }; - self.base.display_list.flatten(ContentStackingLevel); - let new_layer = RenderLayer { - id: self.layer_id(0), - display_list: Arc::new(mem::replace(&mut self.base.display_list, DisplayList::new())), - position: Rect(origin, size), - background_color: color::rgba(1.0, 1.0, 1.0, 0.0), - scroll_policy: scroll_policy, - }; - self.base.layers.push_back(new_layer) + + let transparent = color::rgba(1.0, 1.0, 1.0, 0.0); + let stacking_context = + Arc::new(StackingContext::new(display_list, + bounds, + z_index, + Some(Arc::new(RenderLayer::new(self.layer_id(0), + transparent, + scroll_policy))))); + + self.base.display_list_building_result = StackingContextResult(stacking_context) } fn build_display_list_for_floating_block(&mut self, layout_context: &LayoutContext) { - self.build_display_list_for_block(layout_context, RootOfStackingContextLevel); - self.base.display_list.flatten(FloatStackingLevel) + let mut display_list = box DisplayList::new(); + self.build_display_list_for_block_base(&mut *display_list, + layout_context, + RootOfStackingContextLevel); + display_list.form_float_pseudo_stacking_context(); + self.base.display_list_building_result = DisplayListResult(display_list); } } @@ -873,3 +908,51 @@ fn position_to_offset(position: LengthOrPercentage, Au(total_length): Au) -> f32 } } +/// "Steps" as defined by CSS 2.1 § E.2. +#[deriving(Clone, PartialEq, Show)] +pub enum StackingLevel { + /// The border and backgrounds for the root of this stacking context: steps 1 and 2. + BackgroundAndBordersStackingLevel, + /// Borders and backgrounds for block-level descendants: step 4. + BlockBackgroundsAndBordersStackingLevel, + /// All other content. + ContentStackingLevel, +} + +impl StackingLevel { + #[inline] + pub fn from_background_and_border_level(level: BackgroundAndBorderLevel) -> StackingLevel { + match level { + RootOfStackingContextLevel => BackgroundAndBordersStackingLevel, + BlockLevel => BlockBackgroundsAndBordersStackingLevel, + ContentLevel => ContentStackingLevel, + } + } +} + +/// Which level to place backgrounds and borders in. +pub enum BackgroundAndBorderLevel { + RootOfStackingContextLevel, + BlockLevel, + ContentLevel, +} + +trait StackingContextConstruction { + /// Adds the given display item at the specified level to this display list. + fn push(&mut self, display_item: DisplayItem, level: StackingLevel); +} + +impl StackingContextConstruction for DisplayList { + fn push(&mut self, display_item: DisplayItem, level: StackingLevel) { + match level { + BackgroundAndBordersStackingLevel => { + self.background_and_borders.push_back(display_item) + } + BlockBackgroundsAndBordersStackingLevel => { + self.block_backgrounds_and_borders.push_back(display_item) + } + ContentStackingLevel => self.content.push_back(display_item), + } + } +} + diff --git a/servo/components/layout/flow.rs b/servo/components/layout/flow.rs index e22ea27c36e4..41cc5570a60a 100644 --- a/servo/components/layout/flow.rs +++ b/servo/components/layout/flow.rs @@ -28,6 +28,8 @@ use css::node_style::StyledNode; use block::BlockFlow; use context::LayoutContext; +use display_list_builder::{DisplayListBuildingResult, DisplayListResult}; +use display_list_builder::{NoDisplayListBuildingResult, StackingContextResult}; use floats::Floats; use flow_list::{FlowList, FlowListIterator, MutFlowListIterator}; use flow_ref::FlowRef; @@ -45,10 +47,7 @@ use table_rowgroup::TableRowGroupFlow; use table_wrapper::TableWrapperFlow; use wrapper::ThreadSafeLayoutNode; -use collections::dlist::DList; use geom::{Point2D, Rect, Size2D}; -use gfx::display_list::DisplayList; -use gfx::render_task::RenderLayer; use serialize::{Encoder, Encodable}; use servo_msg::compositor_msg::LayerId; use servo_util::geometry::Au; @@ -681,8 +680,10 @@ 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, + /// The position of the absolute containing block relative to the nearest ancestor stacking + /// context. If the absolute containing block establishes the stacking context for this flow, + /// and this flow is not itself absolutely-positioned, then this is (0, 0). + pub stacking_relative_position_of_absolute_containing_block: Point2D, /// Whether the absolute containing block forces positioned descendants to be layerized. /// @@ -696,7 +697,7 @@ impl AbsolutePositionInfo { // of the root layer. AbsolutePositionInfo { relative_containing_block_size: LogicalSize::zero(writing_mode), - absolute_containing_block_position: Zero::zero(), + stacking_relative_position_of_absolute_containing_block: Zero::zero(), layers_needed_for_positioned_flows: false, } } @@ -742,8 +743,9 @@ pub struct BaseFlow { /// The collapsible margins for this flow, if any. pub collapsible_margins: CollapsibleMargins, - /// The position of this flow in page coordinates, computed during display list construction. - pub abs_position: Point2D, + /// The position of this flow relative to the start of the nearest ancestor stacking context. + /// This is computed during the top-down pass of display list construction. + pub stacking_relative_position: Point2D, /// Details about descendants with position 'absolute' or 'fixed' for which we are the /// containing block. This is in tree order. This includes any direct children. @@ -779,11 +781,8 @@ pub struct BaseFlow { /// rectangles. pub clip_rect: Rect, - /// The unflattened display items for this flow. - pub display_list: DisplayList, - - /// Any layers that we're bubbling up, in a linked list. - pub layers: DList, + /// The results of display list building for this flow. + pub display_list_building_result: DisplayListBuildingResult, /// The writing mode for this flow. pub writing_mode: WritingMode, @@ -806,8 +805,12 @@ impl> Encodable for BaseFlow { fn encode(&self, e: &mut S) -> Result<(), E> { e.emit_struct("base", 0, |e| { try!(e.emit_struct_field("id", 0, |e| self.debug_id().encode(e))) - try!(e.emit_struct_field("abs_position", 1, |e| self.abs_position.encode(e))) - try!(e.emit_struct_field("intrinsic_inline_sizes", 2, |e| self.intrinsic_inline_sizes.encode(e))) + try!(e.emit_struct_field("stacking_relative_position", + 1, + |e| self.stacking_relative_position.encode(e))) + try!(e.emit_struct_field("intrinsic_inline_sizes", + 2, + |e| self.intrinsic_inline_sizes.encode(e))) try!(e.emit_struct_field("position", 3, |e| self.position.encode(e))) e.emit_struct_field("children", 4, |e| { e.emit_seq(self.children.len(), |e| { @@ -893,15 +896,14 @@ impl BaseFlow { parallel: FlowParallelInfo::new(), floats: Floats::new(writing_mode), collapsible_margins: CollapsibleMargins::new(), - abs_position: Zero::zero(), + stacking_relative_position: Zero::zero(), abs_descendants: Descendants::new(), absolute_static_i_offset: Au(0), fixed_static_i_offset: Au(0), block_container_inline_size: Au(0), block_container_explicit_block_size: None, absolute_cb: ContainingBlockLink::new(), - display_list: DisplayList::new(), - layers: DList::new(), + display_list_building_result: NoDisplayListBuildingResult, absolute_position_info: AbsolutePositionInfo::new(writing_mode), clip_rect: Rect(Zero::zero(), Size2D(Au(0), Au(0))), flags: flags, @@ -922,13 +924,23 @@ impl BaseFlow { p as uint } + /// Ensures that all display list items generated by this flow are within the flow's overflow + /// rect. This should only be used for debugging. pub fn validate_display_list_geometry(&self) { let position_with_overflow = self.position.union(&self.overflow); - let bounds = Rect(self.abs_position, + let bounds = Rect(self.stacking_relative_position, Size2D(position_with_overflow.size.inline, position_with_overflow.size.block)); - for item in self.display_list.iter() { + let all_items = match self.display_list_building_result { + NoDisplayListBuildingResult => Vec::new(), + StackingContextResult(ref stacking_context) => { + stacking_context.display_list.all_display_items() + } + DisplayListResult(ref display_list) => display_list.all_display_items(), + }; + + for item in all_items.iter() { let paint_bounds = match item.base().bounds.intersection(&item.base().clip_rect) { None => continue, Some(rect) => rect, @@ -944,12 +956,15 @@ impl BaseFlow { } } - pub fn child_fragment_absolute_position(&self, fragment: &Fragment) -> Point2D { + /// Returns the position of the given fragment relative to the start of the nearest ancestor + /// stacking context. The fragment must be a child fragment of this flow. + pub fn stacking_relative_position_of_child_fragment(&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)) + self.stacking_relative_position.add_size(&relative_offset.to_physical(self.writing_mode)) } } diff --git a/servo/components/layout/fragment.rs b/servo/components/layout/fragment.rs index d9fd9db48394..def9cd337764 100644 --- a/servo/components/layout/fragment.rs +++ b/servo/components/layout/fragment.rs @@ -1481,11 +1481,31 @@ impl Fragment { self.style = (*new_style).clone() } - pub fn abs_bounds_from_origin(&self, fragment_origin: &Point2D) -> Rect { + /// Given the stacking-context-relative position of the containing flow, returns the boundaries + /// of this fragment relative to the parent stacking context. + pub fn stacking_relative_bounds(&self, stacking_relative_flow_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) + self.border_box + .to_physical(self.style.writing_mode, container_size) + .translate(stacking_relative_flow_origin) + } + + /// Returns true if this fragment establishes a new stacking context and false otherwise. + pub fn establishes_stacking_context(&self) -> bool { + match self.style().get_box().position { + position::absolute | position::fixed => { + // FIXME(pcwalton): This should only establish a new stacking context when + // `z-index` is not `auto`. But this matches what we did before. + true + } + position::relative | position::static_ => { + // FIXME(pcwalton): `position: relative` establishes a new stacking context if + // `z-index` is not `auto`. But this matches what we did before. + false + } + } } } diff --git a/servo/components/layout/inline.rs b/servo/components/layout/inline.rs index c6ca65fd799f..d1a6b22b992b 100644 --- a/servo/components/layout/inline.rs +++ b/servo/components/layout/inline.rs @@ -6,7 +6,7 @@ use css::node_style::StyledNode; use context::LayoutContext; -use display_list_builder::FragmentDisplayListBuilding; +use display_list_builder::{ContentLevel, DisplayListResult, FragmentDisplayListBuilding}; use floats::{FloatLeft, Floats, PlacementInfo}; use flow::{BaseFlow, FlowClass, Flow, InlineFlowClass, MutableFlowUtils}; use flow; @@ -20,7 +20,7 @@ use text; use collections::{RingBuf}; use geom::{Rect, Size2D}; -use gfx::display_list::ContentLevel; +use gfx::display_list::DisplayList; use gfx::font::FontMetrics; use gfx::font_context::FontContext; use gfx::text::glyph::CharIndex; @@ -1136,34 +1136,34 @@ impl Flow for InlineFlow { fn compute_absolute_position(&mut self) { for fragment in self.fragments.fragments.iter_mut() { - let absolute_position = match fragment.specific { + let stacking_relative_position = match fragment.specific { InlineBlockFragment(ref mut info) => { let block_flow = info.flow_ref.as_block(); // FIXME(#2795): Get the real container size let container_size = Size2D::zero(); - block_flow.base.abs_position = - self.base.abs_position + + block_flow.base.stacking_relative_position = + self.base.stacking_relative_position + fragment.border_box.start.to_physical(self.base.writing_mode, container_size); - block_flow.base.abs_position + block_flow.base.stacking_relative_position } InlineAbsoluteHypotheticalFragment(ref mut info) => { let block_flow = info.flow_ref.as_block(); // FIXME(#2795): Get the real container size let container_size = Size2D::zero(); - block_flow.base.abs_position = - self.base.abs_position + + block_flow.base.stacking_relative_position = + self.base.stacking_relative_position + fragment.border_box.start.to_physical(self.base.writing_mode, container_size); - block_flow.base.abs_position + block_flow.base.stacking_relative_position } _ => continue, }; let clip_rect = fragment.clip_rect_for_children(self.base.clip_rect, - absolute_position); + stacking_relative_position); match fragment.specific { InlineBlockFragment(ref mut info) => { @@ -1183,11 +1183,11 @@ impl Flow for InlineFlow { fn build_display_list(&mut self, layout_context: &LayoutContext) { let size = self.base.position.size.to_physical(self.base.writing_mode); - if !Rect(self.base.abs_position, size).intersects(&layout_context.shared.dirty) { - debug!("inline block (abs pos {}, size {}) didn't intersect \ - dirty rect two", - self.base.abs_position, - size); + if !Rect(self.base.stacking_relative_position, size).intersects(&layout_context.shared + .dirty) { + debug!("inline block (stacking relative pos {}, size {}) didn't intersect dirty rect", + self.base.stacking_relative_position, + size); return } @@ -1195,9 +1195,10 @@ impl Flow for InlineFlow { // not recurse on a line if nothing in it can intersect the dirty region. debug!("Flow: building display list for {:u} inline fragments", self.fragments.len()); + let mut display_list = box DisplayList::new(); for fragment in self.fragments.fragments.iter_mut() { - let fragment_origin = self.base.child_fragment_absolute_position(fragment); - fragment.build_display_list(&mut self.base.display_list, + let fragment_origin = self.base.stacking_relative_position_of_child_fragment(fragment); + fragment.build_display_list(&mut *display_list, layout_context, fragment_origin, ContentLevel, @@ -1205,14 +1206,15 @@ impl Flow for InlineFlow { match fragment.specific { InlineBlockFragment(ref mut block_flow) => { let block_flow = block_flow.flow_ref.deref_mut(); - self.base - .display_list - .append_from(&mut flow::mut_base(block_flow).display_list) + flow::mut_base(block_flow).display_list_building_result + .add_to(&mut *display_list) } _ => {} } } + self.base.display_list_building_result = DisplayListResult(display_list); + if opts::get().validate_display_list_geometry { self.base.validate_display_list_geometry(); } @@ -1223,8 +1225,9 @@ impl Flow for InlineFlow { 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)); + let fragment_origin = + self.base.stacking_relative_position_of_child_fragment(fragment); + iterator.process(fragment, fragment.stacking_relative_bounds(&fragment_origin)); } } } diff --git a/servo/components/layout/layout_task.rs b/servo/components/layout/layout_task.rs index 15d9daccbc86..8fe2afb35ab4 100644 --- a/servo/components/layout/layout_task.rs +++ b/servo/components/layout/layout_task.rs @@ -19,15 +19,13 @@ use sequential; use util::{LayoutDataAccess, LayoutDataWrapper, OpaqueNodeMethods, ToGfxColor}; use wrapper::{LayoutNode, TLayoutNode, ThreadSafeLayoutNode}; -use collections::dlist::DList; use encoding::EncodingRef; use encoding::all::UTF_8; use geom::point::Point2D; use geom::rect::Rect; use geom::size::Size2D; use geom::scale_factor::ScaleFactor; -use gfx::display_list::{ContentStackingLevel, DisplayItem, DisplayList}; -use gfx::display_list::{OpaqueNode}; +use gfx::display_list::{DisplayList, OpaqueNode, StackingContext}; use gfx::render_task::{RenderInitMsg, RenderChan, RenderLayer}; use gfx::{render_task, color}; use layout_traits; @@ -51,7 +49,6 @@ use gfx::font_cache_task::{FontCacheTask}; use servo_net::local_image_cache::{ImageResponder, LocalImageCache}; use servo_net::resource_task::{ResourceTask, load_bytes_iter}; use servo_util::geometry::Au; -use servo_util::geometry; use servo_util::logical_geometry::LogicalPoint; use servo_util::opts; use servo_util::smallvec::{SmallVec, SmallVec1, VecLike}; @@ -79,8 +76,8 @@ pub struct LayoutTaskData { /// The size of the viewport. pub screen_size: Size2D, - /// A cached display list. - pub display_list: Option>, + /// The root stacking context. + pub stacking_context: Option>, pub stylist: Box, @@ -277,7 +274,7 @@ impl LayoutTask { LayoutTaskData { local_image_cache: local_image_cache, screen_size: screen_size, - display_list: None, + stacking_context: None, stylist: box Stylist::new(device), parallel_traversal: parallel_traversal, dirty: Rect::zero(), @@ -621,7 +618,7 @@ impl LayoutTask { 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 = + flow::mut_base(layout_root.deref_mut()).stacking_relative_position = LogicalPoint::zero(writing_mode).to_physical(writing_mode, rw_data.screen_size); @@ -643,13 +640,7 @@ impl LayoutTask { } } - 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())); + debug!("Done building display list."); // FIXME(pcwalton): This is really ugly and can't handle overflow: scroll. Refactor // it with extreme prejudice. @@ -678,31 +669,23 @@ impl LayoutTask { 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, - }; + let mut display_list = box DisplayList::new(); + flow::mut_base(layout_root.deref_mut()).display_list_building_result + .add_to(&mut *display_list); + let render_layer = Arc::new(RenderLayer::new(layout_root.layer_id(0), + color, + Scrollable)); + let origin = Rect(Point2D(Au(0), Au(0)), root_size); + let stacking_context = Arc::new(StackingContext::new(display_list, + origin, + 0, + Some(render_layer))); - 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) - } + rw_data.stacking_context = Some(stacking_context.clone()); debug!("Layout done!"); - self.render_chan.send(RenderInitMsg(layers)); + self.render_chan.send(RenderInitMsg(stacking_context)); }); } @@ -933,27 +916,23 @@ impl LayoutRPC for LayoutRPCImpl { ContentBoxesResponse(rw_data.content_boxes_response.clone()) } - /// Requests the node containing the point of interest + /// Requests the node containing the point of interest. fn hit_test(&self, _: TrustedNodeAddress, point: Point2D) -> Result { - fn hit_test<'a,I>(point: Point2D, mut iterator: I) - -> Option - where I: Iterator<&'a DisplayItem> { - for item in iterator { - // 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())) - } - } - None - } 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 => panic!("no display list!"), - Some(ref display_list) => hit_test(point, display_list.list.iter().rev()), + match rw_data.stacking_context { + None => panic!("no root stacking context!"), + Some(ref stacking_context) => { + let mut result = Vec::new(); + stacking_context.hit_test(point, &mut result, true); + if !result.is_empty() { + Some(HitTestResponse(result[0])) + } else { + None + } + } } }; @@ -965,29 +944,17 @@ impl LayoutRPC for LayoutRPCImpl { 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 { - // 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 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 => panic!("no display list!"), - Some(ref display_list) => { - mouse_over_test(point, display_list.list.iter().rev(), &mut mouse_over_list); + match rw_data.stacking_context { + None => panic!("no root stacking context!"), + Some(ref stacking_context) => { + stacking_context.hit_test(point, &mut mouse_over_list, false); } - }; + } } if mouse_over_list.is_empty() { diff --git a/servo/components/util/dlist.rs b/servo/components/util/dlist.rs index 52836786de38..f22c07268bad 100644 --- a/servo/components/util/dlist.rs +++ b/servo/components/util/dlist.rs @@ -96,3 +96,30 @@ pub fn append_from(this: &mut DList, other: &mut DList) { other.length = 0; } } + +/// Prepends the items in the other list to this one, leaving the other list empty. +#[inline] +pub fn prepend_from(this: &mut DList, other: &mut DList) { + unsafe { + let this = mem::transmute::<&mut DList,&mut RawDList>(this); + let other = mem::transmute::<&mut DList,&mut RawDList>(other); + if this.length == 0 { + this.head = mem::replace(&mut other.head, ptr::null_mut()); + this.tail = mem::replace(&mut other.tail, ptr::null_mut()); + this.length = mem::replace(&mut other.length, 0); + return + } + + let old_other_tail = mem::replace(&mut other.tail, ptr::null_mut()); + if old_other_tail.is_null() { + return + } + (*old_other_tail).next = this.head; + (*this.head).prev = old_other_tail; + + this.head = mem::replace(&mut other.head, ptr::null_mut()); + this.length += other.length; + other.length = 0; + } +} + diff --git a/servo/components/util/geometry.rs b/servo/components/util/geometry.rs index 8dd6d633da7c..06b74c7c3a0b 100644 --- a/servo/components/util/geometry.rs +++ b/servo/components/util/geometry.rs @@ -74,6 +74,11 @@ impl Default for Au { } } +pub static ZERO_POINT: Point2D = Point2D { + x: Au(0), + y: Au(0), +}; + pub static ZERO_RECT: Rect = Rect { origin: Point2D { x: Au(0),