From 184238c3485d82cbacb79791b69b499a80613703 Mon Sep 17 00:00:00 2001 From: Martin Robinson Date: Fri, 4 Sep 2015 12:44:43 -0700 Subject: [PATCH] Layerize StackingContexts that are on top of layers StackingContexts that should be painted on top of StackingContexts that are already layerized should automatically get their own layer. This will ensure proper painting order. --- components/gfx/display_list/mod.rs | 105 +++++++++++++++++- components/layout/block.rs | 2 +- components/layout/flow.rs | 2 +- components/msg/compositor_msg.rs | 24 +++- tests/ref/basic.list | 1 + tests/ref/stacked_layers.html | 23 ++++ tests/ref/stacked_layers_ref.html | 23 ++++ .../min-width-not-important.html.ini | 5 - 8 files changed, 171 insertions(+), 14 deletions(-) create mode 100644 tests/ref/stacked_layers.html create mode 100644 tests/ref/stacked_layers_ref.html delete mode 100644 tests/wpt/metadata/html/rendering/non-replaced-elements/the-fieldset-element-0/min-width-not-important.html.ini diff --git a/components/gfx/display_list/mod.rs b/components/gfx/display_list/mod.rs index d04576726e6..4c0196b7ee4 100644 --- a/components/gfx/display_list/mod.rs +++ b/components/gfx/display_list/mod.rs @@ -36,6 +36,7 @@ use paint_task::PaintLayer; use smallvec::SmallVec; use std::collections::linked_list::{self, LinkedList}; use std::fmt; +use std::mem; use std::slice::Iter; use std::sync::Arc; use style::computed_values::{border_style, cursor, filter, image_rendering, mix_blend_mode}; @@ -304,7 +305,7 @@ pub struct StackingContext { impl StackingContext { /// Creates a new stacking context. #[inline] - pub fn new(mut display_list: Box, + pub fn new(display_list: Box, bounds: &Rect, overflow: &Rect, z_index: i32, @@ -317,8 +318,7 @@ impl StackingContext { scroll_policy: ScrollPolicy, layer_id: Option) -> StackingContext { - display_list.sort_and_layerize_children(); - StackingContext { + let mut stacking_context = StackingContext { display_list: display_list, bounds: *bounds, overflow: *overflow, @@ -331,6 +331,27 @@ impl StackingContext { scrolls_overflow_area: scrolls_overflow_area, scroll_policy: scroll_policy, layer_id: layer_id, + }; + StackingContextLayerCreator::add_layers_to_preserve_drawing_order(&mut stacking_context); + stacking_context + } + + pub fn create_layered_child(&self, + layer_id: LayerId, + display_list: Box) -> StackingContext { + StackingContext { + display_list: display_list, + bounds: self.bounds.clone(), + overflow: self.overflow.clone(), + z_index: self.z_index, + filters: self.filters.clone(), + blend_mode: self.blend_mode, + transform: Matrix4::identity(), + perspective: Matrix4::identity(), + establishes_3d_context: false, + scrolls_overflow_area: self.scrolls_overflow_area, + scroll_policy: self.scroll_policy, + layer_id: Some(layer_id), } } @@ -646,6 +667,84 @@ impl StackingContext { } } +struct StackingContextLayerCreator { + display_list_for_next_layer: Option>, + all_following_children_need_layers: bool, +} + +impl StackingContextLayerCreator { + fn new() -> StackingContextLayerCreator { + StackingContextLayerCreator { + display_list_for_next_layer: None, + all_following_children_need_layers: false, + } + } + + #[inline] + fn add_layers_to_preserve_drawing_order(stacking_context: &mut StackingContext) { + let mut state = StackingContextLayerCreator::new(); + + // First we need to sort child stacking contexts by z-index, so we can detect + // situations where unlayered ones should be on top of layered ones. + let existing_children = mem::replace(&mut stacking_context.display_list.children, + LinkedList::new()); + let mut sorted_children: SmallVec<[Arc; 8]> = SmallVec::new(); + sorted_children.extend(existing_children.into_iter()); + sorted_children.sort_by(|this, other| this.z_index.cmp(&other.z_index)); + + // FIXME(#7566, mrobinson): This should properly handle unlayered children that are on + // top of unlayered children which have child stacking contexts with layers. + for child_stacking_context in sorted_children.into_iter() { + if state.stacking_context_needs_layer(&child_stacking_context) { + state.add_stacking_context(child_stacking_context, stacking_context); + } else { + stacking_context.display_list.children.push_back(child_stacking_context); + } + } + state.finish_building_current_layer(stacking_context); + } + + #[inline] + fn stacking_context_needs_layer(&mut self, stacking_context: &Arc) -> bool { + self.all_following_children_need_layers || stacking_context.layer_id.is_some() + } + + #[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 next_layer_id = + stacking_context.display_list.layered_children.back().unwrap().id.next_layer_id(); + let child_stacking_context = + Arc::new(stacking_context.create_layered_child(next_layer_id, display_list)); + stacking_context.display_list.layered_children.push_back( + PaintLayer::new(next_layer_id, color::transparent(), child_stacking_context)); + self.all_following_children_need_layers = true; + } + } + + fn add_stacking_context(&mut self, + stacking_context: Arc, + parent_stacking_context: &mut StackingContext) { + if let Some(layer_id) = stacking_context.layer_id { + self.finish_building_current_layer(parent_stacking_context); + parent_stacking_context.display_list.layered_children.push_back( + PaintLayer::new(layer_id, color::transparent(), 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.all_following_children_need_layers = true; + return; + } + + if self.display_list_for_next_layer.is_none() { + self.display_list_for_next_layer = Some(box DisplayList::new()); + } + if let Some(ref mut display_list) = self.display_list_for_next_layer { + display_list.children.push_back(stacking_context); + } + } +} + /// Returns the stacking context in the given tree of stacking contexts with a specific layer ID. pub fn find_stacking_context_with_layer_id(this: &Arc, layer_id: LayerId) -> Option> { diff --git a/components/layout/block.rs b/components/layout/block.rs index a12d3d1bc3a..720b529bca5 100644 --- a/components/layout/block.rs +++ b/components/layout/block.rs @@ -2058,7 +2058,7 @@ impl Flow for BlockFlow { // FIXME(#2010, pcwalton): This is a hack and is totally bogus in the presence of pseudo- // elements. But until we have incremental reflow we can't do better--we recreate the flow // for every DOM node so otherwise we nuke layers on every reflow. - LayerId(self.fragment.node.id() as usize, fragment_index) + LayerId(self.fragment.node.id() as usize, fragment_index, 0) } fn is_absolute_containing_block(&self) -> bool { diff --git a/components/layout/flow.rs b/components/layout/flow.rs index 8629881d8e0..b4a457b341f 100644 --- a/components/layout/flow.rs +++ b/components/layout/flow.rs @@ -361,7 +361,7 @@ pub trait Flow: fmt::Debug + Sync + Send + 'static { #[allow(unsafe_code)] fn layer_id(&self, fragment_id: u32) -> LayerId { let obj = unsafe { mem::transmute::<&&Self, &raw::TraitObject>(&self) }; - LayerId(obj.data as usize, fragment_id) + LayerId(obj.data as usize, fragment_id, 0) } /// Attempts to perform incremental fixup of this flow by replacing its fragment's style with diff --git a/components/msg/compositor_msg.rs b/components/msg/compositor_msg.rs index d07fed3dac6..fc3e46d0945 100644 --- a/components/msg/compositor_msg.rs +++ b/components/msg/compositor_msg.rs @@ -36,19 +36,35 @@ impl FrameTreeId { } #[derive(Clone, PartialEq, Eq, Copy, Hash, Deserialize, Serialize, HeapSizeOf)] -pub struct LayerId(pub usize, pub u32); +pub struct LayerId( + /// A base layer ID, currently derived from DOM element pointer address. + pub usize, + + /// FIXME(#2010, pcwalton): A marker for overflow scroll layers. + pub u32, + + /// A sub ID, which is used for synthesizing new layers for content that + /// belongs on top of this layer. This prevents accidentally making colliding + /// layer ids. + pub u32 +); impl Debug for LayerId { fn fmt(&self, f: &mut Formatter) -> fmt::Result { - let LayerId(a, b) = *self; - write!(f, "Layer({}, {})", a, b) + let LayerId(a, b, c) = *self; + write!(f, "Layer({}, {}, {})", a, b, c) } } impl LayerId { /// FIXME(#2011, pcwalton): This is unfortunate. Maybe remove this in the future. pub fn null() -> LayerId { - LayerId(0, 0) + LayerId(0, 0, 0) + } + + pub fn next_layer_id(&self) -> LayerId { + let LayerId(a, b, sub_id) = *self; + LayerId(a, b, sub_id + 1) } } diff --git a/tests/ref/basic.list b/tests/ref/basic.list index 2c59c96f43f..666d5f309b2 100644 --- a/tests/ref/basic.list +++ b/tests/ref/basic.list @@ -323,6 +323,7 @@ flaky_cpu == linebreak_simple_a.html linebreak_simple_b.html == servo_center_a.html servo_center_ref.html == setattribute_id_restyle_a.html setattribute_id_restyle_b.html == simple_inline_absolute_containing_block_a.html simple_inline_absolute_containing_block_ref.html +== stacked_layers.html stacked_layers_ref.html == stacking_context_overflow_a.html stacking_context_overflow_ref.html == stacking_context_overflow_relative_outline_a.html stacking_context_overflow_relative_outline_ref.html == style_is_in_doc.html style_is_in_doc_ref.html diff --git a/tests/ref/stacked_layers.html b/tests/ref/stacked_layers.html new file mode 100644 index 00000000000..615acb0b3c9 --- /dev/null +++ b/tests/ref/stacked_layers.html @@ -0,0 +1,23 @@ + + + + + + +
+
+
+
+ +
+
+
+
+ + diff --git a/tests/ref/stacked_layers_ref.html b/tests/ref/stacked_layers_ref.html new file mode 100644 index 00000000000..27ec9f85692 --- /dev/null +++ b/tests/ref/stacked_layers_ref.html @@ -0,0 +1,23 @@ + + + + + + +
+
+
+
+ +
+
+
+
+ + diff --git a/tests/wpt/metadata/html/rendering/non-replaced-elements/the-fieldset-element-0/min-width-not-important.html.ini b/tests/wpt/metadata/html/rendering/non-replaced-elements/the-fieldset-element-0/min-width-not-important.html.ini deleted file mode 100644 index 24f32b30a6c..00000000000 --- a/tests/wpt/metadata/html/rendering/non-replaced-elements/the-fieldset-element-0/min-width-not-important.html.ini +++ /dev/null @@ -1,5 +0,0 @@ -[min-width-not-important.html] - type: reftest - reftype: == - refurl: /html/rendering/non-replaced-elements/the-fieldset-element-0/ref.html - expected: FAIL