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