diff options
-rw-r--r-- | components/gfx/display_list/mod.rs | 470 | ||||
-rw-r--r-- | components/gfx/display_list/optimizer.rs | 64 | ||||
-rw-r--r-- | components/gfx/lib.rs | 1 | ||||
-rw-r--r-- | components/gfx/render_context.rs | 4 | ||||
-rw-r--r-- | components/gfx/render_task.rs | 144 | ||||
-rw-r--r-- | components/layout/block.rs | 93 | ||||
-rw-r--r-- | components/layout/display_list_builder.rs | 305 | ||||
-rw-r--r-- | components/layout/flow.rs | 59 | ||||
-rw-r--r-- | components/layout/fragment.rs | 26 | ||||
-rw-r--r-- | components/layout/inline.rs | 47 | ||||
-rw-r--r-- | components/layout/layout_task.rs | 107 | ||||
-rw-r--r-- | components/util/dlist.rs | 27 | ||||
-rw-r--r-- | components/util/geometry.rs | 5 |
13 files changed, 807 insertions, 545 deletions
diff --git a/components/gfx/display_list/mod.rs b/components/gfx/display_list/mod.rs index 55782463436..88413b350a8 100644 --- a/components/gfx/display_list/mod.rs +++ b/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 { +/// 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 { /// The border and backgrounds for the root of this stacking context: steps 1 and 2. - BackgroundAndBordersStackingLevel, + pub background_and_borders: DList<DisplayItem>, /// Borders and backgrounds for block-level descendants: step 4. - BlockBackgroundsAndBordersStackingLevel, + pub block_backgrounds_and_borders: DList<DisplayItem>, /// Floats: step 5. These are treated as pseudo-stacking contexts. - FloatStackingLevel, + pub floats: DList<DisplayItem>, /// 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) + pub content: DList<DisplayItem>, + /// Child stacking contexts. + pub children: DList<Arc<StackingContext>>, } -impl StackingLevel { +impl DisplayList { + /// Creates a new, empty display list. #[inline] - pub fn from_background_and_border_level(level: BackgroundAndBorderLevel) -> StackingLevel { - match level { - RootOfStackingContextLevel => BackgroundAndBordersStackingLevel, - BlockLevel => BlockBackgroundsAndBordersStackingLevel, - ContentLevel => ContentStackingLevel, + pub fn new() -> DisplayList { + DisplayList { + background_and_borders: DList::new(), + block_backgrounds_and_borders: DList::new(), + floats: DList::new(), + content: DList::new(), + children: DList::new(), } } -} -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. + /// Appends all display items from `other` into `self`, preserving stacking order and emptying + /// `other` in the process. #[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(), - } + pub fn append_from(&mut self, other: &mut DisplayList) { + 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); } - /// 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 => {} - } + /// Merges all display items from all non-float stacking levels to the `float` stacking level. + #[inline] + 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); + } - self.positioned_descendants.push((z_index, head)) - } - } + /// 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<DisplayItem> { + let mut result = Vec::new(); + for display_item in self.background_and_borders.iter() { + result.push((*display_item).clone()) + } + for display_item in self.block_backgrounds_and_borders.iter() { + result.push((*display_item).clone()) + } + 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 } } -/// 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)] -pub struct DisplayList { - pub list: DList<DisplayItem>, -} - -pub enum DisplayListIterator<'a> { - EmptyDisplayListIterator, - ParentDisplayListIterator(Items<'a,DisplayList>), +/// 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<DisplayList>, + /// The layer for this stacking context, if there is one. + pub layer: Option<Arc<RenderLayer>>, + /// The position and size of this stacking context. + pub bounds: Rect<Au>, + /// The clipping rect for this stacking context, in the coordinate system of the *parent* + /// stacking context. + pub clip_rect: Rect<Au>, + /// The `z-index` for this stacking context. + pub z_index: i32, } -impl<'a> Iterator<&'a DisplayList> for DisplayListIterator<'a> { +impl StackingContext { + /// Creates a new stacking context. + /// + /// TODO(pcwalton): Stacking contexts should not always be clipped to their bounds, to handle + /// overflow properly. #[inline] - fn next(&mut self) -> Option<&'a DisplayList> { - match *self { - EmptyDisplayListIterator => None, - ParentDisplayListIterator(ref mut subiterator) => subiterator.next(), + pub fn new(display_list: Box<DisplayList>, + bounds: Rect<Au>, + z_index: i32, + layer: Option<Arc<RenderLayer>>) + -> StackingContext { + StackingContext { + display_list: display_list, + layer: layer, + bounds: bounds, + clip_rect: bounds, + z_index: z_index, } } -} -impl DisplayList { - /// Creates a new display list. - #[inline] - pub fn new() -> DisplayList { - DisplayList { - list: DList::new(), - } - } + /// 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<AzFloat>, + current_transform: &Matrix2D<AzFloat>, + current_clip_stack: &mut Vec<Rect<Au>>) { + // 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); - /// Creates a new display list from the given list of display items. - fn from_list(list: DList<DisplayItem>) -> DisplayList { - DisplayList { - list: list, + // Sort positioned children according to z-index. + let mut positioned_children = SmallVec8::new(); + for kid in display_list.children.iter() { + positioned_children.push((*kid).clone()); } - } - - /// Appends the given item to the display list. - #[inline] - pub fn push(&mut self, item: DisplayItem) { - self.list.push_back(item); - } + positioned_children.as_slice_mut().sort_by(|this, other| this.z_index.cmp(&other.z_index)); - /// Appends the items in the given display list to this one, removing them in the process. - #[inline] - pub fn append_from(&mut self, other: &mut DisplayList) { - servo_dlist::append_from(&mut self.list, &mut other.list) - } + // 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) + } - /// Returns the first display item in this list. - #[inline] - fn front(&self) -> Option<&DisplayItem> { - self.list.front() - } + // Step 3: Positioned descendants with negative z-indices. + 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); + } + } - pub fn debug(&self) { - for item in self.list.iter() { - item.debug_with_level(0); + // Step 4: 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) } - } - /// 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<AzFloat>, - current_clip_stack: &mut Vec<Rect<Au>>) { - debug!("Beginning display list."); - for item in self.list.iter() { - item.draw_into_context(render_context, current_transform, current_clip_stack) + // Step 5: Floats. + for display_item in display_list.floats.iter() { + display_item.draw_into_context(render_context, current_transform, current_clip_stack) } - debug!("Ending display list."); - } - /// Returns a preorder iterator over the given display list. - #[inline] - pub fn iter<'a>(&'a self) -> DisplayItemIterator<'a> { - ParentDisplayItemIterator(self.list.iter()) - } + // TODO(pcwalton): Step 6: Inlines that generate stacking contexts. - /// Flattens a display list into a display list with a single stacking level according to the - /// steps in CSS 2.1 § E.2. - /// - /// 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 + // Step 7: Content. + for display_item in display_list.content.iter() { + display_item.draw_into_context(render_context, current_transform, current_clip_stack) } - if self.list.len() == 1 { - self.set_stacking_level(resulting_level); - return + + // Steps 8 and 9: Positioned descendants with nonnegative z-indices. + 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); + } } - let mut stacking_context = StackingContext::new(); - stacking_context.init_from_list(self); - debug_assert!(self.list.is_empty()); + // TODO(pcwalton): Step 10: Outlines. + } - // Steps 1 and 2: Borders and background for the root. - self.append_from(&mut stacking_context.background_and_borders); + /// 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<AzFloat>, + child_stacking_context: &StackingContext) + -> Rect<AzFloat> { + static ZERO_AZURE_RECT: Rect<f32> = Rect { + origin: Point2D { + x: 0.0, + y: 0.0, + }, + size: Size2D { + width: 0.0, + height: 0.0 + } + }; - // 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 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) + } - // 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) + /// 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<Au>, + result: &mut Vec<UntrustedNodeAddress>, + topmost_only: bool) { + fn hit_test_in_list<'a,I>(point: Point2D<Au>, + result: &mut Vec<UntrustedNodeAddress>, + 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 + } + } } } - // Step 4: Block backgrounds and borders. - self.append_from(&mut stacking_context.block_backgrounds_and_borders); + debug_assert!(!topmost_only || result.is_empty()); - // Step 5: Floats. - self.append_from(&mut stacking_context.floats); + // 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 7: Content. - self.append_from(&mut stacking_context.content); - - // 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) + // 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 } } - // TODO(pcwalton): Step 10: Outlines. + // 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()) + } +} - self.set_stacking_level(resulting_level); +/// 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<StackingContext>, layer_id: LayerId) + -> Option<Arc<StackingContext>> { + match this.layer { + Some(ref layer) if layer.id == layer_id => return Some((*this).clone()), + Some(_) | None => {} } - /// 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; + 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<Au>, node: OpaqueNode, level: StackingLevel, clip_rect: Rect<Au>) - -> BaseDisplayItem { + pub fn new(bounds: Rect<Au>, node: OpaqueNode, clip_rect: Rect<Au>) -> 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<AzFloat>, current_clip_stack: &mut Vec<Rect<Au>>) { - // 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/components/gfx/display_list/optimizer.rs b/components/gfx/display_list/optimizer.rs index 69cdc469de3..9ce3fbfc3f1 100644 --- a/components/gfx/display_list/optimizer.rs +++ b/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<DisplayList>, /// The visible rect in page coordinates. visible_rect: Rect<Au>, } impl DisplayListOptimizer { - /// `visible_rect` specifies the visible rect in page coordinates. - pub fn new(display_list: Arc<DisplayList>, visible_rect: Rect<Au>) -> DisplayListOptimizer { + /// Creates a new display list optimizer object. `visible_rect` specifies the visible rect in + /// page coordinates. + pub fn new(visible_rect: &Rect<f32>) -> 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<DisplayItem>, + 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<DisplayItem> { - // 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<Arc<StackingContext>>, + mut stacking_contexts: I) + where I: Iterator<&'a Arc<StackingContext>> { + 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/components/gfx/lib.rs b/components/gfx/lib.rs index c267e139fe1..701390e072c 100644 --- a/components/gfx/lib.rs +++ b/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/components/gfx/render_context.rs b/components/gfx/render_context.rs index c8c1df2cc63..ea7771efb9d 100644 --- a/components/gfx/render_context.rs +++ b/components/gfx/render_context.rs @@ -467,7 +467,7 @@ impl<'a> RenderContext<'a> { } } -trait ToAzurePoint { +pub trait ToAzurePoint { fn to_azure_point(&self) -> Point2D<AzFloat>; } @@ -477,7 +477,7 @@ impl ToAzurePoint for Point2D<Au> { } } -trait ToAzureRect { +pub trait ToAzureRect { fn to_azure_rect(&self) -> Rect<AzFloat>; } diff --git a/components/gfx/render_task.rs b/components/gfx/render_task.rs index e1d190ac5fa..6dc3335faff 100644 --- a/components/gfx/render_task.rs +++ b/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<DisplayList>, - /// The position of the layer in pixels. - pub position: Rect<uint>, /// 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<BufferRequest>, pub scale: f32, @@ -62,7 +68,7 @@ pub struct RenderRequest { } pub enum Msg { - RenderInitMsg(SmallVec1<RenderLayer>), + RenderInitMsg(Arc<StackingContext>), RenderMsg(Vec<RenderRequest>), UnusedBufferMsg(Vec<Box<LayerBuffer>>), PaintPermissionGranted, @@ -102,8 +108,8 @@ pub struct RenderTask<C> { /// The native graphics context. native_graphics_context: Option<NativePaintingGraphicsContext>, - /// The layers to be rendered. - render_layers: SmallVec1<RenderLayer>, + /// The root stacking context sent to us by the layout thread. + root_stacking_context: Option<Arc<StackingContext>>, /// Permission to send paint messages to the compositor paint_permission: bool, @@ -129,17 +135,36 @@ macro_rules! native_graphics_context( fn initialize_layers<C>(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<LayerMetadata>, + stacking_context: &StackingContext, + page_position: &Point2D<Au>) { + 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<C> RenderTask<C> where C: RenderListener + Send { @@ -170,11 +195,8 @@ impl<C> RenderTask<C> 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<C> RenderTask<C> 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<C> RenderTask<C> 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<C> RenderTask<C> 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<C> RenderTask<C> 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<C> RenderTask<C> 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<Box<LayerBuffer>>, - render_layer: RenderLayer, + stacking_context: Arc<StackingContext>, 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<LayerBuffer> { @@ -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<StackingContext>, 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<AzFloat> = 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<Box<LayerBuffer>>, RenderLayer, f32), + PaintTileMsgToWorkerThread(BufferRequest, Option<Box<LayerBuffer>>, Arc<StackingContext>, f32), } enum MsgFromWorkerThread { diff --git a/components/layout/block.rs b/components/layout/block.rs index 9b004bd798c..dbfde997841 100644 --- a/components/layout/block.rs +++ b/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/components/layout/display_list_builder.rs b/components/layout/display_list_builder.rs index 2948d678050..069493058b0 100644 --- a/components/layout/display_list_builder.rs +++ b/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<StackingContext>), + DisplayListResult(Box<DisplayList>), +} + +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<Au>, @@ -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<Au>, @@ -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<Au>, clip_rect: &Rect<Au>, @@ -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<Au>, level: StackingLevel, clip_rect: &Rect<Au>); @@ -102,11 +121,11 @@ pub trait FragmentDisplayListBuilding { flow_origin: Point2D<Au>, clip_rect: &Rect<Au>); - /// 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<Au>, @@ -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<Au>, @@ -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<Au>, clip_rect: &Rect<Au>, @@ -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<Au>, level: StackingLevel, clip_rect: &Rect<Au>) { @@ -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<Au>, background_and_border_level: BackgroundAndBorderLevel, clip_rect: &Rect<Au>) { + // 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<Au>| { 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<RGBA>, rect: || -> LogicalRect<Au>| { 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/components/layout/flow.rs b/components/layout/flow.rs index e22ea27c36e..41cc5570a60 100644 --- a/components/layout/flow.rs +++ b/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<Au>, - /// The position of the absolute containing block. - pub absolute_containing_block_position: Point2D<Au>, + /// 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<Au>, /// 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<Au>, + /// 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<Au>, /// 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<Au>, - /// 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<RenderLayer>, + /// 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<E, S: Encoder<E>> Encodable<S, E> 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<Au> { + /// 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<Au> { 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/components/layout/fragment.rs b/components/layout/fragment.rs index d9fd9db4839..def9cd33776 100644 --- a/components/layout/fragment.rs +++ b/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<Au>) -> Rect<Au> { + /// 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<Au>) + -> Rect<Au> { // 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/components/layout/inline.rs b/components/layout/inline.rs index c6ca65fd799..d1a6b22b992 100644 --- a/components/layout/inline.rs +++ b/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/components/layout/layout_task.rs b/components/layout/layout_task.rs index 15d9daccbc8..8fe2afb35ab 100644 --- a/components/layout/layout_task.rs +++ b/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<Au>, - /// A cached display list. - pub display_list: Option<Arc<DisplayList>>, + /// The root stacking context. + pub stacking_context: Option<Arc<StackingContext>>, pub stylist: Box<Stylist>, @@ -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, - }; - - 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) - } + 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.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<f32>) -> Result<HitTestResponse, ()> { - fn hit_test<'a,I>(point: Point2D<Au>, mut iterator: I) - -> Option<HitTestResponse> - 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<f32>) -> Result<MouseOverResponse, ()> { - fn mouse_over_test<'a,I>(point: Point2D<Au>, - mut iterator: I, - result: &mut Vec<UntrustedNodeAddress>) - 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<UntrustedNodeAddress> = 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/components/util/dlist.rs b/components/util/dlist.rs index 52836786de3..f22c07268ba 100644 --- a/components/util/dlist.rs +++ b/components/util/dlist.rs @@ -96,3 +96,30 @@ pub fn append_from<T>(this: &mut DList<T>, other: &mut DList<T>) { other.length = 0; } } + +/// Prepends the items in the other list to this one, leaving the other list empty. +#[inline] +pub fn prepend_from<T>(this: &mut DList<T>, other: &mut DList<T>) { + unsafe { + let this = mem::transmute::<&mut DList<T>,&mut RawDList<T>>(this); + let other = mem::transmute::<&mut DList<T>,&mut RawDList<T>>(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/components/util/geometry.rs b/components/util/geometry.rs index 8dd6d633da7..06b74c7c3a0 100644 --- a/components/util/geometry.rs +++ b/components/util/geometry.rs @@ -74,6 +74,11 @@ impl Default for Au { } } +pub static ZERO_POINT: Point2D<Au> = Point2D { + x: Au(0), + y: Au(0), +}; + pub static ZERO_RECT: Rect<Au> = Rect { origin: Point2D { x: Au(0), |