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("└─"); + } +}