From 36406cdba8991ac3e88c85e596cec5c55eeec1a3 Mon Sep 17 00:00:00 2001 From: Martin Robinson Date: Wed, 2 Mar 2016 05:52:08 +0501 Subject: [PATCH] servo: Merge #9756 - Flatten display list structure (from mrobinson:flat-display-lists-webrender); r=pcwalton Instead of producing a tree of stacking contexts, display list generation now produces a flat list of display items and a tree of stacking contexts. This will eventually allow display list construction to produce and modify WebRender vertex buffers directly, removing the overhead of display list conversion. This change also moves layerization of the display list to the paint thread, since it isn't currently useful for WebRender. To accomplish this, display list generation now takes three passes of the flow tree: 1. Calculation of absolute positions. 2. Collection of a tree of stacking contexts. 3. Creation of a list of display items. After collection of display items, they are sorted based upon the index of their parent stacking contexts and their position in CSS 2.1 Appendeix E stacking order. This is a big change, but it actually simplifies display list generation. Source-Repo: https://github.com/servo/servo Source-Revision: 62814f7cb486bc267a796b7ce58c51d59240fad0 --- servo/components/gfx/display_list/mod.rs | 1370 +++++++---------- .../components/gfx/display_list/optimizer.rs | 97 -- servo/components/gfx/paint_thread.rs | 584 +++---- servo/components/gfx_traits/lib.rs | 7 +- servo/components/layout/block.rs | 42 +- .../components/layout/display_list_builder.rs | 815 +++++----- servo/components/layout/flex.rs | 15 +- servo/components/layout/flow.rs | 68 +- servo/components/layout/fragment.rs | 32 +- servo/components/layout/inline.rs | 14 +- servo/components/layout/layout_thread.rs | 86 +- servo/components/layout/list_item.rs | 15 +- servo/components/layout/multicol.rs | 24 +- servo/components/layout/query.rs | 49 +- servo/components/layout/sequential.rs | 27 +- servo/components/layout/table.rs | 17 +- servo/components/layout/table_caption.rs | 13 +- servo/components/layout/table_cell.rs | 17 +- servo/components/layout/table_colgroup.rs | 11 +- servo/components/layout/table_row.rs | 17 +- servo/components/layout/table_rowgroup.rs | 13 +- servo/components/layout/table_wrapper.rs | 13 +- servo/components/layout/traversal.rs | 6 +- servo/components/layout/webrender_helpers.rs | 209 +-- servo/components/profile/time.rs | 2 + servo/components/profile_traits/time.rs | 1 + servo/components/util/print_tree.rs | 6 + 27 files changed, 1709 insertions(+), 1861 deletions(-) delete mode 100644 servo/components/gfx/display_list/optimizer.rs diff --git a/servo/components/gfx/display_list/mod.rs b/servo/components/gfx/display_list/mod.rs index 763adb9d71ad..aa3c974a1d94 100644 --- a/servo/components/gfx/display_list/mod.rs +++ b/servo/components/gfx/display_list/mod.rs @@ -16,24 +16,20 @@ use app_units::Au; use azure::azure::AzFloat; -use azure::azure_hl::{Color, DrawTarget}; -use display_list::optimizer::DisplayListOptimizer; +use azure::azure_hl::Color; use euclid::approxeq::ApproxEq; use euclid::num::Zero; +use euclid::rect::TypedRect; use euclid::{Matrix2D, Matrix4, Point2D, Rect, SideOffsets2D, Size2D}; -use gfx_traits::{color, LayerId, LayerKind, ScrollPolicy}; +use gfx_traits::{LayerId, ScrollPolicy}; use heapsize::HeapSizeOf; use msg::constellation_msg::PipelineId; use net_traits::image::base::Image; use paint_context::PaintContext; -use paint_thread::{PaintLayerContents, PaintLayer}; use range::Range; -use self::DisplayItem::*; -use smallvec::SmallVec; use std::cmp::Ordering; -use std::collections::linked_list::{self, LinkedList}; +use std::collections::HashMap; use std::fmt; -use std::mem; use std::sync::Arc; use style::computed_values::{border_style, cursor, filter, image_rendering, mix_blend_mode}; use style::computed_values::{pointer_events}; @@ -41,9 +37,7 @@ use style::properties::ComputedValues; use style_traits::cursor::Cursor; use text::TextRun; use text::glyph::CharIndex; -use util::geometry::MAX_RECT; -use util::linked_list::prepend_from; -use util::opts; +use util::geometry::{self, MAX_RECT, ScreenPx}; use util::print_tree::PrintTree; use webrender_traits::WebGLContextId; @@ -53,8 +47,6 @@ pub use style::dom::OpaqueNode; // layout to use. pub use azure::azure_hl::GradientStop; -pub mod optimizer; - /// The factor that we multiply the blur radius by in order to inflate the boundaries of display /// items that involve a blur. This ensures that the display item boundaries include all the ink. pub static BLUR_INFLATION_FACTOR: i32 = 3; @@ -62,7 +54,7 @@ pub static BLUR_INFLATION_FACTOR: i32 = 3; /// LayerInfo is used to store PaintLayer metadata during DisplayList construction. /// It is also used for tracking LayerIds when creating layers to preserve ordering when /// layered DisplayItems should render underneath unlayered DisplayItems. -#[derive(Clone, Copy, Debug, HeapSizeOf, Deserialize, Serialize)] +#[derive(Clone, Copy, HeapSizeOf, Deserialize, Serialize)] pub struct LayerInfo { /// The base LayerId of this layer. pub layer_id: LayerId, @@ -76,508 +68,414 @@ pub struct LayerInfo { /// The id for the next layer in the sequence. This is used for synthesizing /// layers for content that needs to be displayed on top of this layer. pub next_layer_id: LayerId, + + /// The color of the background in this layer. Used for unpainted content. + pub background_color: Color, } impl LayerInfo { pub fn new(id: LayerId, scroll_policy: ScrollPolicy, - subpage_pipeline_id: Option) + subpage_pipeline_id: Option, + background_color: Color) -> LayerInfo { LayerInfo { layer_id: id, scroll_policy: scroll_policy, subpage_pipeline_id: subpage_pipeline_id, next_layer_id: id.companion_layer_id(), + background_color: background_color, } } - - fn next(&mut self) -> LayerInfo { - let new_layer_info = LayerInfo::new(self.next_layer_id, self.scroll_policy, None); - self.next_layer_id = self.next_layer_id.companion_layer_id(); - new_layer_info - } - - fn next_with_scroll_policy(&mut self, scroll_policy: ScrollPolicy) -> LayerInfo { - let mut new_layer_info = self.next(); - new_layer_info.scroll_policy = scroll_policy; - new_layer_info - } } -/// 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. +#[derive(Clone, HeapSizeOf, Deserialize, Serialize)] +pub struct DisplayListEntry { + pub stacking_context_id: StackingContextId, + pub section: DisplayListSection, + pub item: DisplayItem, +} + +pub struct DisplayListTraversal<'a> { + pub display_list: &'a DisplayList, + pub current_item_index: usize, + pub last_item_index: usize, +} + +impl<'a> DisplayListTraversal<'a> { + fn can_draw_item_at_index(&self, index: usize) -> bool { + index <= self.last_item_index && index < self.display_list.list.len() + } + + pub fn advance(&mut self, context: &StackingContext) -> Option<&'a DisplayListEntry> { + if !self.can_draw_item_at_index(self.current_item_index) { + return None + } + if self.display_list.list[self.current_item_index].stacking_context_id != context.id { + return None + } + + self.current_item_index += 1; + Some(&self.display_list.list[self.current_item_index - 1]) + } + + fn current_item_offset(&self) -> u32 { + self.display_list.get_offset_for_item(&self.display_list.list[self.current_item_index]) + } + + pub fn skip_past_stacking_context(&mut self, stacking_context: &StackingContext) { + let next_stacking_context_offset = + self.display_list.offsets[&stacking_context.id].outlines + 1; + while self.can_draw_item_at_index(self.current_item_index + 1) && + self.current_item_offset() < next_stacking_context_offset { + self.current_item_index += 1; + } + } +} + +#[derive(HeapSizeOf, Deserialize, Serialize)] +pub struct StackingContextOffsets { + pub start: u32, + pub block_backgrounds_and_borders: u32, + pub content: u32, + pub outlines: u32, +} + #[derive(HeapSizeOf, Deserialize, Serialize)] pub struct DisplayList { - /// The border and backgrounds for the root of this stacking context: steps 1 and 2. - pub background_and_borders: LinkedList, - /// Borders and backgrounds for block-level descendants: step 4. - pub block_backgrounds_and_borders: LinkedList, - /// Floats: step 5. These are treated as pseudo-stacking contexts. - pub floats: LinkedList, - /// All non-positioned content. - pub content: LinkedList, - /// All positioned content that does not get a stacking context. - pub positioned_content: LinkedList, - /// Outlines: step 10. - pub outlines: LinkedList, - /// Child PaintLayers that will be rendered on top of everything else. - pub layered_children: LinkedList>, - /// Information about child layers. - pub layer_info: LinkedList, + pub list: Vec, + pub offsets: HashMap, + pub root_stacking_context: StackingContext, } impl DisplayList { - /// Creates a new, empty display list. - #[inline] - pub fn new() -> DisplayList { - DisplayList { - background_and_borders: LinkedList::new(), - block_backgrounds_and_borders: LinkedList::new(), - floats: LinkedList::new(), - content: LinkedList::new(), - positioned_content: LinkedList::new(), - outlines: LinkedList::new(), - layered_children: LinkedList::new(), - layer_info: LinkedList::new(), - } - } + pub fn new(mut root_stacking_context: StackingContext, + items: &mut Option>) + -> DisplayList { + let items = match items.take() { + Some(items) => items, + None => panic!("Tried to create empty display list."), + }; - /// Adds the given display item at the specified section of this display list. - pub fn add_to_section(&mut self, display_item: DisplayItem, section: DisplayListSection) { - self.get_section_mut(section).push_back(display_item); - } + let mut offsets = HashMap::new(); + DisplayList::sort_and_count_stacking_contexts(&mut root_stacking_context, &mut offsets, 0); - /// Creates a new display list which contains a single stacking context. - #[inline] - pub fn new_with_stacking_context(stacking_context: Arc) -> Box { - let mut display_list = box DisplayList::new(); - display_list.positioned_content.push_back( - DisplayItem::StackingContextClass(stacking_context)); + let mut display_list = DisplayList { + list: items, + offsets: offsets, + root_stacking_context: root_stacking_context, + }; + display_list.sort(); display_list } - /// 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 Option>) { - if let Some(mut other) = other.take() { - self.background_and_borders.append(&mut other.background_and_borders); - self.block_backgrounds_and_borders.append(&mut other.block_backgrounds_and_borders); - self.floats.append(&mut other.floats); - self.content.append(&mut other.content); - self.positioned_content.append(&mut other.positioned_content); - self.outlines.append(&mut other.outlines); - self.layered_children.append(&mut other.layered_children); - self.layer_info.append(&mut other.layer_info); + pub fn get_offset_for_item(&self, item: &DisplayListEntry) -> u32 { + let offsets = &self.offsets[&item.stacking_context_id]; + match item.section { + DisplayListSection::BackgroundAndBorders => offsets.start, + DisplayListSection::BlockBackgroundsAndBorders => + offsets.block_backgrounds_and_borders, + DisplayListSection::Content => offsets.content, + DisplayListSection::Outlines => offsets.outlines, } } - /// Merges all display items from all non-float stacking levels to the `float` stacking level. - /// From E.2.5 at http://www.w3.org/TR/CSS21/zindex.html. We do not include positioned content - /// and stacking contexts in the pseudo-stacking-context. - #[inline] - pub fn form_float_pseudo_stacking_context(&mut self) { - prepend_from(&mut self.floats, &mut self.outlines); - prepend_from(&mut self.floats, &mut self.content); - prepend_from(&mut self.floats, &mut self.block_backgrounds_and_borders); - prepend_from(&mut self.floats, &mut self.background_and_borders); - } + fn sort(&mut self) { + let mut list = Vec::new(); + list.append(&mut self.list); - /// Merges all display items from all non-positioned-content stacking levels to the - /// positioned-content stacking level. - #[inline] - pub fn form_pseudo_stacking_context_for_positioned_content(&mut self) { - prepend_from(&mut self.positioned_content, &mut self.outlines); - prepend_from(&mut self.positioned_content, &mut self.content); - prepend_from(&mut self.positioned_content, &mut self.floats); - prepend_from(&mut self.positioned_content, &mut self.block_backgrounds_and_borders); - prepend_from(&mut self.positioned_content, &mut self.background_and_borders); - } - - /// 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 flatten(&self) -> Vec { - let mut result = Vec::new(); - fn flatten_item(result: &mut Vec, item: &DisplayItem) { - match item { - &DisplayItem::StackingContextClass(ref stacking_context) => - result.extend(stacking_context.display_list.flatten().into_iter()), - _ => result.push((*item).clone()), + list.sort_by(|a, b| { + if a.stacking_context_id == b.stacking_context_id { + return a.section.cmp(&b.section); } - } + self.get_offset_for_item(a).cmp(&self.get_offset_for_item(b)) + }); - for display_item in &self.background_and_borders { - flatten_item(&mut result, display_item); - } - for display_item in &self.block_backgrounds_and_borders { - flatten_item(&mut result, display_item); - } - for display_item in &self.floats { - flatten_item(&mut result, display_item); - } - for display_item in &self.content { - flatten_item(&mut result, display_item); - } - for display_item in &self.positioned_content { - flatten_item(&mut result, display_item); - } - for display_item in &self.outlines { - flatten_item(&mut result, display_item); - } - result + self.list.append(&mut list); } - pub fn print(&self, title: String) { - let mut print_tree = PrintTree::new(title); + pub fn print(&self) { + let mut print_tree = PrintTree::new("Display List".to_owned()); self.print_with_tree(&mut print_tree); } + fn sort_and_count_stacking_contexts( + stacking_context: &mut StackingContext, + offsets: &mut HashMap, + mut current_offset: u32) + -> u32 { + stacking_context.children.sort(); + + let start_offset = current_offset; + let mut block_backgrounds_and_borders_offset = None; + let mut content_offset = None; + + for child in stacking_context.children.iter_mut() { + if child.z_index >= 0 { + if block_backgrounds_and_borders_offset.is_none() { + current_offset += 1; + block_backgrounds_and_borders_offset = Some(current_offset); + } + + if child.context_type != StackingContextType::PseudoFloat && + content_offset.is_none() { + current_offset += 1; + content_offset = Some(current_offset); + } + } + + current_offset += 1; + current_offset = + DisplayList::sort_and_count_stacking_contexts(child, offsets, current_offset); + } + + let block_backgrounds_and_borders_offset = + block_backgrounds_and_borders_offset.unwrap_or_else(|| { + current_offset += 1; + current_offset + }); + + let content_offset = content_offset.unwrap_or_else(|| { + current_offset += 1; + current_offset + }); + + current_offset += 1; + + offsets.insert( + stacking_context.id, + StackingContextOffsets { + start: start_offset, + block_backgrounds_and_borders: block_backgrounds_and_borders_offset, + content: content_offset, + outlines: current_offset, + }); + + current_offset + 1 + } + pub fn print_with_tree(&self, print_tree: &mut PrintTree) { - fn print_display_list_section(print_tree: &mut PrintTree, - items: &LinkedList, - title: &str) { - if items.is_empty() { - return; - } - - print_tree.new_level(title.to_owned()); - for item in items { - match item { - &DisplayItem::StackingContextClass(ref stacking_context) => - stacking_context.print_with_tree(print_tree), - _ => print_tree.add_item(format!("{:?}", item)), - } - } - print_tree.end_level(); + print_tree.new_level("Items".to_owned()); + for item in &self.list { + print_tree.add_item(format!("{:?} StackingContext: {:?}", + item.item, + item.stacking_context_id)); } + print_tree.end_level(); - print_display_list_section(print_tree, - &self.background_and_borders, - "Backgrounds and Borders"); - print_display_list_section(print_tree, - &self.block_backgrounds_and_borders, - "Block Backgrounds and Borders"); - print_display_list_section(print_tree, &self.floats, "Floats"); - print_display_list_section(print_tree, &self.content, "Content"); - print_display_list_section(print_tree, &self.positioned_content, "Positioned Content"); - print_display_list_section(print_tree, &self.outlines, "Outlines"); - - if !self.layered_children.is_empty() { - print_tree.new_level("Layers".to_owned()); - for paint_layer in &self.layered_children { - match paint_layer.contents { - PaintLayerContents::StackingContext(ref stacking_context) => - stacking_context.print_with_tree(print_tree), - PaintLayerContents::DisplayList(ref display_list) => { - print_tree.new_level(format!("DisplayList Layer with bounds {:?}:", - display_list.calculate_bounding_rect())); - display_list.print_with_tree(print_tree); - print_tree.end_level(); - } - } - } - print_tree.end_level(); - } + print_tree.new_level("Stacking Contexts".to_owned()); + self.root_stacking_context.print_with_tree(print_tree); + print_tree.end_level(); } - /// Draws the DisplayList in stacking context order according to the steps in CSS 2.1 § E.2. - pub fn draw_into_context(&self, - draw_target: &DrawTarget, - paint_context: &mut PaintContext, - transform: &Matrix4, - clip_rect: Option<&Rect>) { - let mut paint_subcontext = PaintContext { - draw_target: draw_target.clone(), - font_context: &mut *paint_context.font_context, - page_rect: paint_context.page_rect, - screen_rect: paint_context.screen_rect, - clip_rect: clip_rect.map(|clip_rect| *clip_rect), - transient_clip: None, - layer_kind: paint_context.layer_kind, + /// Draws a single DisplayItem into the given PaintContext. + pub fn draw_item_at_index_into_context(&self, + paint_context: &mut PaintContext, + transform: &Matrix4, + index: usize) { + let old_transform = paint_context.draw_target.get_transform(); + paint_context.draw_target.set_transform( + &Matrix2D::new(transform.m11, transform.m12, + transform.m21, transform.m22, + transform.m41, transform.m42)); + + let entry = &self.list[index]; + entry.item.draw_into_context(paint_context); + + paint_context.draw_target.set_transform(&old_transform); + } + + pub fn find_stacking_context<'a>(&'a self, + stacking_context_id: StackingContextId) + -> Option<&'a StackingContext> { + fn find_stacking_context_in_stacking_context<'a>(stacking_context: &'a StackingContext, + stacking_context_id: StackingContextId) + -> Option<&'a StackingContext> { + if stacking_context.id == stacking_context_id { + return Some(stacking_context); + } + + for kid in stacking_context.children.iter() { + let result = find_stacking_context_in_stacking_context(kid, stacking_context_id); + if result.is_some() { + return result; + } + } + + None + } + find_stacking_context_in_stacking_context(&self.root_stacking_context, + stacking_context_id) + } + + /// Draws the DisplayList in order. + pub fn draw_into_context<'a>(&self, + paint_context: &mut PaintContext, + transform: &Matrix4, + stacking_context_id: StackingContextId, + start: usize, + end: usize) { + let stacking_context = self.find_stacking_context(stacking_context_id).unwrap(); + let mut traversal = DisplayListTraversal { + display_list: self, + current_item_index: start, + last_item_index: end, }; - - if opts::get().dump_display_list_optimized { - self.print(format!("Optimized display list. Tile bounds: {:?}", - paint_context.page_rect)); - } - - // Set up our clip rect and transform. - let old_transform = paint_subcontext.draw_target.get_transform(); - let xform_2d = Matrix2D::new(transform.m11, transform.m12, - transform.m21, transform.m22, - transform.m41, transform.m42); - paint_subcontext.draw_target.set_transform(&xform_2d); - paint_subcontext.push_clip_if_applicable(); - - // Steps 1 and 2: Borders and background for the root. - for display_item in &self.background_and_borders { - display_item.draw_into_context(transform, &mut paint_subcontext) - } - - // Step 3: Positioned descendants with negative z-indices. - for positioned_kid in &self.positioned_content { - if let &DisplayItem::StackingContextClass(ref stacking_context) = positioned_kid { - if stacking_context.z_index < 0 { - positioned_kid.draw_into_context(transform, &mut paint_subcontext); - } - } - } - - // Step 4: Block backgrounds and borders. - for display_item in &self.block_backgrounds_and_borders { - display_item.draw_into_context(transform, &mut paint_subcontext) - } - - // Step 5: Floats. - for display_item in &self.floats { - display_item.draw_into_context(transform, &mut paint_subcontext) - } - - // TODO(pcwalton): Step 6: Inlines that generate stacking contexts. - - // Step 7: Content. - for display_item in &self.content { - display_item.draw_into_context(transform, &mut paint_subcontext) - } - - // Step 8 & 9: Positioned descendants with nonnegative, numeric z-indices. - for positioned_kid in &self.positioned_content { - if let &DisplayItem::StackingContextClass(ref stacking_context) = positioned_kid { - if stacking_context.z_index < 0 { - continue; - } - } - positioned_kid.draw_into_context(transform, &mut paint_subcontext); - } - - // Step 10: Outlines. - for display_item in &self.outlines { - display_item.draw_into_context(transform, &mut paint_subcontext) - } - - // Undo our clipping and transform. - paint_subcontext.remove_transient_clip_if_applicable(); - paint_subcontext.pop_clip_if_applicable(); - paint_subcontext.draw_target.set_transform(&old_transform) + self.draw_stacking_context(stacking_context, &mut traversal, paint_context, transform); } - pub fn hit_test(&self, - point: Point2D, - result: &mut Vec, - topmost_only: bool) { - fn hit_test_item(point: Point2D, - result: &mut Vec, - item: &DisplayItem) { - let base_item = match item.base() { - Some(base) => base, - None => return, - }; - - // TODO(pcwalton): Use a precise algorithm here. This will allow us to properly hit - // test elements with `border-radius`, for example. - if !base_item.clip.might_intersect_point(&point) { - // Clipped out. - return; - } - if !item.bounds().contains(&point) { - // Can't possibly hit. - return; - } - if base_item.metadata.pointing.is_none() { - // `pointer-events` is `none`. Ignore this item. - return; - } - - match *item { - DisplayItem::BorderClass(ref border) => { - // If the point is inside the border, it didn't hit the border! - let interior_rect = - Rect::new( - Point2D::new(border.base.bounds.origin.x + - border.border_widths.left, - border.base.bounds.origin.y + - border.border_widths.top), - Size2D::new(border.base.bounds.size.width - - (border.border_widths.left + - border.border_widths.right), - border.base.bounds.size.height - - (border.border_widths.top + - border.border_widths.bottom))); - if interior_rect.contains(&point) { - return; - } - } - DisplayItem::BoxShadowClass(_) => { - // Box shadows can never be hit. - return - } - _ => {} - } - - // We found a hit! - result.push(base_item.metadata); - } - - fn hit_test_in_list<'a, I>(point: Point2D, - result: &mut Vec, - topmost_only: bool, - iterator: I) - where I: Iterator { - for item in iterator { - hit_test_item(point, result, item); - if topmost_only && !result.is_empty() { - return; + fn draw_stacking_context_contents<'a>(&'a self, + stacking_context: &StackingContext, + traversal: &mut DisplayListTraversal<'a>, + paint_context: &mut PaintContext, + transform: &Matrix4, + tile_rect: Option>) { + for child in stacking_context.children.iter() { + while let Some(item) = traversal.advance(stacking_context) { + if item.item.intersects_rect_in_parent_context(tile_rect) { + item.item.draw_into_context(paint_context); } } - } - // Layers that are positioned on top of this layer should get a shot at the hit test first. - for layer in self.layered_children.iter().rev() { - match layer.contents { - PaintLayerContents::StackingContext(ref stacking_context) => - stacking_context.hit_test(point, result, topmost_only), - PaintLayerContents::DisplayList(ref display_list) => - display_list.hit_test(point, result, topmost_only), - } - - if topmost_only && !result.is_empty() { - return - } - } - - // Iterate through display items in reverse stacking order. Steps here refer to the - // painting steps in CSS 2.1 Appendix E. - // - // Step 10: Outlines. - hit_test_in_list(point, result, topmost_only, self.outlines.iter().rev()); - if topmost_only && !result.is_empty() { - return - } - - // Steps 9 and 8: Positioned descendants with nonnegative z-indices. - for kid in self.positioned_content.iter().rev() { - if let &DisplayItem::StackingContextClass(ref stacking_context) = kid { - if stacking_context.z_index < 0 { - continue - } - stacking_context.hit_test(point, result, topmost_only); + if child.intersects_rect_in_parent_context(tile_rect) { + self.draw_stacking_context(child, traversal, paint_context, &transform); } else { - hit_test_item(point, result, kid); - } - - if topmost_only && !result.is_empty() { - return + traversal.skip_past_stacking_context(child); } } - // Steps 8, 7, 5, and 4: Positioned content, content, floats, and block backgrounds and - // borders. - // - // TODO(pcwalton): Step 6: Inlines that generate stacking contexts. - for display_list in &[ - &self.content, - &self.floats, - &self.block_backgrounds_and_borders, - ] { - hit_test_in_list(point, result, topmost_only, display_list.iter().rev()); - if topmost_only && !result.is_empty() { - return + while let Some(item) = traversal.advance(stacking_context) { + if item.item.intersects_rect_in_parent_context(tile_rect) { + item.item.draw_into_context(paint_context); } } - - for kid in self.positioned_content.iter().rev() { - if let &DisplayItem::StackingContextClass(ref stacking_context) = kid { - if stacking_context.z_index >= 0 { - continue - } - stacking_context.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.background_and_borders.iter().rev()) - } - /// Returns the PaintLayer in the given DisplayList with a specific layer ID. - pub fn find_layer_with_layer_id(&self, layer_id: LayerId) -> Option> { - for kid in &self.layered_children { - if let Some(paint_layer) = PaintLayer::find_layer_with_layer_id(&kid, layer_id) { - return Some(paint_layer); - } + + fn draw_stacking_context<'a>(&'a self, + stacking_context: &StackingContext, + traversal: &mut DisplayListTraversal<'a>, + paint_context: &mut PaintContext, + transform: &Matrix4) { + + if stacking_context.context_type != StackingContextType::Real { + self.draw_stacking_context_contents(stacking_context, + traversal, + paint_context, + transform, + None); + return; } - for item in &self.positioned_content { - if let &DisplayItem::StackingContextClass(ref stacking_context) = item { - if let Some(paint_layer) - = stacking_context.display_list.find_layer_with_layer_id(layer_id) { - return Some(paint_layer); - } - } - } + let draw_target = paint_context.get_or_create_temporary_draw_target( + &stacking_context.filters, + stacking_context.blend_mode); - None - } - - /// Calculate the union of all the bounds of all of the items in this display list. - /// This is an expensive operation, so it shouldn't be done unless absolutely necessary - /// and, if possible, the result should be cached. - pub fn calculate_bounding_rect(&self) -> Rect { - fn union_all_items(list: &LinkedList, mut bounds: Rect) -> Rect { - for item in list { - bounds = bounds.union(&item.bounds()); + // If a layer is being used, the transform for this layer + // will be handled by the compositor. + let old_transform = paint_context.draw_target.get_transform(); + let transform = match stacking_context.layer_info { + Some(..) => *transform, + None => { + let pixels_per_px = paint_context.screen_pixels_per_px(); + let origin = &stacking_context.bounds.origin; + transform.translate( + origin.x.to_nearest_pixel(pixels_per_px.get()) as AzFloat, + origin.y.to_nearest_pixel(pixels_per_px.get()) as AzFloat, + 0.0).mul(&stacking_context.transform) } - bounds }; - let mut bounds = Rect::zero(); - bounds = union_all_items(&self.background_and_borders, bounds); - bounds = union_all_items(&self.block_backgrounds_and_borders, bounds); - bounds = union_all_items(&self.floats, bounds); - bounds = union_all_items(&self.content, bounds); - bounds = union_all_items(&self.positioned_content, bounds); - bounds = union_all_items(&self.outlines, bounds); - bounds + { + let mut paint_subcontext = PaintContext { + draw_target: draw_target.clone(), + font_context: &mut *paint_context.font_context, + page_rect: paint_context.page_rect, + screen_rect: paint_context.screen_rect, + clip_rect: Some(stacking_context.overflow), + transient_clip: None, + layer_kind: paint_context.layer_kind, + }; + + // Set up our clip rect and transform. + paint_subcontext.draw_target.set_transform( + &Matrix2D::new(transform.m11, transform.m12, + transform.m21, transform.m22, + transform.m41, transform.m42)); + paint_subcontext.push_clip_if_applicable(); + + self.draw_stacking_context_contents( + stacking_context, + traversal, + &mut paint_subcontext, + &transform, + Some(transformed_tile_rect(paint_context.screen_rect, &transform))); + + paint_subcontext.remove_transient_clip_if_applicable(); + paint_subcontext.pop_clip_if_applicable(); + } + + draw_target.set_transform(&old_transform); + paint_context.draw_temporary_draw_target_if_necessary( + &draw_target, &stacking_context.filters, stacking_context.blend_mode); } - #[inline] - fn get_section_mut(&mut self, section: DisplayListSection) -> &mut LinkedList { - match section { - DisplayListSection::BackgroundAndBorders => &mut self.background_and_borders, - DisplayListSection::BlockBackgroundsAndBorders => - &mut self.block_backgrounds_and_borders, - DisplayListSection::Floats => &mut self.floats, - DisplayListSection::Content => &mut self.content, - DisplayListSection::PositionedContent => &mut self.positioned_content, - DisplayListSection::Outlines => &mut self.outlines, - } + /// Places all nodes containing the point of interest into `result`, topmost first. Respects + /// the `pointer-events` CSS property 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) -> Vec { + let mut traversal = DisplayListTraversal { + display_list: self, + current_item_index: 0, + last_item_index: self.list.len() - 1, + }; + let mut result = Vec::new(); + self.root_stacking_context.hit_test(&mut traversal, point, &mut result); + result.reverse(); + result } } -#[derive(Clone, Copy, Debug)] +fn transformed_tile_rect(tile_rect: TypedRect, transform: &Matrix4) -> Rect { + // Invert the current transform, then use this to back transform + // the tile rect (placed at the origin) into the space of this + // stacking context. + let inverse_transform = transform.invert(); + let inverse_transform_2d = Matrix2D::new(inverse_transform.m11, inverse_transform.m12, + inverse_transform.m21, inverse_transform.m22, + inverse_transform.m41, inverse_transform.m42); + let tile_size = Size2D::new(tile_rect.as_f32().size.width, tile_rect.as_f32().size.height); + let tile_rect = Rect::new(Point2D::zero(), tile_size).to_untyped(); + geometry::f32_rect_to_au_rect(inverse_transform_2d.transform_rect(&tile_rect)) +} + + +/// Display list sections that make up a stacking context. Each section here refers +/// to the steps in CSS 2.1 Appendix E. +/// +#[derive(Clone, Copy, Debug, Deserialize, Eq, HeapSizeOf, Ord, PartialEq, PartialOrd, RustcEncodable, Serialize)] pub enum DisplayListSection { BackgroundAndBorders, BlockBackgroundsAndBorders, - Floats, Content, - PositionedContent, Outlines, } +#[derive(Clone, Copy, Debug, Deserialize, Eq, HeapSizeOf, Ord, PartialEq, PartialOrd, RustcEncodable, Serialize)] +pub enum StackingContextType { + Real, + PseudoPositioned, + PseudoFloat, +} + #[derive(HeapSizeOf, Deserialize, Serialize)] /// 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 ID of this StackingContext for uniquely identifying it. + pub id: StackingContextId, + + /// The type of this StackingContext. Used for collecting and sorting. + pub context_type: StackingContextType, /// The position and size of this stacking context. pub bounds: Rect, @@ -609,14 +507,15 @@ pub struct StackingContext { /// The layer info for this stacking context, if there is any. pub layer_info: Option, - /// The LayerId of the last child layer of this stacking context. - pub last_child_layer_info: Option, + /// Children of this StackingContext. + pub children: Vec, } impl StackingContext { /// Creates a new stacking context. #[inline] - pub fn new(display_list: Box, + pub fn new(id: StackingContextId, + context_type: StackingContextType, bounds: &Rect, overflow: &Rect, z_index: i32, @@ -628,8 +527,9 @@ impl StackingContext { scrolls_overflow_area: bool, layer_info: Option) -> StackingContext { - let mut stacking_context = StackingContext { - display_list: display_list, + StackingContext { + id: id, + context_type: context_type, bounds: *bounds, overflow: *overflow, z_index: z_index, @@ -640,334 +540,121 @@ impl StackingContext { establishes_3d_context: establishes_3d_context, scrolls_overflow_area: scrolls_overflow_area, layer_info: layer_info, - last_child_layer_info: None, - }; - // webrender doesn't care about layers in the display list - it's handled internally. - if !opts::get().use_webrender { - StackingContextLayerCreator::add_layers_to_preserve_drawing_order(&mut stacking_context); - } - stacking_context - } - - /// Draws the stacking context in the proper order according to the steps in CSS 2.1 § E.2. - pub fn draw_into_context(&self, - display_list: &DisplayList, - paint_context: &mut PaintContext, - transform: &Matrix4, - clip_rect: Option<&Rect>) { - let temporary_draw_target = - paint_context.get_or_create_temporary_draw_target(&self.filters, self.blend_mode); - - display_list.draw_into_context(&temporary_draw_target, - paint_context, - transform, - clip_rect); - - paint_context.draw_temporary_draw_target_if_necessary(&temporary_draw_target, - &self.filters, - self.blend_mode) - - } - - /// Optionally optimize and then draws the stacking context. - pub fn optimize_and_draw_into_context(&self, - paint_context: &mut PaintContext, - transform: &Matrix4, - clip_rect: Option<&Rect>) { - - // If a layer is being used, the transform for this layer - // will be handled by the compositor. - let transform = match self.layer_info { - Some(..) => *transform, - None => transform.mul(&self.transform), - }; - - // TODO(gw): This is a hack to avoid running the DL optimizer - // on 3d transformed tiles. We should have a better solution - // than just disabling the opts here. - if paint_context.layer_kind == LayerKind::HasTransform || - opts::get().use_webrender { // webrender takes care of all culling via aabb tree! - self.draw_into_context(&self.display_list, - paint_context, - &transform, - clip_rect); - - } else { - // Invert the current transform, then use this to back transform - // the tile rect (placed at the origin) into the space of this - // stacking context. - let inverse_transform = transform.invert(); - let inverse_transform_2d = Matrix2D::new(inverse_transform.m11, inverse_transform.m12, - inverse_transform.m21, inverse_transform.m22, - inverse_transform.m41, inverse_transform.m42); - - let tile_size = Size2D::new(paint_context.screen_rect.as_f32().size.width, - paint_context.screen_rect.as_f32().size.height); - let tile_rect = Rect::new(Point2D::zero(), tile_size).to_untyped(); - let tile_rect = inverse_transform_2d.transform_rect(&tile_rect); - - // Optimize the display list to throw out out-of-bounds display items and so forth. - let display_list = DisplayListOptimizer::new(&tile_rect).optimize(&*self.display_list); - - self.draw_into_context(&display_list, - paint_context, - &transform, - clip_rect); + children: Vec::new(), } } - /// Places all nodes containing the point of interest into `result`, topmost first. Respects - /// the `pointer-events` CSS property 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) { + pub fn hit_test<'a>(&self, + traversal: &mut DisplayListTraversal<'a>, + point: Point2D, + result: &mut Vec) { // Convert the point into stacking context local space - let point = point - self.bounds.origin; - - debug_assert!(!topmost_only || result.is_empty()); - let inv_transform = self.transform.invert(); - let frac_point = inv_transform.transform_point(&Point2D::new(point.x.to_f32_px(), - point.y.to_f32_px())); - let point = Point2D::new(Au::from_f32_px(frac_point.x), Au::from_f32_px(frac_point.y)); - self.display_list.hit_test(point, result, topmost_only) - } - - pub fn print(&self, title: String) { - let mut print_tree = PrintTree::new(title); - self.print_with_tree(&mut print_tree); - } - - fn print_with_tree(&self, print_tree: &mut PrintTree) { - if self.layer_info.is_some() { - print_tree.new_level(format!("Layered StackingContext at {:?} with overflow {:?}:", - self.bounds, - self.overflow)); + let point = if self.context_type == StackingContextType::Real { + let point = point - self.bounds.origin; + let inv_transform = self.transform.invert(); + let frac_point = inv_transform.transform_point(&Point2D::new(point.x.to_f32_px(), + point.y.to_f32_px())); + Point2D::new(Au::from_f32_px(frac_point.x), Au::from_f32_px(frac_point.y)) } else { - print_tree.new_level(format!("StackingContext at {:?} with overflow {:?}:", - self.bounds, - self.overflow)); + point + }; + + for child in self.children.iter() { + while let Some(item) = traversal.advance(self) { + item.item.hit_test(point, result); + } + child.hit_test(traversal, point, result); + } + + while let Some(item) = traversal.advance(self) { + item.item.hit_test(point, result); + } + } + + pub fn print_with_tree(&self, print_tree: &mut PrintTree) { + print_tree.new_level(format!("{:?}", self)); + for kid in self.children.iter() { + kid.print_with_tree(print_tree); } - self.display_list.print_with_tree(print_tree); print_tree.end_level(); } - fn scroll_policy(&self) -> ScrollPolicy { - match self.layer_info { - Some(ref layer_info) => layer_info.scroll_policy, - None => ScrollPolicy::Scrollable, - } - } - - fn get_layer_info(&mut self, layer_id: LayerId) -> &mut LayerInfo { - for layer_info in self.display_list.layer_info.iter_mut() { - if layer_info.layer_id == layer_id { - return layer_info; - } + pub fn intersects_rect_in_parent_context(&self, rect: Option>) -> bool { + // We only do intersection checks for real stacking contexts, since + // pseudo stacking contexts might not have proper position information. + if self.context_type != StackingContextType::Real { + return true; } - panic!("Could not find LayerInfo with id: {:?}", layer_id); + let rect = match rect { + Some(ref rect) => rect, + None => return true, + }; + + // Transform this stacking context to get it into the same space as + // the parent stacking context. + let origin_x = self.bounds.origin.x.to_f32_px(); + let origin_y = self.bounds.origin.y.to_f32_px(); + + let transform = Matrix4::identity().translate(origin_x, + origin_y, + 0.0) + .mul(&self.transform); + let transform_2d = Matrix2D::new(transform.m11, transform.m12, + transform.m21, transform.m22, + transform.m41, transform.m42); + + let overflow = geometry::au_rect_to_f32_rect(self.overflow); + let overflow = transform_2d.transform_rect(&overflow); + let overflow = geometry::f32_rect_to_au_rect(overflow); + + rect.intersects(&overflow) } } -struct StackingContextLayerCreator { - display_list_for_next_layer: Option, - next_layer_info: Option, - building_ordering_layer: bool, - last_child_layer_info: Option, +impl Ord for StackingContext { + fn cmp(&self, other: &Self) -> Ordering { + if self.z_index != 0 || other.z_index != 0 { + return self.z_index.cmp(&other.z_index); + } + + match (self.context_type, other.context_type) { + (StackingContextType::PseudoFloat, StackingContextType::PseudoFloat) => Ordering::Equal, + (StackingContextType::PseudoFloat, _) => Ordering::Less, + (_, StackingContextType::PseudoFloat) => Ordering::Greater, + (_, _) => Ordering::Equal, + } + } } -impl StackingContextLayerCreator { - fn new() -> StackingContextLayerCreator { - // webrender doesn't care about layers in the display list - it's handled internally. - debug_assert!(!opts::get().use_webrender); - - StackingContextLayerCreator { - display_list_for_next_layer: None, - next_layer_info: None, - building_ordering_layer: false, - last_child_layer_info: None, - } +impl PartialOrd for StackingContext { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) } +} - #[inline] - fn add_layers_to_preserve_drawing_order(stacking_context: &mut StackingContext) { - let mut state = StackingContextLayerCreator::new(); - - // First we need to sort positioned content by z-index, so we can paint - // it in order and also so that we can detect situations where unlayered - // content should be on top of layered content. - let positioned_content = mem::replace(&mut stacking_context.display_list.positioned_content, - LinkedList::new()); - let mut sorted_positioned_content: SmallVec<[DisplayItem; 8]> = SmallVec::new(); - sorted_positioned_content.extend(positioned_content.into_iter()); - sorted_positioned_content.sort_by(|this, other| this.compare_zindex(other)); - - // It's important here that we process all elements in paint order, so we can detect - // situations where layers are needed to maintain paint order. - state.layerize_display_list_section(DisplayListSection::BackgroundAndBorders, - stacking_context); - - let mut remaining_positioned_content: SmallVec<[DisplayItem; 8]> = SmallVec::new(); - for item in sorted_positioned_content.into_iter() { - if !item.has_negative_z_index() { - remaining_positioned_content.push(item); - } else { - state.add_display_item(item, DisplayListSection::PositionedContent, stacking_context); - } - } - - state.layerize_display_list_section(DisplayListSection::BlockBackgroundsAndBorders, - stacking_context); - state.layerize_display_list_section(DisplayListSection::Floats, stacking_context); - state.layerize_display_list_section(DisplayListSection::Content, stacking_context); - - for item in remaining_positioned_content.into_iter() { - assert!(!item.has_negative_z_index()); - state.add_display_item(item, DisplayListSection::PositionedContent, stacking_context); - } - - state.layerize_display_list_section(DisplayListSection::Outlines, stacking_context); - - state.finish_building_current_layer(stacking_context); - stacking_context.last_child_layer_info = state.find_last_child_layer_info(stacking_context); +impl Eq for StackingContext {} +impl PartialEq for StackingContext { + fn eq(&self, other: &Self) -> bool { + self.id == other.id } +} - #[inline] - fn layerize_display_list_section(&mut self, - section: DisplayListSection, - stacking_context: &mut StackingContext) { - let section_list = stacking_context.display_list.get_section_mut(section).split_off(0); - for item in section_list.into_iter() { - self.add_display_item(item, section, stacking_context); - } - } +impl fmt::Debug for StackingContext { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let type_string = if self.layer_info.is_some() { + "Layered StackingContext" + } else if self.context_type == StackingContextType::Real { + "StackingContext" + } else { + "Pseudo-StackingContext" + }; - #[inline] - fn all_following_children_need_layers(&self) -> bool { - self.next_layer_info.is_some() - } - - #[inline] - fn display_item_needs_layer(&mut self, item: &DisplayItem) -> bool { - match *item { - LayeredItemClass(_) => true, - StackingContextClass(ref stacking_context) => - stacking_context.layer_info.is_some() || self.all_following_children_need_layers(), - _ => self.all_following_children_need_layers(), - } - } - - #[inline] - fn prepare_ordering_layer(&mut self, - stacking_context: &mut StackingContext) { - if self.building_ordering_layer { - assert!(self.next_layer_info.is_some()); - return; - } - - let next_layer_info = Some(stacking_context - .get_layer_info(self.next_layer_info.unwrap().layer_id) - .next_with_scroll_policy(ScrollPolicy::Scrollable)); - self.finish_building_current_layer(stacking_context); - self.next_layer_info = next_layer_info; - - self.building_ordering_layer = true; - } - - fn add_display_item(&mut self, - item: DisplayItem, - section: DisplayListSection, - parent_stacking_context: &mut StackingContext) { - if !self.display_item_needs_layer(&item) { - if let DisplayItem::StackingContextClass(ref stacking_context) = item { - // This StackingContext has a layered child somewhere in its children. - // We need to give all new StackingContexts their own layer, so that they - // draw on top of this layered child. - if let Some(layer_info) = stacking_context.last_child_layer_info { - self.last_child_layer_info = stacking_context.last_child_layer_info; - self.building_ordering_layer = true; - self.next_layer_info = - Some(layer_info.clone().next_with_scroll_policy(ScrollPolicy::Scrollable)); - } - } - - parent_stacking_context.display_list.add_to_section(item, section); - return; - } - - if let StackingContextClass(ref stacking_context) = item { - // There is a bit of subtlety here. If this item is a stacking context, - // yet doesn't have a layer assigned this code will fall through. This means that - // stacking contexts that are promoted to layers will share layers with sibling - // display items. - let layer_info = stacking_context.layer_info.clone(); - if let Some(mut layer_info) = layer_info { - self.finish_building_current_layer(parent_stacking_context); - - // We have started processing layered stacking contexts, so any stacking context that - // we process from now on needs its own layer to ensure proper rendering order. - self.building_ordering_layer = true; - self.next_layer_info = - Some(layer_info.next_with_scroll_policy(parent_stacking_context.scroll_policy())); - - parent_stacking_context.display_list.layered_children.push_back( - Arc::new(PaintLayer::new_with_stacking_context(layer_info, - stacking_context.clone(), - color::transparent()))); - return; - } - } - - if let LayeredItemClass(item) = item { - if let Some(ref next_layer_info) = self.next_layer_info { - if item.layer_id == next_layer_info.layer_id && !self.building_ordering_layer { - return; - } - } - - self.finish_building_current_layer(parent_stacking_context); - self.building_ordering_layer = false; - self.next_layer_info = - Some(parent_stacking_context.get_layer_info(item.layer_id).clone()); - self.add_display_item_to_display_list(item.item, section); - return; - } - - self.prepare_ordering_layer(parent_stacking_context); - self.add_display_item_to_display_list(item, section); - } - - fn add_display_item_to_display_list(&mut self, - item: DisplayItem, - section: DisplayListSection) { - if self.display_list_for_next_layer.is_none() { - self.display_list_for_next_layer = Some(DisplayList::new()); - } - - if let Some(ref mut display_list) = self.display_list_for_next_layer { - display_list.add_to_section(item, section); - } - } - - fn find_last_child_layer_info(self, - stacking_context: &mut StackingContext) - -> Option { - if let Some(layer) = stacking_context.display_list.layered_children.back() { - return Some(LayerInfo::new(layer.id, ScrollPolicy::Scrollable, None)); - } - - self.last_child_layer_info - } - - #[inline] - fn finish_building_current_layer(&mut self, stacking_context: &mut StackingContext) { - if let Some(display_list) = self.display_list_for_next_layer.take() { - let layer_info = self.next_layer_info.take().unwrap(); - stacking_context.display_list.layered_children.push_back( - Arc::new(PaintLayer::new_with_display_list(layer_info, display_list))); - } + write!(f, "{} at {:?} with overflow {:?}: {:?}", + type_string, + self.bounds, + self.overflow, + self.id) } } @@ -982,7 +669,6 @@ pub enum DisplayItem { GradientClass(Box), LineClass(Box), BoxShadowClass(Box), - StackingContextClass(Arc), LayeredItemClass(Box), NoopClass(Box), IframeClass(Box), @@ -1003,7 +689,9 @@ pub struct BaseDisplayItem { impl BaseDisplayItem { #[inline(always)] - pub fn new(bounds: &Rect, metadata: DisplayItemMetadata, clip: &ClippingRegion) + pub fn new(bounds: &Rect, + metadata: DisplayItemMetadata, + clip: &ClippingRegion) -> BaseDisplayItem { // Detect useless clipping regions here and optimize them to `ClippingRegion::max()`. // The painting backend may want to optimize out clipping regions and this makes it easier @@ -1011,7 +699,7 @@ impl BaseDisplayItem { BaseDisplayItem { bounds: *bounds, metadata: metadata, - clip: if clip.does_not_clip_rect(bounds) { + clip: if clip.does_not_clip_rect(&bounds) { ClippingRegion::max() } else { (*clip).clone() @@ -1020,7 +708,6 @@ impl BaseDisplayItem { } } - /// A clipping region for a display item. Currently, this can describe rectangles, rounded /// rectangles (for `border-radius`), or arbitrary intersections of the two. Arbitrary transforms /// are not supported because those are handled by the higher-level `StackingContext` abstraction. @@ -1412,6 +1099,9 @@ pub struct LayeredItem { /// The id of the layer this item belongs to. pub layer_id: LayerId, + + /// The id of the layer this item belongs to. + pub layer_info: LayerInfo, } /// How a box shadow should be clipped. @@ -1427,31 +1117,13 @@ pub enum BoxShadowClipMode { Inset, } -pub enum DisplayItemIterator<'a> { - Empty, - Parent(linked_list::Iter<'a, DisplayItem>), -} - -impl<'a> Iterator for DisplayItemIterator<'a> { - type Item = &'a DisplayItem; - #[inline] - fn next(&mut self) -> Option<&'a DisplayItem> { - match *self { - DisplayItemIterator::Empty => None, - DisplayItemIterator::Parent(ref mut subiterator) => subiterator.next(), - } - } -} - impl DisplayItem { /// Paints this display item into the given painting context. - fn draw_into_context(&self, transform: &Matrix4, paint_context: &mut PaintContext) { - if let Some(base) = self.base() { - let this_clip = &base.clip; - match paint_context.transient_clip { - Some(ref transient_clip) if transient_clip == this_clip => {} - Some(_) | None => paint_context.push_transient_clip((*this_clip).clone()), - } + fn draw_into_context(&self, paint_context: &mut PaintContext) { + let this_clip = &self.base().clip; + match paint_context.transient_clip { + Some(ref transient_clip) if transient_clip == this_clip => {} + Some(_) | None => paint_context.push_transient_clip((*this_clip).clone()), } match *self { @@ -1506,53 +1178,44 @@ impl DisplayItem { box_shadow.clip_mode); } - DisplayItem::StackingContextClass(ref stacking_context) => { - let pixels_per_px = paint_context.screen_pixels_per_px(); - let new_transform = - transform.translate(stacking_context.bounds - .origin - .x - .to_nearest_pixel(pixels_per_px.get()) as AzFloat, - stacking_context.bounds - .origin - .y - .to_nearest_pixel(pixels_per_px.get()) as AzFloat, - 0.0); - stacking_context.optimize_and_draw_into_context(paint_context, - &new_transform, - Some(&stacking_context.overflow)) - - } - - DisplayItem::LayeredItemClass(_) => panic!("Found layered item during drawing."), + DisplayItem::LayeredItemClass(ref item) => item.item.draw_into_context(paint_context), DisplayItem::NoopClass(_) => { } DisplayItem::IframeClass(..) => {} } } - pub fn base(&self) -> Option<&BaseDisplayItem> { + pub fn intersects_rect_in_parent_context(&self, rect: Option>) -> bool { + let rect = match rect { + Some(ref rect) => rect, + None => return true, + }; + + if !rect.intersects(&self.bounds()) { + return false; + } + + self.base().clip.might_intersect_rect(&rect) + } + + pub fn base(&self) -> &BaseDisplayItem { match *self { - DisplayItem::SolidColorClass(ref solid_color) => Some(&solid_color.base), - DisplayItem::TextClass(ref text) => Some(&text.base), - DisplayItem::ImageClass(ref image_item) => Some(&image_item.base), - DisplayItem::WebGLClass(ref webgl_item) => Some(&webgl_item.base), - DisplayItem::BorderClass(ref border) => Some(&border.base), - DisplayItem::GradientClass(ref gradient) => Some(&gradient.base), - DisplayItem::LineClass(ref line) => Some(&line.base), - DisplayItem::BoxShadowClass(ref box_shadow) => Some(&box_shadow.base), + DisplayItem::SolidColorClass(ref solid_color) => &solid_color.base, + DisplayItem::TextClass(ref text) => &text.base, + DisplayItem::ImageClass(ref image_item) => &image_item.base, + DisplayItem::WebGLClass(ref webgl_item) => &webgl_item.base, + DisplayItem::BorderClass(ref border) => &border.base, + DisplayItem::GradientClass(ref gradient) => &gradient.base, + DisplayItem::LineClass(ref line) => &line.base, + DisplayItem::BoxShadowClass(ref box_shadow) => &box_shadow.base, DisplayItem::LayeredItemClass(ref layered_item) => layered_item.item.base(), - DisplayItem::NoopClass(ref base_item) => Some(base_item), - DisplayItem::StackingContextClass(_) => None, - DisplayItem::IframeClass(ref iframe) => Some(&iframe.base), + DisplayItem::NoopClass(ref base_item) => base_item, + DisplayItem::IframeClass(ref iframe) => &iframe.base, } } pub fn bounds(&self) -> Rect { - match *self { - DisplayItem::StackingContextClass(ref stacking_context) => stacking_context.bounds, - _ => self.base().unwrap().bounds, - } + self.base().bounds } pub fn debug_with_level(&self, level: u32) { @@ -1563,22 +1226,44 @@ impl DisplayItem { println!("{}+ {:?}", indent, self); } - fn compare_zindex(&self, other: &DisplayItem) -> Ordering { - match (self, other) { - (&DisplayItem::StackingContextClass(ref this), - &DisplayItem::StackingContextClass(ref other)) => this.z_index.cmp(&other.z_index), - (&DisplayItem::StackingContextClass(ref this), _) => this.z_index.cmp(&0), - (_, &DisplayItem::StackingContextClass(ref other)) => 0.cmp(&other.z_index), - (_, _) => Ordering::Equal, + fn hit_test(&self, point: Point2D, result: &mut Vec) { + // TODO(pcwalton): Use a precise algorithm here. This will allow us to properly hit + // test elements with `border-radius`, for example. + let base_item = self.base(); + if !base_item.clip.might_intersect_point(&point) { + // Clipped out. + return; + } + if !self.bounds().contains(&point) { + // Can't possibly hit. + return; + } + if base_item.metadata.pointing.is_none() { + // `pointer-events` is `none`. Ignore this item. + return; } - } - fn has_negative_z_index(&self) -> bool { - if let &DisplayItem::StackingContextClass(ref stacking_context) = self { - stacking_context.z_index < 0 - } else { - false + if let DisplayItem::BorderClass(ref border) = *self { + // If the point is inside the border, it didn't hit the border! + let interior_rect = + Rect::new( + Point2D::new(border.base.bounds.origin.x + + border.border_widths.left, + border.base.bounds.origin.y + + border.border_widths.top), + Size2D::new(border.base.bounds.size.width - + (border.border_widths.left + + border.border_widths.right), + border.base.bounds.size.height - + (border.border_widths.top + + border.border_widths.bottom))); + if interior_rect.contains(&point) { + return; + } } + + // We found a hit! + result.push(base_item.metadata); } } @@ -1599,7 +1284,6 @@ impl fmt::Debug for DisplayItem { DisplayItem::GradientClass(_) => "Gradient".to_owned(), DisplayItem::LineClass(_) => "Line".to_owned(), DisplayItem::BoxShadowClass(_) => "BoxShadow".to_owned(), - DisplayItem::StackingContextClass(_) => "StackingContext".to_owned(), DisplayItem::LayeredItemClass(ref layered_item) => format!("LayeredItem({:?})", layered_item.item), DisplayItem::NoopClass(_) => "Noop".to_owned(), @@ -1609,3 +1293,33 @@ impl fmt::Debug for DisplayItem { ) } } + +#[derive(Clone, Debug, PartialEq, Eq, Copy, Hash, Deserialize, Serialize, HeapSizeOf, RustcEncodable)] +pub enum FragmentType { + /// A StackingContext for the fragment body itself. + FragmentBody, + /// A StackingContext created to contain ::before pseudo-element content. + BeforePseudoContent, + /// A StackingContext created to contain ::after pseudo-element content. + AfterPseudoContent, +} + +/// A unique ID for every stacking context. +#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, HeapSizeOf, PartialEq, RustcEncodable, Serialize)] +pub struct StackingContextId( + /// The type of the fragment for this StackingContext. This serves to differentiate + /// StackingContexts that share fragments. + FragmentType, + /// The identifier for this StackingContexts, derived from the Flow's memory address. + usize +); + +impl StackingContextId { + pub fn new(id: usize) -> StackingContextId { + StackingContextId(FragmentType::FragmentBody, id) + } + + pub fn new_of_type(id: usize, fragment_type: FragmentType) -> StackingContextId { + StackingContextId(fragment_type, id) + } +} diff --git a/servo/components/gfx/display_list/optimizer.rs b/servo/components/gfx/display_list/optimizer.rs deleted file mode 100644 index 10abe306549a..000000000000 --- a/servo/components/gfx/display_list/optimizer.rs +++ /dev/null @@ -1,97 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * 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/. */ - -//! Transforms a display list to produce a visually-equivalent, but cheaper-to-paint, one. - -use app_units::Au; -use display_list::{DisplayItem, DisplayList, StackingContext}; -use euclid::rect::Rect; -use euclid::{Matrix2D, Matrix4}; -use std::collections::linked_list::LinkedList; -use std::sync::Arc; -use util::geometry; - -/// Transforms a display list to produce a visually-equivalent, but cheaper-to-paint, one. -pub struct DisplayListOptimizer { - /// The visible rect in page coordinates. - visible_rect: Rect, -} - -impl 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 { - visible_rect: geometry::f32_rect_to_au_rect(*visible_rect), - } - } - - /// 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_display_items(&mut result.positioned_content, - display_list.positioned_content.iter()); - self.add_in_bounds_display_items(&mut result.outlines, - display_list.outlines.iter()); - result - } - - /// Adds display items that intersect the visible rect to `result_list`. - fn add_in_bounds_display_items<'a, I>(&self, - result_list: &mut LinkedList, - display_items: I) - where I: Iterator { - for display_item in display_items { - if !self.should_include_display_item(display_item) { - continue; - } - result_list.push_back((*display_item).clone()) - } - } - - fn should_include_display_item(&self, item: &DisplayItem) -> bool { - if let &DisplayItem::StackingContextClass(ref stacking_context) = item { - return self.should_include_stacking_context(stacking_context); - } - - if !self.visible_rect.intersects(&item.bounds()) { - return false; - } - - if let Some(base_item) = item.base() { - if !base_item.clip.might_intersect_rect(&self.visible_rect) { - return false; - } - } - - true - } - - fn should_include_stacking_context(&self, stacking_context: &Arc) -> bool { - // Transform this stacking context to get it into the same space as - // the parent stacking context. - let origin_x = stacking_context.bounds.origin.x.to_f32_px(); - let origin_y = stacking_context.bounds.origin.y.to_f32_px(); - - let transform = Matrix4::identity().translate(origin_x, - origin_y, - 0.0) - .mul(&stacking_context.transform); - let transform_2d = Matrix2D::new(transform.m11, transform.m12, - transform.m21, transform.m22, - transform.m41, transform.m42); - - let overflow = geometry::au_rect_to_f32_rect(stacking_context.overflow); - let overflow = transform_2d.transform_rect(&overflow); - let overflow = geometry::f32_rect_to_au_rect(overflow); - - self.visible_rect.intersects(&overflow) - } -} diff --git a/servo/components/gfx/paint_thread.rs b/servo/components/gfx/paint_thread.rs index fdb85aff3391..b427ae70ea3f 100644 --- a/servo/components/gfx/paint_thread.rs +++ b/servo/components/gfx/paint_thread.rs @@ -8,15 +8,16 @@ use app_units::Au; use azure::AzFloat; use azure::azure_hl::{BackendType, Color, DrawTarget, SurfaceFormat}; use canvas_traits::CanvasMsg; -use display_list::{DisplayItem, DisplayList, LayerInfo, StackingContext}; +use display_list::{DisplayItem, DisplayList, DisplayListEntry, DisplayListTraversal}; +use display_list::{LayerInfo, StackingContext, StackingContextId, StackingContextType}; use euclid::Matrix4; use euclid::point::Point2D; use euclid::rect::Rect; use euclid::size::Size2D; use font_cache_thread::FontCacheThread; use font_context::FontContext; -use gfx_traits::{Epoch, FrameTreeId, LayerId, LayerKind, LayerProperties}; -use gfx_traits::{PaintListener, PaintMsg as ConstellationMsg, ScrollPolicy, color}; +use gfx_traits::PaintMsg as ConstellationMsg; +use gfx_traits::{Epoch, FrameTreeId, LayerId, LayerKind, LayerProperties, PaintListener}; use ipc_channel::ipc::IpcSender; use layers::layers::{BufferRequest, LayerBuffer, LayerBufferSet}; use layers::platform::surface::{NativeDisplay, NativeSurface}; @@ -37,143 +38,292 @@ use util::opts; use util::thread; use util::thread_state; -#[derive(Clone, Deserialize, Serialize, HeapSizeOf)] -pub enum PaintLayerContents { - StackingContext(Arc), - DisplayList(Arc), -} +#[derive(Clone, HeapSizeOf)] +struct PaintLayer { + /// The LayerProperties, which describe the layer in a way that the Compositor + /// can consume. + pub layer_properties: LayerProperties, -/// Information about a hardware graphics layer that layout sends to the painting thread. -#[derive(Clone, Deserialize, Serialize, HeapSizeOf)] -pub struct PaintLayer { - /// A per-pipeline ID describing this layer that should be stable across reflows. - pub id: LayerId, - /// The color of the background in this layer. Used for unpainted content. - pub background_color: Color, - /// The content of this layer, which is either a stacking context or a display list. - pub contents: PaintLayerContents, - /// The layer's boundaries in the parent layer's coordinate system. - pub bounds: Rect, - /// The scrolling policy of this layer. - pub scroll_policy: ScrollPolicy, - /// The pipeline of the subpage that this layer represents, if there is one. - pub subpage_pipeline_id: Option, + /// The StackingContextId of the StackingContext that is the immediate + /// parent of this layer. This is used to ensure applying the proper transform + /// when painting. + pub starting_stacking_context_id: StackingContextId, + + /// The indices (in the DisplayList) to the first and last display item + /// that are the contents of this layer. + pub display_list_indices: Option<(usize, usize)>, + + /// When painting, whether to draw the start by entering the surrounding StackingContext + /// or simply to draw the single item this PaintLayer contains. + pub single_item: bool, + + /// The layer's bounds start at the overflow origin, but display items are + /// positioned relative to the stacking context bounds, so we need to + /// offset by the overflow rect (which will be in the coordinate system of + /// the stacking context bounds). + pub display_list_origin: Point2D } impl PaintLayer { - /// Creates a new `PaintLayer` with a stacking context. - pub fn new_with_stacking_context(layer_info: LayerInfo, - stacking_context: Arc, - background_color: Color) - -> PaintLayer { + fn new_from_stacking_context(layer_info: &LayerInfo, + stacking_context: &StackingContext, + parent_origin: &Point2D, + transform: &Matrix4, + perspective: &Matrix4, + parent_id: Option) + -> PaintLayer { let bounds = Rect::new(stacking_context.bounds.origin + stacking_context.overflow.origin, stacking_context.overflow.size); - PaintLayer { - id: layer_info.layer_id, - background_color: background_color, - contents: PaintLayerContents::StackingContext(stacking_context), - bounds: bounds, - scroll_policy: layer_info.scroll_policy, - subpage_pipeline_id: layer_info.subpage_pipeline_id, - } - } - - /// Creates a new `PaintLayer` with a display list. - pub fn new_with_display_list(layer_info: LayerInfo, - display_list: DisplayList) - -> PaintLayer { - let bounds = display_list.calculate_bounding_rect().expand_to_px_boundaries(); - PaintLayer { - id: layer_info.layer_id, - background_color: color::transparent(), - contents: PaintLayerContents::DisplayList(Arc::new(display_list)), - bounds: bounds, - scroll_policy: layer_info.scroll_policy, - subpage_pipeline_id: layer_info.subpage_pipeline_id, - } - } - - pub fn find_layer_with_layer_id(this: &Arc, - layer_id: LayerId) - -> Option> { - if this.id == layer_id { - return Some(this.clone()); - } - - match this.contents { - PaintLayerContents::StackingContext(ref stacking_context) => - stacking_context.display_list.find_layer_with_layer_id(layer_id), - PaintLayerContents::DisplayList(ref display_list) => - display_list.find_layer_with_layer_id(layer_id), - } - } - - fn build_layer_properties(&self, - parent_origin: &Point2D, - transform: &Matrix4, - perspective: &Matrix4, - parent_id: Option) - -> LayerProperties { let layer_boundaries = Rect::new( - Point2D::new((parent_origin.x + self.bounds.min_x()).to_nearest_px() as f32, - (parent_origin.y + self.bounds.min_y()).to_nearest_px() as f32), - Size2D::new(self.bounds.size.width.to_nearest_px() as f32, - self.bounds.size.height.to_nearest_px() as f32)); + Point2D::new((parent_origin.x + bounds.min_x()).to_nearest_px() as f32, + (parent_origin.y + bounds.min_y()).to_nearest_px() as f32), + Size2D::new(bounds.size.width.to_nearest_px() as f32, + bounds.size.height.to_nearest_px() as f32)); - let (transform, - perspective, - establishes_3d_context, - scrolls_overflow_area) = match self.contents { - PaintLayerContents::StackingContext(ref stacking_context) => { - (transform.mul(&stacking_context.transform), - perspective.mul(&stacking_context.perspective), - stacking_context.establishes_3d_context, - stacking_context.scrolls_overflow_area) + let transform = transform.mul(&stacking_context.transform); + let perspective = perspective.mul(&stacking_context.perspective); + let establishes_3d_context = stacking_context.establishes_3d_context; + let scrolls_overflow_area = stacking_context.scrolls_overflow_area; + + PaintLayer { + layer_properties: LayerProperties { + id: layer_info.layer_id, + parent_id: parent_id, + rect: layer_boundaries, + background_color: layer_info.background_color, + scroll_policy: layer_info.scroll_policy, + transform: transform, + perspective: perspective, + establishes_3d_context: establishes_3d_context, + scrolls_overflow_area: scrolls_overflow_area, + subpage_pipeline_id: layer_info.subpage_pipeline_id, }, - PaintLayerContents::DisplayList(_) => { - (*transform, *perspective, false, false) - } + starting_stacking_context_id: stacking_context.id, + display_list_indices: None, + single_item: false, + display_list_origin: Point2D::new(stacking_context.overflow.origin.x.to_f32_px(), + stacking_context.overflow.origin.y.to_f32_px()), + } + } + + fn new_for_display_item(layer_info: &LayerInfo, + item_bounds: &Rect, + parent_origin: &Point2D, + transform: &Matrix4, + perspective: &Matrix4, + parent_id: Option, + stacking_context_id: StackingContextId, + item_index: usize) + -> PaintLayer { + let bounds = item_bounds.expand_to_px_boundaries(); + let layer_boundaries = Rect::new( + Point2D::new((parent_origin.x + bounds.min_x()).to_nearest_px() as f32, + (parent_origin.y + bounds.min_y()).to_nearest_px() as f32), + Size2D::new(bounds.size.width.to_nearest_px() as f32, + bounds.size.height.to_nearest_px() as f32)); + + PaintLayer { + layer_properties: LayerProperties { + id: layer_info.layer_id, + parent_id: parent_id, + rect: layer_boundaries, + background_color: layer_info.background_color, + scroll_policy: layer_info.scroll_policy, + transform: *transform, + perspective: *perspective, + establishes_3d_context: false, + scrolls_overflow_area: false, + subpage_pipeline_id: layer_info.subpage_pipeline_id, + }, + starting_stacking_context_id: stacking_context_id, + display_list_indices: Some((item_index, item_index)), + single_item: true, + display_list_origin: Point2D::new(bounds.origin.x.to_f32_px(), + bounds.origin.y.to_f32_px()), + } + } + + fn add_item(&mut self, index: usize) { + let indices = match self.display_list_indices { + Some((first, _)) => (first, index), + None => (index, index), }; + self.display_list_indices = Some(indices); + } - LayerProperties { - id: self.id, - parent_id: parent_id, - rect: layer_boundaries, - background_color: self.background_color, - scroll_policy: self.scroll_policy, - transform: transform, - perspective: perspective, - establishes_3d_context: establishes_3d_context, - scrolls_overflow_area: scrolls_overflow_area, - subpage_pipeline_id: self.subpage_pipeline_id, + fn make_companion_layer(&mut self) { + self.layer_properties.id = self.layer_properties.id.companion_layer_id(); + self.display_list_indices = None; + } +} + +struct LayerCreator { + layers: Vec, + layer_details_stack: Vec, + current_layer: Option, + current_entry_index: usize, +} + +impl LayerCreator { + fn create_layers_with_display_list(display_list: &DisplayList) -> Vec { + let mut layer_creator = LayerCreator { + layers: Vec::new(), + layer_details_stack: Vec::new(), + current_layer: None, + current_entry_index: 0, + }; + let mut traversal = DisplayListTraversal { + display_list: display_list, + current_item_index: 0, + last_item_index: display_list.list.len(), + }; + layer_creator.create_layers_for_stacking_context(&display_list.root_stacking_context, + &mut traversal, + &Point2D::zero(), + &Matrix4::identity(), + &Matrix4::identity()); + layer_creator.layers + } + + fn finalize_current_layer(&mut self) { + if let Some(current_layer) = self.current_layer.take() { + self.layers.push(current_layer); } } - // The origin for child layers might be somewhere other than the layer origin, - // since layer boundaries are expanded to include overflow. - pub fn origin_for_child_layers(&self) -> Point2D { - match self.contents { - PaintLayerContents::StackingContext(ref stacking_context) => - -stacking_context.overflow.origin, - PaintLayerContents::DisplayList(_) => Point2D::zero(), - } + fn current_parent_layer_id(&self) -> Option { + self.layer_details_stack.last().as_ref().map(|layer| + layer.layer_properties.id + ) } - pub fn display_list_origin(&self) -> Point2D { - // The layer's bounds start at the overflow origin, but display items are - // positioned relative to the stacking context counds, so we need to - // offset by the overflow rect (which will be in the coordinate system of - // the stacking context bounds). - match self.contents { - PaintLayerContents::StackingContext(ref stacking_context) => { - Point2D::new(stacking_context.overflow.origin.x.to_f32_px(), - stacking_context.overflow.origin.y.to_f32_px()) + fn current_parent_stacking_context_id(&self) -> StackingContextId { + self.layer_details_stack.last().unwrap().starting_stacking_context_id + } - }, - PaintLayerContents::DisplayList(_) => { - Point2D::new(self.bounds.origin.x.to_f32_px(), self.bounds.origin.y.to_f32_px()) + fn create_layers_for_stacking_context<'a>(&mut self, + stacking_context: &StackingContext, + traversal: &mut DisplayListTraversal<'a>, + parent_origin: &Point2D, + transform: &Matrix4, + perspective: &Matrix4) { + if let Some(ref layer_info) = stacking_context.layer_info { + self.finalize_current_layer(); + let new_layer = PaintLayer::new_from_stacking_context( + layer_info, + stacking_context, + parent_origin, + transform, + perspective, + self.current_parent_layer_id()); + self.layer_details_stack.push(new_layer.clone()); + self.current_layer = Some(new_layer); + + // When there is a new layer, the transforms and origin are handled by + // the compositor, so the new transform and perspective matrices are + // just the identity. + // + // The origin for child layers which might be somewhere other than the + // layer origin, since layer boundaries are expanded to include overflow. + self.process_stacking_context_items(stacking_context, + traversal, + &-stacking_context.overflow.origin, + &Matrix4::identity(), + &Matrix4::identity()); + self.finalize_current_layer(); + self.layer_details_stack.pop(); + return; + } + + if stacking_context.context_type != StackingContextType::Real { + self.process_stacking_context_items(stacking_context, + traversal, + parent_origin, + transform, + perspective); + return; + } + + self.process_stacking_context_items(stacking_context, + traversal, + &(stacking_context.bounds.origin + *parent_origin), + &transform.mul(&stacking_context.transform), + &perspective.mul(&stacking_context.perspective)); + } + + fn process_stacking_context_items<'a>(&mut self, + stacking_context: &StackingContext, + traversal: &mut DisplayListTraversal<'a>, + parent_origin: &Point2D, + transform: &Matrix4, + perspective: &Matrix4) { + for kid in stacking_context.children.iter() { + while let Some(item) = traversal.advance(stacking_context) { + self.create_layers_for_item(item, + parent_origin, + transform, + perspective); } + self.create_layers_for_stacking_context(kid, + traversal, + parent_origin, + transform, + perspective); } + + while let Some(item) = traversal.advance(stacking_context) { + self.create_layers_for_item(item, + parent_origin, + transform, + perspective); + } + } + + + fn create_layers_for_item<'a>(&mut self, + item: &DisplayListEntry, + parent_origin: &Point2D, + transform: &Matrix4, + perspective: &Matrix4) { + if let DisplayItem::LayeredItemClass(ref layered_item) = item.item { + // We need to finalize the last layer here before incrementing the entry + // index, otherwise this item will be placed into the parent layer. + self.finalize_current_layer(); + let layer = PaintLayer::new_for_display_item( + &layered_item.layer_info, + &layered_item.item.bounds(), + parent_origin, + transform, + perspective, + self.current_parent_layer_id(), + self.current_parent_stacking_context_id(), + self.current_entry_index); + self.layers.push(layer); + self.current_entry_index += 1; + return; + } + + // If we don't have a current layer, we are an item that belonged to a + // previous layer that was finalized by a child layer. We need to + // resurrect a copy of the original ancestor layer to ensure that this + // item is ordered on top of the child layers when painted. + if self.current_layer.is_none() { + let mut new_layer = self.layer_details_stack.pop().unwrap(); + new_layer.make_companion_layer(); + + if new_layer.layer_properties.parent_id == None { + new_layer.layer_properties.parent_id = + Some(new_layer.layer_properties.id.original()); + } + + self.layer_details_stack.push(new_layer.clone()); + self.current_layer = Some(new_layer); + } + + if let Some(ref mut current_layer) = self.current_layer { + current_layer.add_item(self.current_entry_index); + } + self.current_entry_index += 1; } } @@ -192,7 +342,7 @@ pub enum Msg { #[derive(Deserialize, Serialize)] pub enum LayoutToPaintMsg { - PaintInit(Epoch, PaintLayer), + PaintInit(Epoch, Arc), CanvasLayer(LayerId, IpcSender), Exit(IpcSender<()>), } @@ -216,7 +366,10 @@ pub struct PaintThread { time_profiler_chan: time::ProfilerChan, /// The root paint layer sent to us by the layout thread. - root_paint_layer: Option>, + root_display_list: Option>, + + /// A map that associates LayerIds with their corresponding layers. + layer_map: HashMap>, /// Permission to send paint messages to the compositor paint_permission: bool, @@ -273,7 +426,8 @@ impl PaintThread where C: PaintListener + Send + 'static { chrome_to_paint_port: chrome_to_paint_port, compositor: compositor, time_profiler_chan: time_profiler_chan, - root_paint_layer: None, + root_display_list: None, + layer_map: HashMap::new(), paint_permission: false, current_epoch: None, worker_threads: worker_threads, @@ -312,9 +466,9 @@ impl PaintThread where C: PaintListener + Send + 'static { }; match message { - Msg::FromLayout(LayoutToPaintMsg::PaintInit(epoch, paint_layer)) => { + Msg::FromLayout(LayoutToPaintMsg::PaintInit(epoch, display_list)) => { self.current_epoch = Some(epoch); - self.root_paint_layer = Some(Arc::new(paint_layer)); + self.root_display_list = Some(display_list); if self.paint_permission { self.initialize_layers(); @@ -326,7 +480,7 @@ impl PaintThread where C: PaintListener + Send + 'static { self.canvas_map.insert(layer_id, canvas_renderer); } Msg::FromChrome(ChromeToPaintMsg::Paint(requests, frame_tree_id)) => { - if self.paint_permission && self.root_paint_layer.is_some() { + if self.paint_permission && self.root_display_list.is_some() { let mut replies = Vec::new(); for PaintRequest { buffer_requests, scale, layer_id, epoch, layer_kind } in requests { @@ -350,7 +504,7 @@ impl PaintThread where C: PaintListener + Send + 'static { Msg::FromChrome(ChromeToPaintMsg::PaintPermissionGranted) => { self.paint_permission = true; - if self.root_paint_layer.is_some() { + if self.root_display_list.is_some() { self.initialize_layers(); } } @@ -390,14 +544,15 @@ impl PaintThread where C: PaintListener + Send + 'static { layer_id: LayerId, layer_kind: LayerKind) { time::profile(time::ProfilerCategory::Painting, None, self.time_profiler_chan.clone(), || { + let display_list = match self.root_display_list { + Some(ref display_list) => display_list.clone(), + None => return, + }; + // Bail out if there is no appropriate layer. - let paint_layer = if let Some(ref root_paint_layer) = self.root_paint_layer { - match PaintLayer::find_layer_with_layer_id(root_paint_layer, layer_id) { - Some(paint_layer) => paint_layer, - None => return, - } - } else { - return + let layer = match self.layer_map.get(&layer_id) { + Some(layer) => layer.clone(), + None => return, }; // Divide up the layer into tiles and distribute them to workers via a simple round- @@ -408,7 +563,8 @@ impl PaintThread where C: PaintListener + Send + 'static { let thread_id = i % self.worker_threads.len(); self.worker_threads[thread_id].paint_tile(thread_id, tile, - paint_layer.clone(), + display_list.clone(), + layer.clone(), scale, layer_kind); } @@ -425,109 +581,18 @@ impl PaintThread where C: PaintListener + Send + 'static { } fn initialize_layers(&mut self) { - let root_paint_layer = match self.root_paint_layer { + let root_display_list = match self.root_display_list { None => return, - Some(ref root_paint_layer) => root_paint_layer, + Some(ref root_display_list) => root_display_list, }; - - let mut properties = Vec::new(); - build_from_paint_layer(&mut properties, - root_paint_layer, - &Point2D::zero(), - &Matrix4::identity(), - &Matrix4::identity(), - None); + let layers = LayerCreator::create_layers_with_display_list(&root_display_list); + let properties = layers.iter().map(|layer| layer.layer_properties.clone()).collect(); self.compositor.initialize_layers_for_pipeline(self.id, properties, self.current_epoch.unwrap()); - - fn build_from_paint_layer(properties: &mut Vec, - paint_layer: &Arc, - parent_origin: &Point2D, - transform: &Matrix4, - perspective: &Matrix4, - parent_id: Option) { - - properties.push(paint_layer.build_layer_properties(parent_origin, - transform, - perspective, - parent_id)); - - match paint_layer.contents { - PaintLayerContents::StackingContext(ref context) => { - // When there is a new layer, the transforms and origin are handled by the compositor, - // so the new transform and perspective matrices are just the identity. - continue_walking_stacking_context(properties, - &context, - &paint_layer.origin_for_child_layers(), - &Matrix4::identity(), - &Matrix4::identity(), - Some(paint_layer.id)); - }, - PaintLayerContents::DisplayList(ref display_list) => { - for kid in display_list.positioned_content.iter() { - if let &DisplayItem::StackingContextClass(ref stacking_context) = kid { - build_from_stacking_context(properties, - &stacking_context, - &parent_origin, - &transform, - &perspective, - parent_id) - - } - } - for kid in display_list.layered_children.iter() { - build_from_paint_layer(properties, - &kid, - &parent_origin, - &transform, - &perspective, - parent_id) - } - }, - } - } - - fn build_from_stacking_context(properties: &mut Vec, - stacking_context: &Arc, - parent_origin: &Point2D, - transform: &Matrix4, - perspective: &Matrix4, - parent_id: Option) { - continue_walking_stacking_context(properties, - stacking_context, - &(stacking_context.bounds.origin + *parent_origin), - &transform.mul(&stacking_context.transform), - &perspective.mul(&stacking_context.perspective), - parent_id); - } - - fn continue_walking_stacking_context(properties: &mut Vec, - stacking_context: &Arc, - parent_origin: &Point2D, - transform: &Matrix4, - perspective: &Matrix4, - parent_id: Option) { - for kid in stacking_context.display_list.positioned_content.iter() { - if let &DisplayItem::StackingContextClass(ref stacking_context) = kid { - build_from_stacking_context(properties, - &stacking_context, - &parent_origin, - &transform, - &perspective, - parent_id) - - } - } - - for kid in stacking_context.display_list.layered_children.iter() { - build_from_paint_layer(properties, - &kid, - &parent_origin, - &transform, - &perspective, - parent_id) - } + self.layer_map.clear(); + for layer in layers.into_iter() { + self.layer_map.insert(layer.layer_properties.id, Arc::new(layer)); } } } @@ -570,11 +635,13 @@ impl WorkerThreadProxy { fn paint_tile(&mut self, thread_id: usize, tile: BufferRequest, + display_list: Arc, paint_layer: Arc, scale: f32, layer_kind: LayerKind) { let msg = MsgToWorkerThread::PaintTile(thread_id, tile, + display_list, paint_layer, scale, layer_kind); @@ -640,9 +707,15 @@ impl WorkerThread { loop { match self.receiver.recv().unwrap() { MsgToWorkerThread::Exit => break, - MsgToWorkerThread::PaintTile(thread_id, tile, paint_layer, scale, layer_kind) => { + MsgToWorkerThread::PaintTile(thread_id, + tile, + display_list, + paint_layer, + scale, + layer_kind) => { let buffer = self.optimize_and_paint_tile(thread_id, tile, + display_list, paint_layer, scale, layer_kind); @@ -676,6 +749,7 @@ impl WorkerThread { fn optimize_and_paint_tile(&mut self, thread_id: usize, mut tile: BufferRequest, + display_list: Arc, paint_layer: Arc, scale: f32, layer_kind: LayerKind) @@ -700,7 +774,7 @@ impl WorkerThread { // Apply the translation to paint the tile we want. let matrix = Matrix4::identity(); let matrix = matrix.scale(scale as AzFloat, scale as AzFloat, 1.0); - let tile_bounds = tile.page_rect.translate(&paint_layer.display_list_origin()); + let tile_bounds = tile.page_rect.translate(&paint_layer.display_list_origin); let matrix = matrix.translate(-tile_bounds.origin.x as AzFloat, -tile_bounds.origin.y as AzFloat, 0.0); @@ -711,26 +785,22 @@ impl WorkerThread { // Draw the display list. time::profile(time::ProfilerCategory::PaintingPerTile, None, - self.time_profiler_sender.clone(), - || { - match paint_layer.contents { - PaintLayerContents::StackingContext(ref stacking_context) => { - stacking_context.optimize_and_draw_into_context(&mut paint_context, - &matrix, - None); - } - PaintLayerContents::DisplayList(ref display_list) => { - paint_context.remove_transient_clip_if_applicable(); - let draw_target = paint_context.draw_target.clone(); - display_list.draw_into_context(&draw_target, - &mut paint_context, - &matrix, - None); - } - } - - paint_context.draw_target.flush(); - }); + self.time_profiler_sender.clone(), || { + if let Some((start, end)) = paint_layer.display_list_indices { + if paint_layer.single_item { + display_list.draw_item_at_index_into_context( + &mut paint_context, &matrix, start); + } else { + display_list.draw_into_context( + &mut paint_context, + &matrix, + paint_layer.starting_stacking_context_id, + start, + end); + } + } + paint_context.draw_target.flush(); + }); if opts::get().show_debug_parallel_paint { // Overlay a transparent solid color to identify the thread that @@ -791,7 +861,7 @@ impl WorkerThread { enum MsgToWorkerThread { Exit, - PaintTile(usize, BufferRequest, Arc, f32, LayerKind), + PaintTile(usize, BufferRequest, Arc, Arc, f32, LayerKind), } enum MsgFromWorkerThread { diff --git a/servo/components/gfx_traits/lib.rs b/servo/components/gfx_traits/lib.rs index 84e74038e40a..c3cda7c1bb98 100644 --- a/servo/components/gfx_traits/lib.rs +++ b/servo/components/gfx_traits/lib.rs @@ -98,11 +98,16 @@ impl LayerId { let LayerId(layer_type, id, companion) = *self; LayerId(layer_type, id, companion + 1) } + + pub fn original(&self) -> LayerId { + let LayerId(layer_type, id, _) = *self; + LayerId(layer_type, id, 0) + } } /// All layer-specific information that the painting task sends to the compositor other than the /// buffer contents of the layer itself. -#[derive(Copy, Clone)] +#[derive(Copy, Clone, HeapSizeOf)] pub struct LayerProperties { /// An opaque ID. This is usually the address of the flow and index of the box within it. pub id: LayerId, diff --git a/servo/components/layout/block.rs b/servo/components/layout/block.rs index 05ffa1838f0e..a538a768efd1 100644 --- a/servo/components/layout/block.rs +++ b/servo/components/layout/block.rs @@ -29,8 +29,8 @@ use app_units::{Au, MAX_AU}; use context::LayoutContext; -use display_list_builder::{BlockFlowDisplayListBuilding, BorderPaintingMode}; -use display_list_builder::{FragmentDisplayListBuilding}; +use display_list_builder::BlockFlowDisplayListBuilding; +use display_list_builder::{BorderPaintingMode, DisplayListBuildState, FragmentDisplayListBuilding}; use euclid::{Point2D, Rect, Size2D}; use floats::{ClearType, FloatKind, Floats, PlacementInfo}; use flow::{BLOCK_POSITION_IS_STATIC}; @@ -45,7 +45,7 @@ use flow_list::FlowList; use flow_ref::FlowRef; use fragment::{CoordinateSystem, Fragment, FragmentBorderBoxIterator, HAS_LAYER, Overflow}; use fragment::{SpecificFragmentInfo}; -use gfx::display_list::{ClippingRegion, DisplayList}; +use gfx::display_list::{ClippingRegion, StackingContext, StackingContextId}; use gfx_traits::LayerId; use incremental::{BUBBLE_ISIZES, REFLOW, REFLOW_OUT_OF_FLOW, REPAINT}; use layout_debug; @@ -1622,6 +1622,29 @@ impl BlockFlow { } } + pub fn establishes_pseudo_stacking_context(&self) -> bool { + if self.fragment.establishes_stacking_context() { + return false; + } + + self.base.flags.contains(IS_ABSOLUTELY_POSITIONED) || + self.fragment.style.get_box().position != position::T::static_ || + self.base.flags.is_float() + } + + pub fn has_scrolling_overflow(&self) -> bool { + if !self.base.flags.contains(IS_ABSOLUTELY_POSITIONED) { + return false; + } + + match (self.fragment.style().get_box().overflow_x, + self.fragment.style().get_box().overflow_y.0) { + (overflow_x::T::auto, _) | (overflow_x::T::scroll, _) | + (_, overflow_x::T::auto) | (_, overflow_x::T::scroll) => true, + (_, _) => false, + } + } + pub fn compute_inline_sizes(&mut self, layout_context: &LayoutContext) { if !self.base.restyle_damage.intersects(REFLOW_OUT_OF_FLOW | REFLOW) { return @@ -2084,10 +2107,15 @@ impl Flow for BlockFlow { } } - fn build_display_list(&mut self, layout_context: &LayoutContext) { - self.build_display_list_for_block(box DisplayList::new(), - layout_context, - BorderPaintingMode::Separate); + fn collect_stacking_contexts(&mut self, + parent_id: StackingContextId, + contexts: &mut Vec) + -> StackingContextId { + self.collect_stacking_contexts_for_block(parent_id, contexts) + } + + fn build_display_list(&mut self, state: &mut DisplayListBuildState) { + self.build_display_list_for_block(state, BorderPaintingMode::Separate); self.fragment.restyle_damage.remove(REPAINT); if opts::get().validate_display_list_geometry { self.base.validate_display_list_geometry(); diff --git a/servo/components/layout/display_list_builder.rs b/servo/components/layout/display_list_builder.rs index 8d5e5c48ed01..04beaca20645 100644 --- a/servo/components/layout/display_list_builder.rs +++ b/servo/components/layout/display_list_builder.rs @@ -24,11 +24,12 @@ use fragment::{CoordinateSystem, Fragment, HAS_LAYER, ImageFragmentInfo, Scanned use fragment::{SpecificFragmentInfo}; use gfx::display_list::{BLUR_INFLATION_FACTOR, BaseDisplayItem, BorderDisplayItem}; use gfx::display_list::{BorderRadii, BoxShadowClipMode, BoxShadowDisplayItem, ClippingRegion}; -use gfx::display_list::{DisplayItem, DisplayItemMetadata, DisplayList, DisplayListSection}; +use gfx::display_list::{DisplayItem, DisplayItemMetadata, DisplayListSection}; use gfx::display_list::{GradientDisplayItem}; use gfx::display_list::{GradientStop, IframeDisplayItem, ImageDisplayItem, WebGLDisplayItem, LayeredItem, LayerInfo}; use gfx::display_list::{LineDisplayItem, OpaqueNode, SolidColorDisplayItem}; -use gfx::display_list::{StackingContext, TextDisplayItem, TextOrientation}; +use gfx::display_list::{StackingContext, StackingContextId, StackingContextType}; +use gfx::display_list::{TextDisplayItem, TextOrientation, DisplayListEntry}; use gfx::paint_thread::THREAD_TINT_COLORS; use gfx::text::glyph::CharIndex; use gfx_traits::{color, ScrollPolicy}; @@ -59,6 +60,53 @@ use table_cell::CollapsedBordersForCell; use url::Url; use util::opts; +pub struct DisplayListBuildState<'a> { + pub layout_context: &'a LayoutContext<'a>, + pub items: Vec, + pub stacking_context_id_stack: Vec, +} + +impl<'a> DisplayListBuildState<'a> { + pub fn new(layout_context: &'a LayoutContext, + stacking_context_id: StackingContextId) + -> DisplayListBuildState<'a> { + DisplayListBuildState { + layout_context: layout_context, + items: Vec::new(), + stacking_context_id_stack: vec!(stacking_context_id), + } + } + + fn add_display_item(&mut self, display_item: DisplayItem, section: DisplayListSection) { + let stacking_context_id = self.stacking_context_id(); + self.items.push( + DisplayListEntry { + stacking_context_id: stacking_context_id, + section: section, + item: display_item + }); + } + + fn append_from(&mut self, other_list: &mut Option>) { + if let Some(mut other) = other_list.take() { + self.items.append(&mut other); + } + } + + fn stacking_context_id(&self) -> StackingContextId { + self.stacking_context_id_stack.last().unwrap().clone() + } + + fn push_stacking_context_id(&mut self, stacking_context_id: StackingContextId) { + self.stacking_context_id_stack.push(stacking_context_id); + } + + fn pop_stacking_context_id(&mut self) { + self.stacking_context_id_stack.pop(); + assert!(!self.stacking_context_id_stack.is_empty()); + } +} + /// The logical width of an insertion point: at the moment, a one-pixel-wide line. const INSERTION_POINT_LOGICAL_WIDTH: Au = Au(1 * AU_PER_PX); @@ -83,9 +131,8 @@ 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, + state: &mut DisplayListBuildState, style: &ComputedValues, - display_list: &mut DisplayList, - layout_context: &LayoutContext, display_list_section: DisplayListSection, absolute_bounds: &Rect, clip: &ClippingRegion); @@ -101,9 +148,8 @@ pub trait FragmentDisplayListBuilding { /// Adds the display items necessary to paint the background image of this fragment to the /// appropriate section of the display list. fn build_display_list_for_background_image(&self, + state: &mut DisplayListBuildState, style: &ComputedValues, - display_list: &mut DisplayList, - layout_context: &LayoutContext, display_list_section: DisplayListSection, absolute_bounds: &Rect, clip: &ClippingRegion, @@ -112,7 +158,7 @@ pub trait FragmentDisplayListBuilding { /// Adds the display items necessary to paint the background linear gradient of this fragment /// to the appropriate section of the display list. fn build_display_list_for_background_linear_gradient(&self, - display_list: &mut DisplayList, + state: &mut DisplayListBuildState, display_list_section: DisplayListSection, absolute_bounds: &Rect, clip: &ClippingRegion, @@ -123,9 +169,9 @@ pub trait FragmentDisplayListBuilding { /// necessary. fn build_display_list_for_borders_if_applicable( &self, + state: &mut DisplayListBuildState, style: &ComputedValues, border_painting_mode: BorderPaintingMode, - display_list: &mut DisplayList, bounds: &Rect, display_list_section: DisplayListSection, clip: &ClippingRegion); @@ -133,25 +179,24 @@ pub trait FragmentDisplayListBuilding { /// Adds the display items necessary to paint the outline of this fragment to the display list /// if necessary. fn build_display_list_for_outline_if_applicable(&self, + state: &mut DisplayListBuildState, style: &ComputedValues, - display_list: &mut DisplayList, bounds: &Rect, clip: &ClippingRegion); /// Adds the display items necessary to paint the box shadow of this fragment to the display /// list if necessary. fn build_display_list_for_box_shadow_if_applicable(&self, + state: &mut DisplayListBuildState, style: &ComputedValues, - list: &mut DisplayList, - layout_context: &LayoutContext, display_list_section: DisplayListSection, absolute_bounds: &Rect, clip: &ClippingRegion); /// Adds display items necessary to draw debug boxes around a scanned text fragment. fn build_debug_borders_around_text_fragments(&self, + state: &mut DisplayListBuildState, style: &ComputedValues, - display_list: &mut DisplayList, stacking_relative_border_box: &Rect, stacking_relative_content_box: &Rect, text_fragment: &ScannedTextFragmentInfo, @@ -159,7 +204,7 @@ pub trait FragmentDisplayListBuilding { /// Adds display items necessary to draw debug boxes around this fragment. fn build_debug_borders_around_fragment(&self, - display_list: &mut DisplayList, + state: &mut DisplayListBuildState, stacking_relative_border_box: &Rect, clip: &ClippingRegion); @@ -167,8 +212,8 @@ pub trait FragmentDisplayListBuilding { /// /// Arguments: /// - /// * `display_list`: The display list to add display items to. - /// * `layout_context`: The layout context. + /// * `state`: The display building state, including the display list currently + /// under construction and other metadata useful for constructing it. /// * `dirty`: The dirty rectangle in the coordinate system of the owning flow. /// * `stacking_relative_flow_origin`: Position of the origin of the owning flow with respect /// to its nearest ancestor stacking context. @@ -178,8 +223,7 @@ pub trait FragmentDisplayListBuilding { /// * `stacking_relative_display_port`: The position and size of the display port with respect /// to the nearest ancestor stacking context. fn build_display_list(&mut self, - display_list: &mut DisplayList, - layout_context: &LayoutContext, + state: &mut DisplayListBuildState, stacking_relative_flow_origin: &Point2D, relative_containing_block_size: &LogicalSize, relative_containing_block_mode: WritingMode, @@ -205,7 +249,7 @@ pub trait FragmentDisplayListBuilding { /// Builds the display items necessary to paint the selection and/or caret for this fragment, /// if any. fn build_display_items_for_selection_if_necessary(&self, - display_list: &mut DisplayList, + state: &mut DisplayListBuildState, stacking_relative_border_box: &Rect, display_list_section: DisplayListSection, clip: &ClippingRegion); @@ -215,7 +259,7 @@ pub trait FragmentDisplayListBuilding { /// /// `shadow_blur_radius` will be `Some` if this is a shadow, even if the blur radius is zero. fn build_display_list_for_text_fragment(&self, - display_list: &mut DisplayList, + state: &mut DisplayListBuildState, text_fragment: &ScannedTextFragmentInfo, text_color: RGBA, stacking_relative_content_box: &Rect, @@ -225,7 +269,7 @@ pub trait FragmentDisplayListBuilding { /// Creates the display item for a text decoration: underline, overline, or line-through. fn build_display_list_for_text_decoration(&self, - display_list: &mut DisplayList, + state: &mut DisplayListBuildState, color: &RGBA, stacking_relative_box: &LogicalRect, clip: &ClippingRegion, @@ -233,18 +277,17 @@ pub trait FragmentDisplayListBuilding { /// A helper method that `build_display_list` calls to create per-fragment-type display items. fn build_fragment_type_specific_display_items(&mut self, - display_list: &mut DisplayList, - layout_context: &LayoutContext, + state: &mut DisplayListBuildState, stacking_relative_border_box: &Rect, clip: &ClippingRegion); /// Creates a stacking context for associated fragment. fn create_stacking_context(&self, + id: StackingContextId, base_flow: &BaseFlow, - display_list: Box, scroll_policy: ScrollPolicy, mode: StackingContextCreationMode) - -> Arc; + -> StackingContext; } fn handle_overlapping_radii(size: &Size2D, radii: &BorderRadii) -> BorderRadii { @@ -291,9 +334,8 @@ fn build_border_radius(abs_bounds: &Rect, border_style: &Border) -> BorderRa impl FragmentDisplayListBuilding for Fragment { fn build_display_list_for_background_if_applicable(&self, + state: &mut DisplayListBuildState, style: &ComputedValues, - display_list: &mut DisplayList, - layout_context: &LayoutContext, display_list_section: DisplayListSection, absolute_bounds: &Rect, clip: &ClippingRegion) { @@ -332,14 +374,15 @@ impl FragmentDisplayListBuilding for Fragment { } } - display_list.add_to_section(DisplayItem::SolidColorClass(box SolidColorDisplayItem { - base: BaseDisplayItem::new(&bounds, - DisplayItemMetadata::new(self.node, - style, - Cursor::DefaultCursor), - &clip), - color: background_color.to_gfx_color(), - }), display_list_section); + state.add_display_item( + DisplayItem::SolidColorClass(box SolidColorDisplayItem { + base: BaseDisplayItem::new(&bounds, + DisplayItemMetadata::new(self.node, + style, + Cursor::DefaultCursor), + &clip), + color: background_color.to_gfx_color(), + }), display_list_section); // The background image is painted on top of the background color. // Implements background image, per spec: @@ -348,21 +391,20 @@ impl FragmentDisplayListBuilding for Fragment { match background.background_image.0 { None => {} Some(computed::Image::LinearGradient(ref gradient)) => { - self.build_display_list_for_background_linear_gradient(display_list, + self.build_display_list_for_background_linear_gradient(state, display_list_section, &bounds, &clip, gradient, - style) + style); } Some(computed::Image::Url(ref image_url)) => { - self.build_display_list_for_background_image(style, - display_list, - layout_context, + self.build_display_list_for_background_image(state, + style, display_list_section, &bounds, &clip, - image_url) + image_url); } } } @@ -421,15 +463,15 @@ impl FragmentDisplayListBuilding for Fragment { } fn build_display_list_for_background_image(&self, + state: &mut DisplayListBuildState, style: &ComputedValues, - display_list: &mut DisplayList, - layout_context: &LayoutContext, display_list_section: DisplayListSection, absolute_bounds: &Rect, clip: &ClippingRegion, image_url: &Url) { let background = style.get_background(); - let image = layout_context.get_or_request_image(image_url.clone(), UsePlaceholder::No); + let image = + state.layout_context.get_or_request_image(image_url.clone(), UsePlaceholder::No); if let Some(image) = image { debug!("(building display list) building background image"); @@ -519,7 +561,7 @@ impl FragmentDisplayListBuilding for Fragment { }; // Create the image display item. - display_list.add_to_section(DisplayItem::ImageClass(box ImageDisplayItem { + state.add_display_item(DisplayItem::ImageClass(box ImageDisplayItem { base: BaseDisplayItem::new(&bounds, DisplayItemMetadata::new(self.node, style, @@ -533,7 +575,7 @@ impl FragmentDisplayListBuilding for Fragment { } fn build_display_list_for_background_linear_gradient(&self, - display_list: &mut DisplayList, + state: &mut DisplayListBuildState, display_list_section: DisplayListSection, absolute_bounds: &Rect, clip: &ClippingRegion, @@ -648,13 +690,12 @@ impl FragmentDisplayListBuilding for Fragment { stops: stops, }); - display_list.add_to_section(gradient_display_item, display_list_section) + state.add_display_item(gradient_display_item, display_list_section); } fn build_display_list_for_box_shadow_if_applicable(&self, + state: &mut DisplayListBuildState, style: &ComputedValues, - list: &mut DisplayList, - _layout_context: &LayoutContext, display_list_section: DisplayListSection, absolute_bounds: &Rect, clip: &ClippingRegion) { @@ -667,7 +708,7 @@ impl FragmentDisplayListBuilding for Fragment { box_shadow.spread_radius); // TODO(pcwalton): Multiple border radii; elliptical border radii. - list.add_to_section(DisplayItem::BoxShadowClass(box BoxShadowDisplayItem { + state.add_display_item(DisplayItem::BoxShadowClass(box BoxShadowDisplayItem { base: BaseDisplayItem::new(&bounds, DisplayItemMetadata::new(self.node, style, @@ -692,9 +733,9 @@ impl FragmentDisplayListBuilding for Fragment { fn build_display_list_for_borders_if_applicable( &self, + state: &mut DisplayListBuildState, style: &ComputedValues, border_painting_mode: BorderPaintingMode, - display_list: &mut DisplayList, bounds: &Rect, display_list_section: DisplayListSection, clip: &ClippingRegion) { @@ -738,7 +779,7 @@ impl FragmentDisplayListBuilding for Fragment { } // Append the border to the display list. - display_list.add_to_section(DisplayItem::BorderClass(box BorderDisplayItem { + state.add_display_item(DisplayItem::BorderClass(box BorderDisplayItem { base: BaseDisplayItem::new(&bounds, DisplayItemMetadata::new(self.node, style, @@ -755,8 +796,8 @@ impl FragmentDisplayListBuilding for Fragment { } fn build_display_list_for_outline_if_applicable(&self, + state: &mut DisplayListBuildState, style: &ComputedValues, - display_list: &mut DisplayList, bounds: &Rect, clip: &ClippingRegion) { let width = style.get_outline().outline_width; @@ -780,7 +821,7 @@ impl FragmentDisplayListBuilding for Fragment { // Append the outline to the display list. let color = style.resolve_color(style.get_outline().outline_color).to_gfx_color(); - display_list.outlines.push_back(DisplayItem::BorderClass(box BorderDisplayItem { + state.add_display_item(DisplayItem::BorderClass(box BorderDisplayItem { base: BaseDisplayItem::new(&bounds, DisplayItemMetadata::new(self.node, style, @@ -790,12 +831,12 @@ impl FragmentDisplayListBuilding for Fragment { color: SideOffsets2D::new_all_same(color), style: SideOffsets2D::new_all_same(outline_style), radius: Default::default(), - })) + }), DisplayListSection::Outlines); } fn build_debug_borders_around_text_fragments(&self, + state: &mut DisplayListBuildState, style: &ComputedValues, - display_list: &mut DisplayList, stacking_relative_border_box: &Rect, stacking_relative_content_box: &Rect, text_fragment: &ScannedTextFragmentInfo, @@ -804,7 +845,7 @@ impl FragmentDisplayListBuilding for Fragment { let container_size = Size2D::zero(); // Compute the text fragment bounds and draw a border surrounding them. - display_list.content.push_back(DisplayItem::BorderClass(box BorderDisplayItem { + state.add_display_item(DisplayItem::BorderClass(box BorderDisplayItem { base: BaseDisplayItem::new(stacking_relative_border_box, DisplayItemMetadata::new(self.node, style, @@ -814,7 +855,7 @@ impl FragmentDisplayListBuilding for Fragment { color: SideOffsets2D::new_all_same(color::rgb(0, 0, 200)), style: SideOffsets2D::new_all_same(border_style::T::solid), radius: Default::default(), - })); + }), DisplayListSection::Content); // Draw a rectangle representing the baselines. let mut baseline = LogicalRect::from_physical(self.style.writing_mode, @@ -824,7 +865,7 @@ impl FragmentDisplayListBuilding for Fragment { baseline.size.block = Au(0); let baseline = baseline.to_physical(self.style.writing_mode, container_size); - let line_display_item = box LineDisplayItem { + state.add_display_item(DisplayItem::LineClass(box LineDisplayItem { base: BaseDisplayItem::new(&baseline, DisplayItemMetadata::new(self.node, style, @@ -832,16 +873,15 @@ impl FragmentDisplayListBuilding for Fragment { clip), color: color::rgb(0, 200, 0), style: border_style::T::dashed, - }; - display_list.content.push_back(DisplayItem::LineClass(line_display_item)); + }), DisplayListSection::Content); } fn build_debug_borders_around_fragment(&self, - display_list: &mut DisplayList, + state: &mut DisplayListBuildState, stacking_relative_border_box: &Rect, clip: &ClippingRegion) { // This prints a debug border around the border of this fragment. - display_list.content.push_back(DisplayItem::BorderClass(box BorderDisplayItem { + state.add_display_item(DisplayItem::BorderClass(box BorderDisplayItem { base: BaseDisplayItem::new(stacking_relative_border_box, DisplayItemMetadata::new(self.node, &*self.style, @@ -851,7 +891,7 @@ impl FragmentDisplayListBuilding for Fragment { color: SideOffsets2D::new_all_same(color::rgb(0, 0, 200)), style: SideOffsets2D::new_all_same(border_style::T::solid), radius: Default::default(), - })); + }), DisplayListSection::Content); } fn calculate_style_specified_clip(&self, @@ -875,7 +915,7 @@ impl FragmentDisplayListBuilding for Fragment { } fn build_display_items_for_selection_if_necessary(&self, - display_list: &mut DisplayList, + state: &mut DisplayListBuildState, stacking_relative_border_box: &Rect, display_list_section: DisplayListSection, clip: &ClippingRegion) { @@ -910,7 +950,7 @@ impl FragmentDisplayListBuilding for Fragment { cursor = Cursor::VerticalTextCursor; }; - display_list.add_to_section(DisplayItem::SolidColorClass(box SolidColorDisplayItem { + state.add_display_item(DisplayItem::SolidColorClass(box SolidColorDisplayItem { base: BaseDisplayItem::new(&insertion_point_bounds, DisplayItemMetadata::new(self.node, &*self.style, cursor), &clip), @@ -919,8 +959,7 @@ impl FragmentDisplayListBuilding for Fragment { } fn build_display_list(&mut self, - display_list: &mut DisplayList, - layout_context: &LayoutContext, + state: &mut DisplayListBuildState, stacking_relative_flow_origin: &Point2D, relative_containing_block_size: &LogicalSize, relative_containing_block_mode: WritingMode, @@ -969,16 +1008,14 @@ impl FragmentDisplayListBuilding for Fragment { if let Some(ref inline_context) = self.inline_context { for node in inline_context.nodes.iter().rev() { self.build_display_list_for_background_if_applicable( + state, &*node.style, - display_list, - layout_context, display_list_section, &stacking_relative_border_box, &clip); self.build_display_list_for_box_shadow_if_applicable( + state, &*node.style, - display_list, - layout_context, display_list_section, &stacking_relative_border_box, &clip); @@ -989,69 +1026,65 @@ impl FragmentDisplayListBuilding for Fragment { node.flags.contains(FIRST_FRAGMENT_OF_ELEMENT), node.flags.contains(LAST_FRAGMENT_OF_ELEMENT)); self.build_display_list_for_borders_if_applicable( + state, &*style, border_painting_mode, - display_list, &stacking_relative_border_box, display_list_section, &clip); self.build_display_list_for_outline_if_applicable( + state, &*node.style, - display_list, &stacking_relative_border_box, &clip); } } if !self.is_scanned_text_fragment() { - self.build_display_list_for_background_if_applicable(&*self.style, - display_list, - layout_context, + self.build_display_list_for_background_if_applicable(state, + &*self.style, display_list_section, &stacking_relative_border_box, &clip); - self.build_display_list_for_box_shadow_if_applicable(&*self.style, - display_list, - layout_context, + self.build_display_list_for_box_shadow_if_applicable(state, + &*self.style, display_list_section, &stacking_relative_border_box, &clip); - self.build_display_list_for_borders_if_applicable(&*self.style, + self.build_display_list_for_borders_if_applicable(state, + &*self.style, border_painting_mode, - display_list, &stacking_relative_border_box, display_list_section, &clip); - self.build_display_list_for_outline_if_applicable(&*self.style, - display_list, + self.build_display_list_for_outline_if_applicable(state, + &*self.style, &stacking_relative_border_box, &clip); } // Paint the selection point if necessary. - self.build_display_items_for_selection_if_necessary(display_list, + self.build_display_items_for_selection_if_necessary(state, &stacking_relative_border_box, display_list_section, &clip); } // Create special per-fragment-type display items. - self.build_fragment_type_specific_display_items(display_list, - layout_context, + self.build_fragment_type_specific_display_items(state, &stacking_relative_border_box, &clip); if opts::get().show_debug_fragment_borders { - self.build_debug_borders_around_fragment(display_list, + self.build_debug_borders_around_fragment(state, &stacking_relative_border_box, - &clip) + &clip); } } fn build_fragment_type_specific_display_items(&mut self, - display_list: &mut DisplayList, - layout_context: &LayoutContext, + state: &mut DisplayListBuildState, stacking_relative_border_box: &Rect, clip: &ClippingRegion) { // Compute the context box position relative to the parent stacking context. @@ -1068,7 +1101,7 @@ impl FragmentDisplayListBuilding for Fragment { for text_shadow in self.style.get_effects().text_shadow.0.iter().rev() { let offset = &Point2D::new(text_shadow.offset_x, text_shadow.offset_y); let color = self.style().resolve_color(text_shadow.color); - self.build_display_list_for_text_fragment(display_list, + self.build_display_list_for_text_fragment(state, &**text_fragment, color, &stacking_relative_content_box, @@ -1078,7 +1111,7 @@ impl FragmentDisplayListBuilding for Fragment { } // Create the main text display item. - self.build_display_list_for_text_fragment(display_list, + self.build_display_list_for_text_fragment(state, &**text_fragment, text_color, &stacking_relative_content_box, @@ -1087,12 +1120,12 @@ impl FragmentDisplayListBuilding for Fragment { clip); if opts::get().show_debug_fragment_borders { - self.build_debug_borders_around_text_fragments(self.style(), - display_list, + self.build_debug_borders_around_text_fragments(state, + self.style(), stacking_relative_border_box, &stacking_relative_content_box, &**text_fragment, - clip) + clip); } } SpecificFragmentInfo::Generic | @@ -1107,7 +1140,7 @@ impl FragmentDisplayListBuilding for Fragment { SpecificFragmentInfo::InlineAbsoluteHypothetical(_) | SpecificFragmentInfo::InlineAbsolute(_) => { if opts::get().show_debug_fragment_borders { - self.build_debug_borders_around_fragment(display_list, + self.build_debug_borders_around_fragment(state, stacking_relative_border_box, clip); } @@ -1115,36 +1148,38 @@ impl FragmentDisplayListBuilding for Fragment { SpecificFragmentInfo::Iframe(ref fragment_info) => { if !stacking_relative_content_box.is_empty() { if opts::get().use_webrender { - display_list.content.push_back(DisplayItem::IframeClass(box IframeDisplayItem { - base: BaseDisplayItem::new(&stacking_relative_content_box, - DisplayItemMetadata::new(self.node, - &*self.style, - Cursor::DefaultCursor), - clip), + state.add_display_item(DisplayItem::IframeClass(box IframeDisplayItem { + base: BaseDisplayItem::new( + &stacking_relative_content_box, + DisplayItemMetadata::new(self.node, + &*self.style, + Cursor::DefaultCursor), + clip), iframe: fragment_info.pipeline_id, - })); + }), DisplayListSection::Content); } else { let layer_id = self.layer_id(); - display_list.content.push_back(DisplayItem::LayeredItemClass(box LayeredItem { + state.add_display_item(DisplayItem::LayeredItemClass(box LayeredItem { item: DisplayItem::NoopClass( - box BaseDisplayItem::new(&stacking_relative_content_box, - DisplayItemMetadata::new(self.node, - &*self.style, - Cursor::DefaultCursor), - clip)), - layer_id: layer_id - })); - - display_list.layer_info.push_back(LayerInfo::new(layer_id, - ScrollPolicy::Scrollable, - Some(fragment_info.pipeline_id))); + box BaseDisplayItem::new( + &stacking_relative_content_box, + DisplayItemMetadata::new(self.node, + &*self.style, + Cursor::DefaultCursor), + clip)), + layer_id: layer_id, + layer_info: LayerInfo::new(layer_id, + ScrollPolicy::Scrollable, + Some(fragment_info.pipeline_id), + color::transparent()), + }), DisplayListSection::Content); } } } SpecificFragmentInfo::Image(ref mut image_fragment) => { // Place the image into the display list. if let Some(ref image) = image_fragment.image { - display_list.content.push_back(DisplayItem::ImageClass(box ImageDisplayItem { + state.add_display_item(DisplayItem::ImageClass(box ImageDisplayItem { base: BaseDisplayItem::new(&stacking_relative_content_box, DisplayItemMetadata::new(self.node, &*self.style, @@ -1153,7 +1188,7 @@ impl FragmentDisplayListBuilding for Fragment { image: image.clone(), stretch_size: stacking_relative_content_box.size, image_rendering: self.style.get_effects().image_rendering.clone(), - })); + }), DisplayListSection::Content); } } SpecificFragmentInfo::Canvas(ref canvas_fragment_info) => { @@ -1171,8 +1206,8 @@ impl FragmentDisplayListBuilding for Fragment { FromLayoutMsg::SendData(sender))).unwrap(); let data = receiver.recv().unwrap(); - // Propagate the layer and the renderer to the paint thread. - layout_context.shared.canvas_layers_sender.lock().unwrap().send( + // Propagate the layer and the renderer to the paint task. + state.layout_context.shared.canvas_layers_sender.lock().unwrap().send( (layer_id, (*ipc_renderer).clone())).unwrap(); data @@ -1215,15 +1250,16 @@ impl FragmentDisplayListBuilding for Fragment { }; if opts::get().use_webrender { - display_list.content.push_back(display_item); + state.add_display_item(display_item, DisplayListSection::Content); } else { - display_list.content.push_back(DisplayItem::LayeredItemClass(box LayeredItem { + state.add_display_item(DisplayItem::LayeredItemClass(box LayeredItem { item: display_item, - layer_id: layer_id - })); - - display_list.layer_info.push_back( - LayerInfo::new(layer_id, ScrollPolicy::Scrollable, None)); + layer_id: layer_id, + layer_info: LayerInfo::new(layer_id, + ScrollPolicy::Scrollable, + None, + color::transparent()), + }), DisplayListSection::Content); } } } @@ -1237,14 +1273,16 @@ impl FragmentDisplayListBuilding for Fragment { } fn create_stacking_context(&self, + id: StackingContextId, base_flow: &BaseFlow, - display_list: Box, scroll_policy: ScrollPolicy, mode: StackingContextCreationMode) - -> Arc { + -> StackingContext { let border_box = match mode { - StackingContextCreationMode::Normal | - StackingContextCreationMode::OuterScrollWrapper => { + StackingContextCreationMode::InnerScrollWrapper => { + Rect::new(Point2D::zero(), base_flow.overflow.scroll.size) + } + _ => { self.stacking_relative_border_box(&base_flow.stacking_relative_position, &base_flow.early_absolute_position_info .relative_containing_block_size, @@ -1252,12 +1290,13 @@ impl FragmentDisplayListBuilding for Fragment { .relative_containing_block_mode, CoordinateSystem::Parent) } - StackingContextCreationMode::InnerScrollWrapper => { - Rect::new(Point2D::zero(), base_flow.overflow.scroll.size) - } }; let overflow = match mode { - StackingContextCreationMode::Normal => { + StackingContextCreationMode::InnerScrollWrapper | + StackingContextCreationMode::OuterScrollWrapper => { + Rect::new(Point2D::zero(), border_box.size) + } + _ => { // First, compute the offset of our border box (including relative positioning) // from our flow origin, since that is what `BaseFlow::overflow` is relative to. let border_box_offset = @@ -1265,10 +1304,6 @@ impl FragmentDisplayListBuilding for Fragment { // Then, using that, compute our overflow region relative to our border box. base_flow.overflow.paint.translate(&-border_box_offset) } - StackingContextCreationMode::InnerScrollWrapper | - StackingContextCreationMode::OuterScrollWrapper => { - Rect::new(Point2D::zero(), border_box.size) - } }; let mut transform = Matrix4::identity(); @@ -1355,9 +1390,12 @@ impl FragmentDisplayListBuilding for Fragment { // There are two situations that need layers: when the fragment has the HAS_LAYER // flag and when we are building a layer tree for overflow scrolling. let layer_info = if mode == StackingContextCreationMode::InnerScrollWrapper { - Some(LayerInfo::new(self.layer_id_for_overflow_scroll(), scroll_policy, None)) + Some(LayerInfo::new(self.layer_id_for_overflow_scroll(), + scroll_policy, + None, + color::transparent())) } else if self.flags.contains(HAS_LAYER) { - Some(LayerInfo::new(self.layer_id(), scroll_policy, None)) + Some(LayerInfo::new(self.layer_id(), scroll_policy, None, color::transparent())) } else { None }; @@ -1367,17 +1405,24 @@ impl FragmentDisplayListBuilding for Fragment { let establishes_3d_context = scrolls_overflow_area || transform_style == transform_style::T::flat; - Arc::new(StackingContext::new(display_list, - &border_box, - &overflow, - self.effective_z_index(), - filters, - self.style().get_effects().mix_blend_mode, - transform, - perspective, - establishes_3d_context, - scrolls_overflow_area, - layer_info)) + let context_type = match mode { + StackingContextCreationMode::PseudoFloat => StackingContextType::PseudoFloat, + StackingContextCreationMode::PseudoPositioned => StackingContextType::PseudoPositioned, + _ => StackingContextType::Real, + }; + + StackingContext::new(id, + context_type, + &border_box, + &overflow, + self.effective_z_index(), + filters, + self.style().get_effects().mix_blend_mode, + transform, + perspective, + establishes_3d_context, + scrolls_overflow_area, + layer_info) } fn clipping_region_for_children(&self, @@ -1429,7 +1474,7 @@ impl FragmentDisplayListBuilding for Fragment { } fn build_display_list_for_text_fragment(&self, - display_list: &mut DisplayList, + state: &mut DisplayListBuildState, text_fragment: &ScannedTextFragmentInfo, text_color: RGBA, stacking_relative_content_box: &Rect, @@ -1460,7 +1505,7 @@ impl FragmentDisplayListBuilding for Fragment { container_size); // Create the text display item. - display_list.content.push_back(DisplayItem::TextClass(box TextDisplayItem { + state.add_display_item(DisplayItem::TextClass(box TextDisplayItem { base: BaseDisplayItem::new(&stacking_relative_content_box, DisplayItemMetadata::new(self.node, self.style(), cursor), clip), @@ -1470,7 +1515,7 @@ impl FragmentDisplayListBuilding for Fragment { orientation: orientation, baseline_origin: baseline_origin, blur_radius: shadow_blur_radius.unwrap_or(Au(0)), - })); + }), DisplayListSection::Content); // Create display items for text decorations. let mut text_decorations = self.style() @@ -1492,21 +1537,21 @@ impl FragmentDisplayListBuilding for Fragment { stacking_relative_box.start.b = stacking_relative_content_box.start.b + metrics.ascent - metrics.underline_offset; stacking_relative_box.size.block = metrics.underline_size; - self.build_display_list_for_text_decoration(display_list, + self.build_display_list_for_text_decoration(state, underline_color, &stacking_relative_box, clip, - shadow_blur_radius.unwrap_or(Au(0))) + shadow_blur_radius.unwrap_or(Au(0))); } if let Some(ref overline_color) = text_decorations.overline { let mut stacking_relative_box = stacking_relative_content_box; stacking_relative_box.size.block = metrics.underline_size; - self.build_display_list_for_text_decoration(display_list, + self.build_display_list_for_text_decoration(state, overline_color, &stacking_relative_box, clip, - shadow_blur_radius.unwrap_or(Au(0))) + shadow_blur_radius.unwrap_or(Au(0))); } if let Some(ref line_through_color) = text_decorations.line_through { @@ -1514,16 +1559,16 @@ impl FragmentDisplayListBuilding for Fragment { stacking_relative_box.start.b = stacking_relative_box.start.b + metrics.ascent - metrics.strikeout_offset; stacking_relative_box.size.block = metrics.strikeout_size; - self.build_display_list_for_text_decoration(display_list, + self.build_display_list_for_text_decoration(state, line_through_color, &stacking_relative_box, clip, - shadow_blur_radius.unwrap_or(Au(0))) + shadow_blur_radius.unwrap_or(Au(0))); } } fn build_display_list_for_text_decoration(&self, - display_list: &mut DisplayList, + state: &mut DisplayListBuildState, color: &RGBA, stacking_relative_box: &LogicalRect, clip: &ClippingRegion, @@ -1537,7 +1582,7 @@ impl FragmentDisplayListBuilding for Fragment { let stacking_relative_box = stacking_relative_box.to_physical(self.style.writing_mode, container_size); let metadata = DisplayItemMetadata::new(self.node, &*self.style, Cursor::DefaultCursor); - display_list.content.push_back(DisplayItem::BoxShadowClass(box BoxShadowDisplayItem { + state.add_display_item(DisplayItem::BoxShadowClass(box BoxShadowDisplayItem { base: BaseDisplayItem::new(&shadow_bounds(&stacking_relative_box, blur_radius, Au(0)), metadata, clip), @@ -1548,51 +1593,131 @@ impl FragmentDisplayListBuilding for Fragment { spread_radius: Au(0), border_radius: Au(0), clip_mode: BoxShadowClipMode::None, - })) + }), DisplayListSection::Content); } } pub trait BlockFlowDisplayListBuilding { - fn build_display_list_for_block_base(&mut self, - display_list: &mut DisplayList, - layout_context: &LayoutContext, - border_painting_mode: BorderPaintingMode, - background_border_level: DisplayListSection); - fn build_display_list_for_static_block(&mut self, - display_list: Box, - layout_context: &LayoutContext, - border_painting_mode: BorderPaintingMode, - background_border_level: DisplayListSection); - fn build_display_list_for_absolutely_positioned_block( - &mut self, - display_list: Box, - layout_context: &LayoutContext, - border_painting_mode: BorderPaintingMode); - fn build_display_list_for_floating_block(&mut self, - display_list: Box, - layout_context: &LayoutContext, - border_painting_mode: BorderPaintingMode); + fn collect_stacking_contexts_for_block(&mut self, + parent_id: StackingContextId, + contexts: &mut Vec) + -> StackingContextId; fn build_display_list_for_block(&mut self, - display_list: Box, - layout_context: &LayoutContext, + state: &mut DisplayListBuildState, border_painting_mode: BorderPaintingMode); } impl BlockFlowDisplayListBuilding for BlockFlow { - fn build_display_list_for_block_base(&mut self, - display_list: &mut DisplayList, - layout_context: &LayoutContext, - border_painting_mode: BorderPaintingMode, - background_border_level: DisplayListSection) { + fn collect_stacking_contexts_for_block(&mut self, + parent_id: StackingContextId, + contexts: &mut Vec) + -> StackingContextId { + if !self.fragment.establishes_stacking_context() && + !self.establishes_pseudo_stacking_context() { + self.base.stacking_context_id = parent_id; + self.base.collect_stacking_contexts_for_children(parent_id, contexts); + return parent_id; + } + + let stacking_context_id = + StackingContextId::new_of_type(self.fragment.node.id() as usize, + self.fragment.fragment_type()); + self.base.stacking_context_id = stacking_context_id; + + let inner_stacking_context_id = if self.has_scrolling_overflow() { + StackingContextId::new_of_type(self.base.flow_id(), + self.fragment.fragment_type()) + } else { + stacking_context_id + }; + + let mut child_contexts = Vec::new(); + self.base.collect_stacking_contexts_for_children(inner_stacking_context_id, + &mut child_contexts); + + if self.establishes_pseudo_stacking_context() { + let creation_mode = if self.base.flags.contains(IS_ABSOLUTELY_POSITIONED) || + self.fragment.style.get_box().position != position::T::static_ { + StackingContextCreationMode::PseudoPositioned + } else { + assert!(self.base.flags.is_float()); + StackingContextCreationMode::PseudoFloat + }; + + let mut stacking_context = + self.fragment.create_stacking_context(stacking_context_id, + &self.base, + ScrollPolicy::Scrollable, + creation_mode); + let (mut floating, mut positioned) = child_contexts.into_iter().partition(|context| { + context.context_type == StackingContextType::PseudoFloat + }); + + stacking_context.children.append(&mut floating); + contexts.push(stacking_context); + contexts.append(&mut positioned); + return stacking_context_id; + } + + let scroll_policy = if self.is_fixed() { + ScrollPolicy::FixedPosition + } else { + ScrollPolicy::Scrollable + }; + + let stacking_context = if self.has_scrolling_overflow() { + let mut inner_stacking_context = self.fragment.create_stacking_context( + inner_stacking_context_id, + &self.base, + scroll_policy, + StackingContextCreationMode::InnerScrollWrapper); + inner_stacking_context.children.append(&mut child_contexts); + + let mut outer_stacking_context = self.fragment.create_stacking_context( + stacking_context_id, + &self.base, + scroll_policy, + StackingContextCreationMode::OuterScrollWrapper); + outer_stacking_context.children.push(inner_stacking_context); + outer_stacking_context + } else { + let mut stacking_context = self.fragment.create_stacking_context( + stacking_context_id, + &self.base, + scroll_policy, + StackingContextCreationMode::Normal); + stacking_context.children.append(&mut child_contexts); + stacking_context + }; + + contexts.push(stacking_context); + stacking_context_id + } + + fn build_display_list_for_block(&mut self, + state: &mut DisplayListBuildState, + border_painting_mode: BorderPaintingMode) { + let background_border_section = if self.base.flags.is_float() { + DisplayListSection::BackgroundAndBorders + } else if self.base.flags.contains(IS_ABSOLUTELY_POSITIONED) { + if self.fragment.establishes_stacking_context() { + DisplayListSection::BackgroundAndBorders + } else { + DisplayListSection::BlockBackgroundsAndBorders + } + } else { + DisplayListSection::BlockBackgroundsAndBorders + }; + // Add the box that starts the block context. let clip = if self.fragment.establishes_stacking_context() { self.base.clip.translate(&-self.base.stacking_relative_position) } else { self.base.clip.clone() }; + self.fragment - .build_display_list(display_list, - layout_context, + .build_display_list(state, &self.base.stacking_relative_position, &self.base .early_absolute_position_info @@ -1601,201 +1726,68 @@ impl BlockFlowDisplayListBuilding for BlockFlow { .early_absolute_position_info .relative_containing_block_mode, border_painting_mode, - background_border_level, + background_border_section, &clip, &self.base.stacking_relative_position_of_display_port); // Add children. for kid in self.base.children.iter_mut() { - display_list.append_from(&mut flow::mut_base(kid).display_list_building_result); + state.append_from(&mut flow::mut_base(kid).display_list_building_result); } - self.base.build_display_items_for_debugging_tint(display_list, self.fragment.node); - } - - fn build_display_list_for_static_block(&mut self, - mut display_list: Box, - layout_context: &LayoutContext, - border_painting_mode: BorderPaintingMode, - background_border_level: DisplayListSection) { - self.build_display_list_for_block_base(&mut *display_list, - layout_context, - border_painting_mode, - background_border_level); - - self.base.display_list_building_result = if self.fragment.establishes_stacking_context() { - let scroll_policy = if self.is_fixed() { - ScrollPolicy::FixedPosition - } else { - ScrollPolicy::Scrollable - }; - - Some(DisplayList::new_with_stacking_context( - self.fragment.create_stacking_context(&self.base, - display_list, - scroll_policy, - StackingContextCreationMode::Normal))) - } else { - if self.fragment.style.get_box().position != position::T::static_ { - display_list.form_pseudo_stacking_context_for_positioned_content(); - } - Some(display_list) - } - } - - fn build_display_list_for_absolutely_positioned_block( - &mut self, - mut display_list: Box, - layout_context: &LayoutContext, - border_painting_mode: BorderPaintingMode) { - // If `overflow: scroll` is in effect, we add this fragment's display items to a new - // stacking context. - let outer_display_list_for_overflow_scroll = - match (self.fragment.style().get_box().overflow_x, - self.fragment.style().get_box().overflow_y.0) { - (overflow_x::T::auto, _) | - (overflow_x::T::scroll, _) | - (_, overflow_x::T::auto) | - (_, overflow_x::T::scroll) => { - // Create a separate display list for our own fragment. - let mut outer_display_list_for_overflow_scroll = box DisplayList::new(); - let clip = self.base.clip.translate(&-self.base.stacking_relative_position); - self.fragment.build_display_list( - &mut outer_display_list_for_overflow_scroll, - layout_context, - &self.base.stacking_relative_position, - &self.base.early_absolute_position_info.relative_containing_block_size, - self.base.early_absolute_position_info.relative_containing_block_mode, - border_painting_mode, - DisplayListSection::BackgroundAndBorders, - &clip, - &self.base.stacking_relative_position_of_display_port); - - // Add the fragments of our children to the display list we'll use for the inner - // stacking context. - for kid in self.base.children.iter_mut() { - display_list.append_from(&mut flow::mut_base(kid).display_list_building_result); - } - - Some(outer_display_list_for_overflow_scroll) - } - _ => { - let display_list_section = if self.fragment.establishes_stacking_context() { - DisplayListSection::BackgroundAndBorders - } else { - DisplayListSection::BlockBackgroundsAndBorders - }; - - self.build_display_list_for_block_base(&mut *display_list, - layout_context, - border_painting_mode, - display_list_section); - None - } - }; - - if !self.fragment.flags.contains(HAS_LAYER) && !self.fragment.establishes_stacking_context() { - display_list.form_pseudo_stacking_context_for_positioned_content(); - self.base.display_list_building_result = Some(display_list); - return; - } - - // If we got here, then we need a new layer. - let scroll_policy = if self.is_fixed() { - ScrollPolicy::FixedPosition - } else { - ScrollPolicy::Scrollable - }; - - let stacking_context = match outer_display_list_for_overflow_scroll { - Some(mut outer_display_list) => { - outer_display_list.positioned_content.push_back( - DisplayItem::StackingContextClass(self.fragment.create_stacking_context( - &self.base, - display_list, - scroll_policy, - StackingContextCreationMode::InnerScrollWrapper))); - self.fragment.create_stacking_context( - &self.base, - outer_display_list, - scroll_policy, - StackingContextCreationMode::OuterScrollWrapper) - } - None => { - self.fragment.create_stacking_context( - &self.base, - display_list, - scroll_policy, - StackingContextCreationMode::Normal) - } - }; - - self.base.display_list_building_result = - Some(DisplayList::new_with_stacking_context(stacking_context)); - } - - fn build_display_list_for_floating_block(&mut self, - mut display_list: Box, - layout_context: &LayoutContext, - border_painting_mode: BorderPaintingMode) { - self.build_display_list_for_block_base(&mut *display_list, - layout_context, - border_painting_mode, - DisplayListSection::BackgroundAndBorders); - display_list.form_float_pseudo_stacking_context(); - - self.base.display_list_building_result = if self.fragment.establishes_stacking_context() { - Some(DisplayList::new_with_stacking_context( - self.fragment.create_stacking_context(&self.base, - display_list, - ScrollPolicy::Scrollable, - StackingContextCreationMode::Normal))) - } else { - if self.fragment.style.get_box().position != position::T::static_ { - display_list.form_pseudo_stacking_context_for_positioned_content(); - } - Some(display_list) - } - } - - fn build_display_list_for_block(&mut self, - display_list: Box, - layout_context: &LayoutContext, - border_painting_mode: BorderPaintingMode) { - if self.base.flags.is_float() { - self.build_display_list_for_floating_block(display_list, - layout_context, - border_painting_mode); - } else if self.base.flags.contains(IS_ABSOLUTELY_POSITIONED) { - self.build_display_list_for_absolutely_positioned_block(display_list, - layout_context, - border_painting_mode); - } else { - self.build_display_list_for_static_block( - display_list, - layout_context, - border_painting_mode, - DisplayListSection::BlockBackgroundsAndBorders); - } + self.base.build_display_items_for_debugging_tint(state, self.fragment.node); } } pub trait InlineFlowDisplayListBuilding { + fn collect_stacking_contexts_for_inline(&mut self, + parent_id: StackingContextId, + contexts: &mut Vec) + -> StackingContextId; fn build_display_list_for_inline_fragment_at_index(&mut self, - index: usize, - display_list: &mut DisplayList, - layout_context: &LayoutContext); - fn build_display_list_for_inline(&mut self, layout_context: &LayoutContext); + state: &mut DisplayListBuildState, + index: usize); + fn build_display_list_for_inline(&mut self, state: &mut DisplayListBuildState); } impl InlineFlowDisplayListBuilding for InlineFlow { + fn collect_stacking_contexts_for_inline(&mut self, + parent_id: StackingContextId, + contexts: &mut Vec) + -> StackingContextId { + self.base.stacking_context_id = parent_id; + + for mut fragment in self.fragments.fragments.iter_mut() { + match fragment.specific { + SpecificFragmentInfo::InlineBlock(ref mut block_flow) => { + let block_flow = flow_ref::deref_mut(&mut block_flow.flow_ref); + block_flow.collect_stacking_contexts(parent_id, contexts); + } + SpecificFragmentInfo::InlineAbsoluteHypothetical(ref mut block_flow) => { + let block_flow = flow_ref::deref_mut(&mut block_flow.flow_ref); + block_flow.collect_stacking_contexts(parent_id, contexts); + } + _ if fragment.establishes_stacking_context() => { + fragment.stacking_context_id = + StackingContextId::new_of_type(fragment.fragment_id(), + fragment.fragment_type()); + contexts.push(fragment.create_stacking_context( + fragment.stacking_context_id, + &self.base, + ScrollPolicy::Scrollable, + StackingContextCreationMode::Normal)); + } + _ => fragment.stacking_context_id = parent_id, + } + } + parent_id + } + fn build_display_list_for_inline_fragment_at_index(&mut self, - index: usize, - display_list: &mut DisplayList, - layout_context: &LayoutContext) { + state: &mut DisplayListBuildState, + index: usize) { let fragment = self.fragments.fragments.get_mut(index).unwrap(); - fragment.build_display_list(display_list, - layout_context, + fragment.build_display_list(state, &self.base.stacking_relative_position, &self.base .early_absolute_position_info @@ -1811,69 +1803,51 @@ impl InlineFlowDisplayListBuilding for InlineFlow { match fragment.specific { SpecificFragmentInfo::InlineBlock(ref mut block_flow) => { let block_flow = flow_ref::deref_mut(&mut block_flow.flow_ref); - display_list.append_from( - &mut flow::mut_base(block_flow).display_list_building_result) + state.append_from(&mut flow::mut_base(block_flow).display_list_building_result) } SpecificFragmentInfo::InlineAbsoluteHypothetical(ref mut block_flow) => { let block_flow = flow_ref::deref_mut(&mut block_flow.flow_ref); - display_list.append_from( - &mut flow::mut_base(block_flow).display_list_building_result) + state.append_from(&mut flow::mut_base(block_flow).display_list_building_result) } SpecificFragmentInfo::InlineAbsolute(ref mut block_flow) => { let block_flow = flow_ref::deref_mut(&mut block_flow.flow_ref); - display_list.append_from( - &mut flow::mut_base(block_flow).display_list_building_result) + state.append_from(&mut flow::mut_base(block_flow).display_list_building_result) } _ => {} } } - fn build_display_list_for_inline(&mut self, layout_context: &LayoutContext) { + fn build_display_list_for_inline(&mut self, state: &mut DisplayListBuildState) { // TODO(#228): Once we form lines and have their cached bounds, we can be smarter and // not recurse on a line if nothing in it can intersect the dirty region. debug!("Flow: building display list for {} inline fragments", self.fragments.len()); - let mut display_list = box DisplayList::new(); - // We iterate using an index here, because we want to avoid doing a doing // a double-borrow of self (one mutable for the method call and one immutable // for the self.fragments.fragment iterator itself). for index in 0..self.fragments.fragments.len() { - let establishes_stacking_context = { + let (establishes_stacking_context, stacking_context_id) = { let fragment = self.fragments.fragments.get(index).unwrap(); - match fragment.specific { - SpecificFragmentInfo::InlineBlock(_) | - SpecificFragmentInfo::InlineAbsoluteHypothetical(_) => false, - _ => fragment.establishes_stacking_context(), - } + (self.base.stacking_context_id != fragment.stacking_context_id, + fragment.stacking_context_id) }; if establishes_stacking_context { - let mut fragment_display_list = box DisplayList::new(); - self.build_display_list_for_inline_fragment_at_index(index, - &mut *fragment_display_list, - layout_context); - let fragment = self.fragments.fragments.get(index).unwrap(); - display_list.positioned_content.push_back(DisplayItem::StackingContextClass( - fragment.create_stacking_context( - &self.base, - fragment_display_list, - ScrollPolicy::Scrollable, - StackingContextCreationMode::Normal))); - } else { - self.build_display_list_for_inline_fragment_at_index(index, - &mut *display_list, - layout_context); + state.push_stacking_context_id(stacking_context_id); + } + + self.build_display_list_for_inline_fragment_at_index(state, index); + + if establishes_stacking_context { + state.pop_stacking_context_id(); } } if !self.fragments.fragments.is_empty() { - self.base.build_display_items_for_debugging_tint(&mut *display_list, + self.base.build_display_items_for_debugging_tint(state, self.fragments.fragments[0].node); } - self.base.display_list_building_result = Some(display_list); - if opts::get().validate_display_list_geometry { self.base.validate_display_list_geometry(); } @@ -1881,19 +1855,14 @@ impl InlineFlowDisplayListBuilding for InlineFlow { } pub trait ListItemFlowDisplayListBuilding { - fn build_display_list_for_list_item(&mut self, - display_list: Box, - layout_context: &LayoutContext); + fn build_display_list_for_list_item(&mut self, state: &mut DisplayListBuildState); } impl ListItemFlowDisplayListBuilding for ListItemFlow { - fn build_display_list_for_list_item(&mut self, - mut display_list: Box, - layout_context: &LayoutContext) { + fn build_display_list_for_list_item(&mut self, state: &mut DisplayListBuildState) { // Draw the marker, if applicable. for marker in &mut self.marker_fragments { - marker.build_display_list(&mut *display_list, - layout_context, + marker.build_display_list(state, &self.block_flow.base.stacking_relative_position, &self.block_flow .base @@ -1912,38 +1881,30 @@ impl ListItemFlowDisplayListBuilding for ListItemFlow { } // Draw the rest of the block. - self.block_flow.build_display_list_for_block(display_list, - layout_context, - BorderPaintingMode::Separate) + self.block_flow.build_display_list_for_block(state, BorderPaintingMode::Separate) } } pub trait FlexFlowDisplayListBuilding { - fn build_display_list_for_flex(&mut self, - display_list: Box, - layout_context: &LayoutContext); + fn build_display_list_for_flex(&mut self, state: &mut DisplayListBuildState); } impl FlexFlowDisplayListBuilding for FlexFlow { - fn build_display_list_for_flex(&mut self, - display_list: Box, - layout_context: &LayoutContext) { + fn build_display_list_for_flex(&mut self, state: &mut DisplayListBuildState) { // Draw the rest of the block. - self.as_mut_block().build_display_list_for_block(display_list, - layout_context, - BorderPaintingMode::Separate) + self.as_mut_block().build_display_list_for_block(state, BorderPaintingMode::Separate) } } trait BaseFlowDisplayListBuilding { fn build_display_items_for_debugging_tint(&self, - display_list: &mut DisplayList, + state: &mut DisplayListBuildState, node: OpaqueNode); } impl BaseFlowDisplayListBuilding for BaseFlow { fn build_display_items_for_debugging_tint(&self, - display_list: &mut DisplayList, + state: &mut DisplayListBuildState, node: OpaqueNode) { if !opts::get().show_debug_parallel_layout { return @@ -1956,9 +1917,9 @@ impl BaseFlowDisplayListBuilding for BaseFlow { let mut color = THREAD_TINT_COLORS[thread_id as usize % THREAD_TINT_COLORS.len()]; color.a = 1.0; - display_list.add_to_section(DisplayItem::BorderClass(box BorderDisplayItem { + state.add_display_item(DisplayItem::BorderClass(box BorderDisplayItem { base: BaseDisplayItem::new(&stacking_context_relative_bounds.inflate(Au::from_px(2), - Au::from_px(2)), + Au::from_px(2)), DisplayItemMetadata { node: node, pointing: None, @@ -2027,4 +1988,6 @@ pub enum StackingContextCreationMode { Normal, OuterScrollWrapper, InnerScrollWrapper, + PseudoPositioned, + PseudoFloat, } diff --git a/servo/components/layout/flex.rs b/servo/components/layout/flex.rs index cbb685d046ea..c4e08ed6ffac 100644 --- a/servo/components/layout/flex.rs +++ b/servo/components/layout/flex.rs @@ -9,7 +9,7 @@ use app_units::Au; use block::BlockFlow; use context::LayoutContext; -use display_list_builder::FlexFlowDisplayListBuilding; +use display_list_builder::{DisplayListBuildState, FlexFlowDisplayListBuilding}; use euclid::Point2D; use floats::FloatKind; use flow; @@ -19,7 +19,7 @@ use flow::ImmutableFlowUtils; use flow::{Flow, FlowClass, OpaqueFlow}; use flow::{HAS_LEFT_FLOATED_DESCENDANTS, HAS_RIGHT_FLOATED_DESCENDANTS}; use fragment::{Fragment, FragmentBorderBoxIterator, Overflow}; -use gfx::display_list::DisplayList; +use gfx::display_list::{StackingContext, StackingContextId}; use incremental::{REFLOW, REFLOW_OUT_OF_FLOW}; use layout_debug; use model::MaybeAuto; @@ -420,14 +420,21 @@ impl Flow for FlexFlow { self.block_flow.update_late_computed_block_position_if_necessary(block_position) } - fn build_display_list(&mut self, layout_context: &LayoutContext) { - self.build_display_list_for_flex(Box::new(DisplayList::new()), layout_context); + fn build_display_list(&mut self, state: &mut DisplayListBuildState) { + self.build_display_list_for_flex(state); if opts::get().validate_display_list_geometry { self.block_flow.base.validate_display_list_geometry(); } } + fn collect_stacking_contexts(&mut self, + parent_id: StackingContextId, + contexts: &mut Vec) + -> StackingContextId { + self.block_flow.collect_stacking_contexts(parent_id, contexts) + } + fn repair_style(&mut self, new_style: &Arc) { self.block_flow.repair_style(new_style) } diff --git a/servo/components/layout/flow.rs b/servo/components/layout/flow.rs index b215856b9675..480b86898053 100644 --- a/servo/components/layout/flow.rs +++ b/servo/components/layout/flow.rs @@ -28,12 +28,13 @@ use app_units::Au; use block::BlockFlow; use context::LayoutContext; +use display_list_builder::DisplayListBuildState; use euclid::{Point2D, Rect, Size2D}; use floats::Floats; use flow_list::{FlowList, FlowListIterator, MutFlowListIterator}; use flow_ref::{self, FlowRef, WeakFlowRef}; use fragment::{Fragment, FragmentBorderBoxIterator, Overflow, SpecificFragmentInfo}; -use gfx::display_list::{ClippingRegion, DisplayList}; +use gfx::display_list::{ClippingRegion, DisplayListEntry, StackingContext, StackingContextId}; use gfx_traits::{LayerId, LayerType}; use incremental::{RECONSTRUCT_FLOW, REFLOW, REFLOW_OUT_OF_FLOW, REPAINT, RestyleDamage}; use inline::InlineFlow; @@ -221,6 +222,11 @@ pub trait Flow: fmt::Debug + Sync + Send + 'static { None } + fn collect_stacking_contexts(&mut self, + _parent_id: StackingContextId, + _: &mut Vec) + -> StackingContextId; + /// If this is a float, places it. The default implementation does nothing. fn place_float_if_applicable<'a>(&mut self, _: &'a LayoutContext<'a>) {} @@ -284,7 +290,7 @@ pub trait Flow: fmt::Debug + Sync + Send + 'static { } /// Phase 5 of reflow: builds display lists. - fn build_display_list(&mut self, layout_context: &LayoutContext); + fn build_display_list(&mut self, state: &mut DisplayListBuildState); /// Returns the union of all overflow rects of all of this flow's fragments. fn compute_overflow(&self) -> Overflow; @@ -361,17 +367,13 @@ pub trait Flow: fmt::Debug + Sync + Send + 'static { fn generated_containing_block_size(&self, _: OpaqueFlow) -> LogicalSize; /// Returns a layer ID for the given fragment. - #[allow(unsafe_code)] fn layer_id(&self) -> LayerId { - let obj = unsafe { mem::transmute::<&&Self, &raw::TraitObject>(&self) }; - LayerId::new_of_type(LayerType::FragmentBody, obj.data as usize) + LayerId::new_of_type(LayerType::FragmentBody, base(self).flow_id()) } /// Returns a layer ID for the given fragment. - #[allow(unsafe_code)] fn layer_id_for_overflow_scroll(&self) -> LayerId { - let obj = unsafe { mem::transmute::<&&Self, &raw::TraitObject>(&self) }; - LayerId::new_of_type(LayerType::OverflowScroll, obj.data as usize) + LayerId::new_of_type(LayerType::OverflowScroll, base(self).flow_id()) } /// Attempts to perform incremental fixup of this flow by replacing its fragment's style with @@ -924,7 +926,7 @@ pub struct BaseFlow { pub stacking_relative_position_of_display_port: Rect, /// The results of display list building for this flow. - pub display_list_building_result: Option>, + pub display_list_building_result: Option>, /// The writing mode for this flow. pub writing_mode: WritingMode, @@ -934,6 +936,11 @@ pub struct BaseFlow { /// Various flags for flows, tightly packed to save space. pub flags: FlowFlags, + + /// The ID of the StackingContext that contains this flow. This is initialized + /// to 0, but it assigned during the collect_stacking_contexts phase of display + /// list construction. + pub stacking_context_id: StackingContextId, } impl fmt::Debug for BaseFlow { @@ -958,7 +965,8 @@ impl fmt::Debug for BaseFlow { }; write!(f, - "pos={:?}, overflow={:?}{}{}{}", + "sc={:?} pos={:?}, overflow={:?}{}{}{}", + self.stacking_context_id, self.position, self.overflow, child_count_string, @@ -1097,6 +1105,7 @@ impl BaseFlow { flags: flags, writing_mode: writing_mode, thread_id: 0, + stacking_context_id: StackingContextId::new(0), } } @@ -1136,22 +1145,35 @@ impl BaseFlow { .union(&self.overflow.paint); let bounds = Rect::new(self.stacking_relative_position, position_with_overflow.size); - let all_items = match self.display_list_building_result { - Some(ref display_list) => display_list.flatten(), - None => Vec::new(), + let items = match self.display_list_building_result { + Some(ref items) => items, + None => return, }; - for item in &all_items { - if let Some(base_item) = item.base() { - let paint_bounds = base_item.clip.clone().intersect_rect(&base_item.bounds); - if !paint_bounds.might_be_nonempty() { - continue; - } - - if bounds.union(&paint_bounds.bounding_rect()) != bounds { - error!("DisplayList item {:?} outside of Flow overflow ({:?})", item, paint_bounds); - } + for item in items.iter() { + let base_item = item.item.base(); + let paint_bounds = base_item.clip.clone().intersect_rect(&base_item.bounds); + if !paint_bounds.might_be_nonempty() { + continue; } + + if bounds.union(&paint_bounds.bounding_rect()) != bounds { + error!("DisplayList item {:?} outside of Flow overflow ({:?})", + item.item, + paint_bounds); + } + } + } + + pub fn flow_id(&self) -> usize { + return self as *const BaseFlow as usize; + } + + pub fn collect_stacking_contexts_for_children(&mut self, + parent_id: StackingContextId, + contexts: &mut Vec) { + for kid in self.children.iter_mut() { + kid.collect_stacking_contexts(parent_id, contexts); } } } diff --git a/servo/components/layout/fragment.rs b/servo/components/layout/fragment.rs index 19d4a687a74e..db3a42a06695 100644 --- a/servo/components/layout/fragment.rs +++ b/servo/components/layout/fragment.rs @@ -14,7 +14,7 @@ use floats::ClearType; use flow::{self, Flow}; use flow_ref::{self, FlowRef}; use gfx; -use gfx::display_list::{BLUR_INFLATION_FACTOR, OpaqueNode}; +use gfx::display_list::{BLUR_INFLATION_FACTOR, FragmentType, OpaqueNode, StackingContextId}; use gfx::text::glyph::CharIndex; use gfx::text::text_run::{TextRun, TextRunSlice}; use gfx_traits::{LayerId, LayerType}; @@ -117,6 +117,11 @@ pub struct Fragment { /// A debug ID that is consistent for the life of this fragment (via transform etc). pub debug_id: u16, + + /// The ID of the StackingContext that contains this fragment. This is initialized + /// to 0, but it assigned during the collect_stacking_contexts phase of display + /// list construction. + pub stacking_context_id: StackingContextId, } impl Encodable for Fragment { @@ -790,6 +795,7 @@ impl Fragment { pseudo: node.get_pseudo_element_type().strip(), flags: FragmentFlags::empty(), debug_id: layout_debug::generate_unique_debug_id(), + stacking_context_id: StackingContextId::new(0), } } @@ -816,6 +822,7 @@ impl Fragment { pseudo: pseudo, flags: FragmentFlags::empty(), debug_id: layout_debug::generate_unique_debug_id(), + stacking_context_id: StackingContextId::new(0), } } @@ -848,6 +855,7 @@ impl Fragment { pseudo: self.pseudo.clone(), flags: FragmentFlags::empty(), debug_id: self.debug_id, + stacking_context_id: StackingContextId::new(0), } } @@ -2139,10 +2147,12 @@ impl Fragment { overflow_x::T::visible) => false, (position::T::absolute, _, _, _) | (position::T::fixed, _, _, _) | - (position::T::relative, _, _, _) => true, - (position::T::static_, _, _, _) => { - false - } + (position::T::relative, _, _, _) | + (_, _, overflow_x::T::auto, _) | + (_, _, overflow_x::T::scroll, _) | + (_, _, _, overflow_x::T::auto) | + (_, _, _, overflow_x::T::scroll) => true, + (position::T::static_, _, _, _) => false } } @@ -2424,6 +2434,18 @@ impl Fragment { } } + pub fn fragment_id(&self) -> usize { + return self as *const Fragment as usize; + } + + pub fn fragment_type(&self) -> FragmentType { + match self.pseudo { + PseudoElementType::Normal => FragmentType::FragmentBody, + PseudoElementType::Before(_) => FragmentType::BeforePseudoContent, + PseudoElementType::After(_) => FragmentType::AfterPseudoContent + } + } + pub fn layer_id(&self) -> LayerId { let layer_type = match self.pseudo { PseudoElementType::Normal => LayerType::FragmentBody, diff --git a/servo/components/layout/inline.rs b/servo/components/layout/inline.rs index bac7f3e07b98..ed07f339720a 100644 --- a/servo/components/layout/inline.rs +++ b/servo/components/layout/inline.rs @@ -7,6 +7,7 @@ use app_units::Au; use block::AbsoluteAssignBSizesTraversal; use context::LayoutContext; +use display_list_builder::DisplayListBuildState; use display_list_builder::{FragmentDisplayListBuilding, InlineFlowDisplayListBuilding}; use euclid::{Point2D, Size2D}; use floats::{FloatKind, Floats, PlacementInfo}; @@ -15,7 +16,7 @@ use flow::{self, BaseFlow, Flow, FlowClass, ForceNonfloatedFlag, IS_ABSOLUTELY_P use flow_ref; use fragment::{CoordinateSystem, Fragment, FragmentBorderBoxIterator, Overflow}; use fragment::{SpecificFragmentInfo}; -use gfx::display_list::OpaqueNode; +use gfx::display_list::{OpaqueNode, StackingContext, StackingContextId}; use gfx::font::FontMetrics; use gfx::font_context::FontContext; use incremental::{BUBBLE_ISIZES, REFLOW, REFLOW_OUT_OF_FLOW, REPAINT, RESOLVE_GENERATED_CONTENT}; @@ -1747,8 +1748,15 @@ impl Flow for InlineFlow { fn update_late_computed_block_position_if_necessary(&mut self, _: Au) {} - fn build_display_list(&mut self, layout_context: &LayoutContext) { - self.build_display_list_for_inline(layout_context); + fn collect_stacking_contexts(&mut self, + parent_id: StackingContextId, + contexts: &mut Vec) + -> StackingContextId { + self.collect_stacking_contexts_for_inline(parent_id, contexts) + } + + fn build_display_list(&mut self, state: &mut DisplayListBuildState) { + self.build_display_list_for_inline(state); for fragment in &mut self.fragments.fragments { fragment.restyle_damage.remove(REPAINT); diff --git a/servo/components/layout/layout_thread.rs b/servo/components/layout/layout_thread.rs index 999a979f0281..bf4a6ac9745d 100644 --- a/servo/components/layout/layout_thread.rs +++ b/servo/components/layout/layout_thread.rs @@ -22,11 +22,12 @@ use euclid::size::Size2D; use flow::{self, Flow, ImmutableFlowUtils, MutableFlowUtils, MutableOwnedFlowUtils}; use flow_ref::{self, FlowRef}; use fnv::FnvHasher; -use gfx::display_list::{ClippingRegion, DisplayList, LayerInfo, OpaqueNode, StackingContext}; +use gfx::display_list::{ClippingRegion, DisplayList, LayerInfo}; +use gfx::display_list::{OpaqueNode, StackingContext, StackingContextId, StackingContextType}; use gfx::font; use gfx::font_cache_thread::FontCacheThread; use gfx::font_context; -use gfx::paint_thread::{LayoutToPaintMsg, PaintLayer}; +use gfx::paint_thread::LayoutToPaintMsg; use gfx_traits::{color, Epoch, LayerId, ScrollPolicy}; use heapsize::HeapSizeOf; use incremental::{LayoutDamageComputation, REFLOW, REFLOW_ENTIRE_DOCUMENT, REPAINT}; @@ -81,7 +82,7 @@ use util::opts; use util::thread; use util::thread_state; use util::workqueue::WorkQueue; -use webrender_helpers::WebRenderStackingContextConverter; +use webrender_helpers::WebRenderDisplayListConverter; use webrender_traits; use wrapper::{LayoutNode, NonOpaqueStyleAndLayoutData, ServoLayoutNode, ThreadSafeLayoutNode}; @@ -99,7 +100,7 @@ pub struct LayoutThreadData { pub constellation_chan: ConstellationChan, /// The root stacking context. - pub stacking_context: Option>, + pub display_list: Option>, /// Performs CSS selector matching and style resolution. pub stylist: Box, @@ -452,7 +453,7 @@ impl LayoutThread { rw_data: Arc::new(Mutex::new( LayoutThreadData { constellation_chan: constellation_chan, - stacking_context: None, + display_list: None, stylist: stylist, content_box_response: Rect::zero(), content_boxes_response: Vec::new(), @@ -671,12 +672,12 @@ impl LayoutThread { // FIXME(njn): Just measuring the display tree for now. let rw_data = possibly_locked_rw_data.lock(); - let stacking_context = rw_data.stacking_context.as_ref(); + let display_list = rw_data.display_list.as_ref(); let formatted_url = &format!("url({})", *self.url.borrow()); reports.push(Report { path: path![formatted_url, "layout-thread", "display-list"], kind: ReportKind::ExplicitJemallocHeapSize, - size: stacking_context.map_or(0, |sc| sc.heap_size_of_children()), + size: display_list.map_or(0, |sc| sc.heap_size_of_children()), }); // The LayoutThread has a context in TLS... @@ -860,7 +861,23 @@ impl LayoutThread { flow::mut_base(flow_ref::deref_mut(layout_root)).clip = ClippingRegion::from_rect(&data.page_clip_rect); - sequential::build_display_list_for_subtree(layout_root, shared_layout_context); + let mut root_stacking_context = + StackingContext::new(StackingContextId::new(0), + StackingContextType::Real, + &Rect::zero(), + &Rect::zero(), + 0, + filter::T::new(Vec::new()), + mix_blend_mode::T::normal, + Matrix4::identity(), + Matrix4::identity(), + true, + false, + None); + + sequential::build_display_list_for_subtree(layout_root, + &mut root_stacking_context, + shared_layout_context); if data.goal == ReflowGoal::ForDisplay { debug!("Done building display list."); @@ -875,37 +892,28 @@ impl LayoutThread { root_flow.overflow.scroll.size } }; - let mut display_list = box DisplayList::new(); - display_list.append_from(&mut flow::mut_base(flow_ref::deref_mut(layout_root)) - .display_list_building_result); let origin = Rect::new(Point2D::new(Au(0), Au(0)), root_size); - let stacking_context = Arc::new(StackingContext::new(display_list, - &origin, - &origin, - 0, - filter::T::new(Vec::new()), - mix_blend_mode::T::normal, - Matrix4::identity(), - Matrix4::identity(), - true, - false, - None)); + root_stacking_context.bounds = origin; + root_stacking_context.overflow = origin; + root_stacking_context.layer_info = Some(LayerInfo::new(layout_root.layer_id(), + ScrollPolicy::Scrollable, + None, + root_background_color)); + let display_list = DisplayList::new( + root_stacking_context, + &mut flow::mut_base(flow_ref::deref_mut(layout_root)) + .display_list_building_result); + if opts::get().dump_display_list { - stacking_context.print("DisplayList".to_owned()); + display_list.print(); } if opts::get().dump_display_list_json { - println!("{}", serde_json::to_string_pretty(&stacking_context).unwrap()); + println!("{}", serde_json::to_string_pretty(&display_list).unwrap()); } - rw_data.stacking_context = Some(stacking_context.clone()); - - let layer_info = LayerInfo::new(layout_root.layer_id(), - ScrollPolicy::Scrollable, - None); - let paint_layer = PaintLayer::new_with_stacking_context(layer_info, - stacking_context, - root_background_color); + let display_list = Arc::new(display_list); + rw_data.display_list = Some(display_list.clone()); debug!("Layout done!"); @@ -920,12 +928,12 @@ impl LayoutThread { // TODO(gw) For now only create a root scrolling layer! let root_scroll_layer_id = webrender_traits::ScrollLayerId::new(pipeline_id, 0); - let sc_id = rw_data.stacking_context.as_ref() - .unwrap() - .convert_to_webrender(&self.webrender_api.as_ref().unwrap(), - pipeline_id, - epoch, - Some(root_scroll_layer_id)); + let sc_id = rw_data.display_list.as_ref() + .unwrap() + .convert_to_webrender(&self.webrender_api.as_ref().unwrap(), + pipeline_id, + epoch, + Some(root_scroll_layer_id)); let root_background_color = webrender_traits::ColorF::new(root_background_color.r, root_background_color.g, root_background_color.b, @@ -941,7 +949,7 @@ impl LayoutThread { viewport_size); } else { self.paint_chan - .send(LayoutToPaintMsg::PaintInit(self.epoch, paint_layer)) + .send(LayoutToPaintMsg::PaintInit(self.epoch, display_list)) .unwrap(); } } diff --git a/servo/components/layout/list_item.rs b/servo/components/layout/list_item.rs index 8caf8dfdd774..701ac4ecdf96 100644 --- a/servo/components/layout/list_item.rs +++ b/servo/components/layout/list_item.rs @@ -10,14 +10,14 @@ use app_units::Au; use block::BlockFlow; use context::LayoutContext; -use display_list_builder::ListItemFlowDisplayListBuilding; +use display_list_builder::{DisplayListBuildState, ListItemFlowDisplayListBuilding}; use euclid::Point2D; use floats::FloatKind; use flow::{Flow, FlowClass, OpaqueFlow}; use fragment::{CoordinateSystem, Fragment, FragmentBorderBoxIterator, GeneratedContentInfo}; use fragment::{Overflow}; use generated_content; -use gfx::display_list::DisplayList; +use gfx::display_list::{StackingContext, StackingContextId}; use incremental::RESOLVE_GENERATED_CONTENT; use inline::InlineMetrics; use std::sync::Arc; @@ -142,13 +142,20 @@ impl Flow for ListItemFlow { self.block_flow.update_late_computed_block_position_if_necessary(block_position) } - fn build_display_list(&mut self, layout_context: &LayoutContext) { - self.build_display_list_for_list_item(box DisplayList::new(), layout_context); + fn build_display_list(&mut self, state: &mut DisplayListBuildState) { + self.build_display_list_for_list_item(state); if opts::get().validate_display_list_geometry { self.block_flow.base.validate_display_list_geometry(); } } + fn collect_stacking_contexts(&mut self, + parent_id: StackingContextId, + contexts: &mut Vec) + -> StackingContextId { + self.block_flow.collect_stacking_contexts(parent_id, contexts) + } + fn repair_style(&mut self, new_style: &Arc) { self.block_flow.repair_style(new_style) } diff --git a/servo/components/layout/multicol.rs b/servo/components/layout/multicol.rs index 1485ade4aeb2..36815c825338 100644 --- a/servo/components/layout/multicol.rs +++ b/servo/components/layout/multicol.rs @@ -9,11 +9,13 @@ use app_units::Au; use block::BlockFlow; use context::LayoutContext; +use display_list_builder::DisplayListBuildState; use euclid::Point2D; use floats::FloatKind; use flow::{Flow, FlowClass, OpaqueFlow, mut_base, FragmentationContext}; use flow_ref::{self, FlowRef}; use fragment::{Fragment, FragmentBorderBoxIterator, Overflow}; +use gfx::display_list::{StackingContext, StackingContextId}; use std::cmp::{min, max}; use std::fmt; use std::sync::Arc; @@ -177,9 +179,16 @@ impl Flow for MulticolFlow { self.block_flow.update_late_computed_block_position_if_necessary(block_position) } - fn build_display_list(&mut self, layout_context: &LayoutContext) { + fn build_display_list(&mut self, state: &mut DisplayListBuildState) { debug!("build_display_list_multicol"); - self.block_flow.build_display_list(layout_context); + self.block_flow.build_display_list(state); + } + + fn collect_stacking_contexts(&mut self, + parent_id: StackingContextId, + contexts: &mut Vec) + -> StackingContextId { + self.block_flow.collect_stacking_contexts(parent_id, contexts) } fn repair_style(&mut self, new_style: &Arc) { @@ -255,9 +264,16 @@ impl Flow for MulticolColumnFlow { self.block_flow.update_late_computed_block_position_if_necessary(block_position) } - fn build_display_list(&mut self, layout_context: &LayoutContext) { + fn build_display_list(&mut self, state: &mut DisplayListBuildState) { debug!("build_display_list_multicol column"); - self.block_flow.build_display_list(layout_context); + self.block_flow.build_display_list(state); + } + + fn collect_stacking_contexts(&mut self, + parent_id: StackingContextId, + contexts: &mut Vec) + -> StackingContextId { + self.block_flow.collect_stacking_contexts(parent_id, contexts) } fn repair_style(&mut self, new_style: &Arc) { diff --git a/servo/components/layout/query.rs b/servo/components/layout/query.rs index 0a66a135919a..348a3ad556d0 100644 --- a/servo/components/layout/query.rs +++ b/servo/components/layout/query.rs @@ -11,7 +11,7 @@ use euclid::rect::Rect; use flow; use flow_ref::FlowRef; use fragment::{Fragment, FragmentBorderBoxIterator, SpecificFragmentInfo}; -use gfx::display_list::{DisplayItemMetadata, OpaqueNode}; +use gfx::display_list::OpaqueNode; use layout_thread::LayoutThreadData; use msg::constellation_msg::ConstellationChan; use opaque_node::OpaqueNodeMethods; @@ -69,51 +69,40 @@ impl LayoutRPC for LayoutRPCImpl { /// Requests the node containing the point of interest. fn hit_test(&self, point: Point2D) -> Result { let point = Point2D::new(Au::from_f32_px(point.x), Au::from_f32_px(point.y)); - let resp = { - let &LayoutRPCImpl(ref rw_data) = self; - let rw_data = rw_data.lock().unwrap(); - 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].node.to_untrusted_node_address())) - } else { - None - } - } - } + let &LayoutRPCImpl(ref rw_data) = self; + let rw_data = rw_data.lock().unwrap(); + let result = match rw_data.display_list { + None => panic!("Tried to hit test without a DisplayList"), + Some(ref display_list) => display_list.hit_test(point), }; - if resp.is_some() { - return Ok(resp.unwrap()); + if result.is_empty() { + return Err(()); } - Err(()) + + Ok(HitTestResponse(result[0].node.to_untrusted_node_address())) } fn mouse_over(&self, point: Point2D) -> Result { - let mut mouse_over_list: Vec = vec!(); let point = Point2D::new(Au::from_f32_px(point.x), Au::from_f32_px(point.y)); - { + let mouse_over_list = { let &LayoutRPCImpl(ref rw_data) = self; let rw_data = rw_data.lock().unwrap(); - 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); - } - } + let result = match rw_data.display_list { + None => panic!("Tried to hit test without a DisplayList"), + Some(ref display_list) => display_list.hit_test(point), + }; // Compute the new cursor. - let cursor = if !mouse_over_list.is_empty() { - mouse_over_list[0].pointing.unwrap() + let cursor = if !result.is_empty() { + result[0].pointing.unwrap() } else { Cursor::DefaultCursor }; let ConstellationChan(ref constellation_chan) = rw_data.constellation_chan; constellation_chan.send(ConstellationMsg::SetCursor(cursor)).unwrap(); - } + result + }; if mouse_over_list.is_empty() { Err(()) diff --git a/servo/components/layout/sequential.rs b/servo/components/layout/sequential.rs index ad332f30fddb..e0207ee7ca02 100644 --- a/servo/components/layout/sequential.rs +++ b/servo/components/layout/sequential.rs @@ -12,6 +12,7 @@ use flow::{self, Flow, ImmutableFlowUtils, InorderFlowTraversal, MutableFlowUtil use flow_ref::{self, FlowRef}; use fragment::FragmentBorderBoxIterator; use generated_content::ResolveGeneratedContent; +use gfx::display_list::StackingContext; use style::dom::TNode; use style::traversal::DomTraversalContext; use traversal::{AssignBSizesAndStoreOverflow, AssignISizes}; @@ -75,28 +76,14 @@ pub fn traverse_flow_tree_preorder(root: &mut FlowRef, } pub fn build_display_list_for_subtree(root: &mut FlowRef, + root_stacking_context: &mut StackingContext, shared_layout_context: &SharedLayoutContext) { - fn doit(flow: &mut Flow, - compute_absolute_positions: ComputeAbsolutePositions, - build_display_list: BuildDisplayList) { - if compute_absolute_positions.should_process(flow) { - compute_absolute_positions.process(flow); - } - - for kid in flow::mut_base(flow).child_iter() { - doit(kid, compute_absolute_positions, build_display_list); - } - - if build_display_list.should_process(flow) { - build_display_list.process(flow); - } - } - + let flow_root = flow_ref::deref_mut(root); let layout_context = LayoutContext::new(shared_layout_context); - let compute_absolute_positions = ComputeAbsolutePositions { layout_context: &layout_context }; - let build_display_list = BuildDisplayList { layout_context: &layout_context }; - - doit(flow_ref::deref_mut(root), compute_absolute_positions, build_display_list); + flow_root.traverse_preorder(&ComputeAbsolutePositions { layout_context: &layout_context }); + flow_root.collect_stacking_contexts(root_stacking_context.id, + &mut root_stacking_context.children); + flow_root.traverse_postorder(&BuildDisplayList { layout_context: &layout_context }); } pub fn iterate_through_flow_tree_fragment_border_boxes(root: &mut FlowRef, diff --git a/servo/components/layout/table.rs b/servo/components/layout/table.rs index 7f0a4b09a59e..eb0c57f615a9 100644 --- a/servo/components/layout/table.rs +++ b/servo/components/layout/table.rs @@ -10,13 +10,13 @@ use app_units::Au; use block::{BlockFlow, CandidateBSizeIterator, ISizeAndMarginsComputer}; use block::{ISizeConstraintInput, ISizeConstraintSolution}; use context::LayoutContext; -use display_list_builder::{BlockFlowDisplayListBuilding, BorderPaintingMode}; +use display_list_builder::{BlockFlowDisplayListBuilding, BorderPaintingMode, DisplayListBuildState}; use euclid::Point2D; use flow::{BaseFlow, IMPACTED_BY_RIGHT_FLOATS, ImmutableFlowUtils, OpaqueFlow}; use flow::{self, EarlyAbsolutePositionInfo, Flow, FlowClass, IMPACTED_BY_LEFT_FLOATS}; use flow_list::MutFlowListIterator; use fragment::{Fragment, FragmentBorderBoxIterator, Overflow}; -use gfx::display_list::DisplayList; +use gfx::display_list::{StackingContext, StackingContextId}; use incremental::{REFLOW, REFLOW_OUT_OF_FLOW}; use layout_debug; use model::{IntrinsicISizes, IntrinsicISizesContribution, MaybeAuto}; @@ -452,7 +452,7 @@ impl Flow for TableFlow { self.block_flow.update_late_computed_block_position_if_necessary(block_position) } - fn build_display_list(&mut self, layout_context: &LayoutContext) { + fn build_display_list(&mut self, state: &mut DisplayListBuildState) { let border_painting_mode = match self.block_flow .fragment .style @@ -462,9 +462,14 @@ impl Flow for TableFlow { border_collapse::T::collapse => BorderPaintingMode::Hidden, }; - self.block_flow.build_display_list_for_block(box DisplayList::new(), - layout_context, - border_painting_mode); + self.block_flow.build_display_list_for_block(state, border_painting_mode); + } + + fn collect_stacking_contexts(&mut self, + parent_id: StackingContextId, + contexts: &mut Vec) + -> StackingContextId { + self.block_flow.collect_stacking_contexts(parent_id, contexts) } fn repair_style(&mut self, new_style: &Arc) { diff --git a/servo/components/layout/table_caption.rs b/servo/components/layout/table_caption.rs index 5c01132ccd6e..4bb783154d21 100644 --- a/servo/components/layout/table_caption.rs +++ b/servo/components/layout/table_caption.rs @@ -9,9 +9,11 @@ use app_units::Au; use block::BlockFlow; use context::LayoutContext; +use display_list_builder::DisplayListBuildState; use euclid::Point2D; use flow::{Flow, FlowClass, OpaqueFlow}; use fragment::{Fragment, FragmentBorderBoxIterator, Overflow}; +use gfx::display_list::{StackingContext, StackingContextId}; use std::fmt; use std::sync::Arc; use style::logical_geometry::LogicalSize; @@ -74,9 +76,16 @@ impl Flow for TableCaptionFlow { self.block_flow.update_late_computed_block_position_if_necessary(block_position) } - fn build_display_list(&mut self, layout_context: &LayoutContext) { + fn build_display_list(&mut self, state: &mut DisplayListBuildState) { debug!("build_display_list_table_caption: same process as block flow"); - self.block_flow.build_display_list(layout_context) + self.block_flow.build_display_list(state); + } + + fn collect_stacking_contexts(&mut self, + parent_id: StackingContextId, + contexts: &mut Vec) + -> StackingContextId { + self.block_flow.collect_stacking_contexts(parent_id, contexts) } fn repair_style(&mut self, new_style: &Arc) { diff --git a/servo/components/layout/table_cell.rs b/servo/components/layout/table_cell.rs index d1c33654bbce..0faebcb3cf2a 100644 --- a/servo/components/layout/table_cell.rs +++ b/servo/components/layout/table_cell.rs @@ -10,11 +10,11 @@ use app_units::Au; use block::{BlockFlow, ISizeAndMarginsComputer, MarginsMayCollapseFlag}; use context::LayoutContext; use cssparser::Color; -use display_list_builder::{BlockFlowDisplayListBuilding, BorderPaintingMode}; +use display_list_builder::{BlockFlowDisplayListBuilding, BorderPaintingMode, DisplayListBuildState}; use euclid::{Point2D, Rect, SideOffsets2D, Size2D}; use flow::{Flow, FlowClass, OpaqueFlow}; use fragment::{Fragment, FragmentBorderBoxIterator, Overflow}; -use gfx::display_list::DisplayList; +use gfx::display_list::{StackingContext, StackingContextId}; use layout_debug; use model::MaybeAuto; use std::fmt; @@ -174,7 +174,7 @@ impl Flow for TableCellFlow { self.block_flow.update_late_computed_block_position_if_necessary(block_position) } - fn build_display_list(&mut self, layout_context: &LayoutContext) { + fn build_display_list(&mut self, state: &mut DisplayListBuildState) { if !self.visible { return } @@ -188,9 +188,14 @@ impl Flow for TableCellFlow { border_collapse::T::collapse => BorderPaintingMode::Collapse(&self.collapsed_borders), }; - self.block_flow.build_display_list_for_block(box DisplayList::new(), - layout_context, - border_painting_mode) + self.block_flow.build_display_list_for_block(state, border_painting_mode) + } + + fn collect_stacking_contexts(&mut self, + parent_id: StackingContextId, + contexts: &mut Vec) + -> StackingContextId { + self.block_flow.collect_stacking_contexts(parent_id, contexts) } fn repair_style(&mut self, new_style: &Arc) { diff --git a/servo/components/layout/table_colgroup.rs b/servo/components/layout/table_colgroup.rs index f62ceb535921..9f1a03e7e0b5 100644 --- a/servo/components/layout/table_colgroup.rs +++ b/servo/components/layout/table_colgroup.rs @@ -8,9 +8,11 @@ use app_units::Au; use context::LayoutContext; +use display_list_builder::DisplayListBuildState; use euclid::Point2D; use flow::{BaseFlow, Flow, FlowClass, ForceNonfloatedFlag, OpaqueFlow}; use fragment::{Fragment, FragmentBorderBoxIterator, Overflow, SpecificFragmentInfo}; +use gfx::display_list::{StackingContext, StackingContextId}; use layout_debug; use std::cmp::max; use std::fmt; @@ -90,7 +92,14 @@ impl Flow for TableColGroupFlow { fn update_late_computed_block_position_if_necessary(&mut self, _: Au) {} // Table columns are invisible. - fn build_display_list(&mut self, _: &LayoutContext) {} + fn build_display_list(&mut self, _: &mut DisplayListBuildState) { } + + fn collect_stacking_contexts(&mut self, + parent_id: StackingContextId, + _: &mut Vec) + -> StackingContextId { + parent_id + } fn repair_style(&mut self, _: &Arc) {} diff --git a/servo/components/layout/table_row.rs b/servo/components/layout/table_row.rs index a7da3b197490..f0e326414e22 100644 --- a/servo/components/layout/table_row.rs +++ b/servo/components/layout/table_row.rs @@ -10,12 +10,12 @@ use app_units::Au; use block::{BlockFlow, ISizeAndMarginsComputer}; use context::LayoutContext; use cssparser::{Color, RGBA}; -use display_list_builder::{BlockFlowDisplayListBuilding, BorderPaintingMode}; +use display_list_builder::{BlockFlowDisplayListBuilding, BorderPaintingMode, DisplayListBuildState}; use euclid::Point2D; use flow::{self, EarlyAbsolutePositionInfo, Flow, FlowClass, ImmutableFlowUtils, OpaqueFlow}; use flow_list::MutFlowListIterator; use fragment::{Fragment, FragmentBorderBoxIterator, Overflow}; -use gfx::display_list::DisplayList; +use gfx::display_list::{StackingContext, StackingContextId}; use layout_debug; use model::MaybeAuto; use rustc_serialize::{Encodable, Encoder}; @@ -420,7 +420,7 @@ impl Flow for TableRowFlow { self.block_flow.update_late_computed_block_position_if_necessary(block_position) } - fn build_display_list(&mut self, layout_context: &LayoutContext) { + fn build_display_list(&mut self, state: &mut DisplayListBuildState) { let border_painting_mode = match self.block_flow .fragment .style @@ -430,9 +430,14 @@ impl Flow for TableRowFlow { border_collapse::T::collapse => BorderPaintingMode::Hidden, }; - self.block_flow.build_display_list_for_block(box DisplayList::new(), - layout_context, - border_painting_mode); + self.block_flow.build_display_list_for_block(state, border_painting_mode); + } + + fn collect_stacking_contexts(&mut self, + parent_id: StackingContextId, + contexts: &mut Vec) + -> StackingContextId { + self.block_flow.collect_stacking_contexts(parent_id, contexts) } fn repair_style(&mut self, new_style: &Arc) { diff --git a/servo/components/layout/table_rowgroup.rs b/servo/components/layout/table_rowgroup.rs index 4536f96055ed..6971df77787b 100644 --- a/servo/components/layout/table_rowgroup.rs +++ b/servo/components/layout/table_rowgroup.rs @@ -9,9 +9,11 @@ use app_units::Au; use block::{BlockFlow, ISizeAndMarginsComputer}; use context::LayoutContext; +use display_list_builder::DisplayListBuildState; use euclid::Point2D; use flow::{Flow, FlowClass, OpaqueFlow}; use fragment::{Fragment, FragmentBorderBoxIterator, Overflow}; +use gfx::display_list::{StackingContext, StackingContextId}; use layout_debug; use rustc_serialize::{Encodable, Encoder}; use std::fmt; @@ -203,9 +205,16 @@ impl Flow for TableRowGroupFlow { self.block_flow.update_late_computed_block_position_if_necessary(block_position) } - fn build_display_list(&mut self, layout_context: &LayoutContext) { + fn build_display_list(&mut self, state: &mut DisplayListBuildState) { debug!("build_display_list_table_rowgroup: same process as block flow"); - self.block_flow.build_display_list(layout_context) + self.block_flow.build_display_list(state); + } + + fn collect_stacking_contexts(&mut self, + parent_id: StackingContextId, + contexts: &mut Vec) + -> StackingContextId { + self.block_flow.collect_stacking_contexts(parent_id, contexts) } fn repair_style(&mut self, new_style: &Arc) { diff --git a/servo/components/layout/table_wrapper.rs b/servo/components/layout/table_wrapper.rs index d8cc8d01aa3a..d32241269297 100644 --- a/servo/components/layout/table_wrapper.rs +++ b/servo/components/layout/table_wrapper.rs @@ -17,11 +17,13 @@ use app_units::Au; use block::{AbsoluteNonReplaced, BlockFlow, FloatNonReplaced, ISizeAndMarginsComputer, ISizeConstraintInput}; use block::{ISizeConstraintSolution, MarginsMayCollapseFlag}; use context::LayoutContext; +use display_list_builder::DisplayListBuildState; use euclid::Point2D; use floats::FloatKind; use flow::{Flow, FlowClass, ImmutableFlowUtils}; use flow::{IMPACTED_BY_LEFT_FLOATS, IMPACTED_BY_RIGHT_FLOATS, INLINE_POSITION_IS_STATIC, OpaqueFlow}; use fragment::{Fragment, FragmentBorderBoxIterator, Overflow}; +use gfx::display_list::{StackingContext, StackingContextId}; use model::MaybeAuto; use std::cmp::{max, min}; use std::fmt; @@ -443,8 +445,15 @@ impl Flow for TableWrapperFlow { self.block_flow.generated_containing_block_size(flow) } - fn build_display_list(&mut self, layout_context: &LayoutContext) { - self.block_flow.build_display_list(layout_context) + fn build_display_list(&mut self, state: &mut DisplayListBuildState) { + self.block_flow.build_display_list(state); + } + + fn collect_stacking_contexts(&mut self, + parent_id: StackingContextId, + contexts: &mut Vec) + -> StackingContextId { + self.block_flow.collect_stacking_contexts(parent_id, contexts) } fn repair_style(&mut self, new_style: &Arc) { diff --git a/servo/components/layout/traversal.rs b/servo/components/layout/traversal.rs index 352695293526..696c43b210bb 100644 --- a/servo/components/layout/traversal.rs +++ b/servo/components/layout/traversal.rs @@ -9,6 +9,7 @@ use construct::FlowConstructor; use context::{LayoutContext, SharedLayoutContext}; +use display_list_builder::DisplayListBuildState; use flow::{PostorderFlowTraversal, PreorderFlowTraversal}; use flow::{self, Flow, CAN_BE_FRAGMENTED}; use gfx::display_list::OpaqueNode; @@ -222,7 +223,10 @@ pub struct BuildDisplayList<'a> { impl<'a> PostorderFlowTraversal for BuildDisplayList<'a> { #[inline] fn process(&self, flow: &mut Flow) { - flow.build_display_list(self.layout_context); + let mut state = DisplayListBuildState::new( + self.layout_context, flow::base(flow).stacking_context_id); + flow.build_display_list(&mut state); + flow::mut_base(flow).display_list_building_result = Some(state.items); flow::mut_base(flow).restyle_damage.remove(REPAINT); } diff --git a/servo/components/layout/webrender_helpers.rs b/servo/components/layout/webrender_helpers.rs index a1ab8fac3fe7..6d87b56e2e41 100644 --- a/servo/components/layout/webrender_helpers.rs +++ b/servo/components/layout/webrender_helpers.rs @@ -12,8 +12,8 @@ use azure::azure_hl::Color; use euclid::num::Zero; use euclid::{Point2D, Rect, Size2D}; use gfx::display_list::{BorderRadii, BoxShadowClipMode, ClippingRegion}; -use gfx::display_list::{DisplayItem, DisplayList}; -use gfx::display_list::{GradientStop, StackingContext}; +use gfx::display_list::{DisplayItem, DisplayList, DisplayListEntry, DisplayListSection}; +use gfx::display_list::{DisplayListTraversal, GradientStop, StackingContext, StackingContextType}; use gfx_traits::ScrollPolicy; use msg::constellation_msg::ConvertPipelineIdToWebRender; use style::computed_values::filter::{self, Filter}; @@ -21,31 +21,59 @@ use style::computed_values::{image_rendering, mix_blend_mode}; use style::values::computed::BorderStyle; use webrender_traits; -pub trait WebRenderStackingContextConverter { +trait WebRenderStackingContextConverter { + fn convert_to_webrender<'a>(&self, + traversal: &mut DisplayListTraversal<'a>, + api: &webrender_traits::RenderApi, + pipeline_id: webrender_traits::PipelineId, + epoch: webrender_traits::Epoch, + scroll_layer_id: Option) + -> webrender_traits::StackingContextId; + + fn convert_children_to_webrender<'a>(&self, + traversal: &mut DisplayListTraversal<'a>, + api: &webrender_traits::RenderApi, + pipeline_id: webrender_traits::PipelineId, + epoch: webrender_traits::Epoch, + scroll_layer_id: Option, + builder: &mut webrender_traits::DisplayListBuilder, + force_positioned_stacking_level: bool); + + fn web_render_stacking_level(&self) -> webrender_traits::StackingLevel; +} + +pub trait WebRenderDisplayListConverter { fn convert_to_webrender(&self, api: &webrender_traits::RenderApi, pipeline_id: webrender_traits::PipelineId, epoch: webrender_traits::Epoch, scroll_layer_id: Option) - -> webrender_traits::StackingContextId; -} - -trait WebRenderDisplayListConverter { - fn convert_to_webrender(&self, - api: &webrender_traits::RenderApi, - pipeline_id: webrender_traits::PipelineId, - epoch: webrender_traits::Epoch) -> webrender_traits::DisplayListBuilder; + -> webrender_traits::StackingContextId; } trait WebRenderDisplayItemConverter { fn convert_to_webrender(&self, - api: &webrender_traits::RenderApi, - pipeline_id: webrender_traits::PipelineId, - epoch: webrender_traits::Epoch, level: webrender_traits::StackingLevel, builder: &mut webrender_traits::DisplayListBuilder); } +trait WebRenderDisplayListEntryConverter { + fn web_render_stacking_level(&self) -> webrender_traits::StackingLevel; +} + +impl WebRenderDisplayListEntryConverter for DisplayListEntry { + fn web_render_stacking_level(&self) -> webrender_traits::StackingLevel { + match self.section { + DisplayListSection::BackgroundAndBorders => + webrender_traits::StackingLevel::BackgroundAndBorders, + DisplayListSection::BlockBackgroundsAndBorders => + webrender_traits::StackingLevel::BlockBackgroundAndBorders, + DisplayListSection::Content => webrender_traits::StackingLevel::Content, + DisplayListSection::Outlines => webrender_traits::StackingLevel::Outlines, + } + } +} + trait ToBorderStyle { fn to_border_style(&self) -> webrender_traits::BorderStyle; } @@ -235,11 +263,55 @@ impl ToFilterOps for filter::T { } impl WebRenderStackingContextConverter for StackingContext { - fn convert_to_webrender(&self, - api: &webrender_traits::RenderApi, - pipeline_id: webrender_traits::PipelineId, - epoch: webrender_traits::Epoch, - scroll_layer_id: Option) + fn convert_children_to_webrender<'a>(&self, + traversal: &mut DisplayListTraversal<'a>, + api: &webrender_traits::RenderApi, + pipeline_id: webrender_traits::PipelineId, + epoch: webrender_traits::Epoch, + scroll_layer_id: Option, + builder: &mut webrender_traits::DisplayListBuilder, + force_positioned_stacking_level: bool) { + for child in self.children.iter() { + while let Some(item) = traversal.advance(self) { + let stacking_level = if force_positioned_stacking_level { + webrender_traits::StackingLevel::PositionedContent + } else { + item.web_render_stacking_level() + }; + item.item.convert_to_webrender(stacking_level, builder); + + } + if child.context_type == StackingContextType::Real { + let stacking_context_id = child.convert_to_webrender(traversal, + api, + pipeline_id, + epoch, + None); + builder.push_stacking_context(child.web_render_stacking_level(), + stacking_context_id); + } else { + child.convert_children_to_webrender(traversal, + api, + pipeline_id, + epoch, + scroll_layer_id, + builder, + true); + } + } + + while let Some(item) = traversal.advance(self) { + item.item.convert_to_webrender(webrender_traits::StackingLevel::PositionedContent, + builder); + } + } + + fn convert_to_webrender<'a>(&self, + traversal: &mut DisplayListTraversal<'a>, + api: &webrender_traits::RenderApi, + pipeline_id: webrender_traits::PipelineId, + epoch: webrender_traits::Epoch, + scroll_layer_id: Option) -> webrender_traits::StackingContextId { let scroll_policy = self.layer_info .map_or(webrender_traits::ScrollPolicy::Scrollable, |info| { @@ -259,80 +331,50 @@ impl WebRenderStackingContextConverter for StackingContext { self.establishes_3d_context, self.blend_mode.to_blend_mode(), self.filters.to_filter_ops()); - - let dl_builder = self.display_list.convert_to_webrender(api, - pipeline_id, - epoch); - api.add_display_list(dl_builder, &mut sc, pipeline_id, epoch); - + let mut builder = webrender_traits::DisplayListBuilder::new(); + self.convert_children_to_webrender(traversal, + api, + pipeline_id, + epoch, + scroll_layer_id, + &mut builder, + false); + api.add_display_list(builder, &mut sc, pipeline_id, epoch); api.add_stacking_context(sc, pipeline_id, epoch) } + + fn web_render_stacking_level(&self) -> webrender_traits::StackingLevel { + match self.context_type { + StackingContextType::Real | StackingContextType::PseudoPositioned => + webrender_traits::StackingLevel::PositionedContent, + StackingContextType::PseudoFloat => webrender_traits::StackingLevel::Floats, + } + } } -impl WebRenderDisplayListConverter for Box { +impl WebRenderDisplayListConverter for DisplayList { fn convert_to_webrender(&self, api: &webrender_traits::RenderApi, pipeline_id: webrender_traits::PipelineId, - epoch: webrender_traits::Epoch) -> webrender_traits::DisplayListBuilder { - let mut builder = webrender_traits::DisplayListBuilder::new(); + epoch: webrender_traits::Epoch, + scroll_layer_id: Option) + -> webrender_traits::StackingContextId { + let mut traversal = DisplayListTraversal { + display_list: self, + current_item_index: 0, + last_item_index: self.list.len() - 1, + }; - for item in &self.background_and_borders { - item.convert_to_webrender(api, - pipeline_id, - epoch, - webrender_traits::StackingLevel::BackgroundAndBorders, - &mut builder); - } - - for item in &self.block_backgrounds_and_borders { - item.convert_to_webrender(api, - pipeline_id, - epoch, - webrender_traits::StackingLevel::BlockBackgroundAndBorders, - &mut builder); - } - - for item in &self.floats { - item.convert_to_webrender(api, - pipeline_id, - epoch, - webrender_traits::StackingLevel::Floats, - &mut builder); - } - - for item in &self.content { - item.convert_to_webrender(api, - pipeline_id, - epoch, - webrender_traits::StackingLevel::Content, - &mut builder); - } - - for item in &self.positioned_content { - item.convert_to_webrender(api, - pipeline_id, - epoch, - webrender_traits::StackingLevel::PositionedContent, - &mut builder); - } - - for item in &self.outlines { - item.convert_to_webrender(api, - pipeline_id, - epoch, - webrender_traits::StackingLevel::Outlines, - &mut builder); - } - - builder + self.root_stacking_context.convert_to_webrender(&mut traversal, + api, + pipeline_id, + epoch, + scroll_layer_id) } } impl WebRenderDisplayItemConverter for DisplayItem { fn convert_to_webrender(&self, - api: &webrender_traits::RenderApi, - pipeline_id: webrender_traits::PipelineId, - epoch: webrender_traits::Epoch, level: webrender_traits::StackingLevel, builder: &mut webrender_traits::DisplayListBuilder) { match *self { @@ -469,13 +511,6 @@ impl WebRenderDisplayItemConverter for DisplayItem { item.base.clip.to_clip_region(), pipeline_id); } - DisplayItem::StackingContextClass(ref item) => { - let stacking_context_id = item.convert_to_webrender(api, - pipeline_id, - epoch, - None); - builder.push_stacking_context(level, stacking_context_id); - } } } } diff --git a/servo/components/profile/time.rs b/servo/components/profile/time.rs index 5bb2a0cc0dc8..a71648307188 100644 --- a/servo/components/profile/time.rs +++ b/servo/components/profile/time.rs @@ -58,6 +58,7 @@ impl Formattable for ProfilerCategory { ProfilerCategory::LayoutRestyleDamagePropagation | ProfilerCategory::LayoutNonIncrementalReset | ProfilerCategory::LayoutGeneratedContent | + ProfilerCategory::LayoutDisplayListSorting | ProfilerCategory::LayoutMain | ProfilerCategory::LayoutDispListBuild | ProfilerCategory::LayoutDamagePropagate | @@ -79,6 +80,7 @@ impl Formattable for ProfilerCategory { ProfilerCategory::LayoutSelectorMatch => "Selector Matching", ProfilerCategory::LayoutTreeBuilder => "Tree Building", ProfilerCategory::LayoutDamagePropagate => "Damage Propagation", + ProfilerCategory::LayoutDisplayListSorting => "Sorting Display List", ProfilerCategory::LayoutGeneratedContent => "Generated Content Resolution", ProfilerCategory::LayoutMain => "Primary Layout Pass", ProfilerCategory::LayoutParallelWarmup => "Parallel Warmup", diff --git a/servo/components/profile_traits/time.rs b/servo/components/profile_traits/time.rs index 925ebe64c3df..d0adf2cb0d99 100644 --- a/servo/components/profile_traits/time.rs +++ b/servo/components/profile_traits/time.rs @@ -48,6 +48,7 @@ pub enum ProfilerCategory { LayoutTreeBuilder, LayoutDamagePropagate, LayoutGeneratedContent, + LayoutDisplayListSorting, LayoutMain, LayoutParallelWarmup, LayoutDispListBuild, diff --git a/servo/components/util/print_tree.rs b/servo/components/util/print_tree.rs index e94385053b94..7a6ac4f0be9a 100644 --- a/servo/components/util/print_tree.rs +++ b/servo/components/util/print_tree.rs @@ -57,3 +57,9 @@ impl PrintTree { } } } + +impl Drop for PrintTree { + fn drop(&mut self) { + self.flush_queued_item("└─"); + } +}