mirror of
https://github.com/servo/servo.git
synced 2025-07-22 23:03:42 +01:00
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.
This commit is contained in:
parent
c442132196
commit
184238c348
8 changed files with 171 additions and 14 deletions
|
@ -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<DisplayList>,
|
||||
pub fn new(display_list: Box<DisplayList>,
|
||||
bounds: &Rect<Au>,
|
||||
overflow: &Rect<Au>,
|
||||
z_index: i32,
|
||||
|
@ -317,8 +318,7 @@ impl StackingContext {
|
|||
scroll_policy: ScrollPolicy,
|
||||
layer_id: Option<LayerId>)
|
||||
-> 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<DisplayList>) -> 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<Box<DisplayList>>,
|
||||
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<StackingContext>; 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<StackingContext>) -> 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<StackingContext>,
|
||||
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<StackingContext>, layer_id: LayerId)
|
||||
-> Option<Arc<StackingContext>> {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
23
tests/ref/stacked_layers.html
Normal file
23
tests/ref/stacked_layers.html
Normal file
|
@ -0,0 +1,23 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
.test { float: left; margin-right: 25px; }
|
||||
.box { height: 50px; width: 50px; }
|
||||
.gray { background: rgb(200, 200, 200); }
|
||||
.grayer { background: rgb(80, 80, 80); }
|
||||
.grayest { background: rgb(0, 0, 0); }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="test grayest box">
|
||||
<div class="grayer box" style="margin-left: 10px; margin-top: 10px; position: fixed;"></div>
|
||||
<div class="gray box" style="margin-left: 20px; margin-top: 10px; position: relative; top: 10px; z-index: 5;"> </div>
|
||||
</div>
|
||||
|
||||
<div class="test grayest box">
|
||||
<div class="grayer box" style="margin-left: 10px; margin-top: 10px; position: fixed;"></div>
|
||||
<div class="gray box" style="margin-left: 20px; margin-top: 10px; position: absolute; top: 20px; z-index: 5;"> </div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
23
tests/ref/stacked_layers_ref.html
Normal file
23
tests/ref/stacked_layers_ref.html
Normal file
|
@ -0,0 +1,23 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
.test { float: left; margin-right: 25px; }
|
||||
.box { height: 50px; width: 50px; }
|
||||
.gray { background: rgb(200, 200, 200); }
|
||||
.grayer { background: rgb(80, 80, 80); }
|
||||
.grayest { background: rgb(0, 0, 0); }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="test grayest box">
|
||||
<div class="grayer box" style="margin-left: 10px; margin-top: 10px; position: absolute;"></div>
|
||||
<div class="gray box" style="margin-left: 20px; margin-top: 10px; position: relative; top: 10px; z-index: 5;"></div>
|
||||
</div>
|
||||
|
||||
<div class="test grayest box">
|
||||
<div class="grayer box" style="margin-left: 10px; margin-top: 10px; position: absolute;"></div>
|
||||
<div class="gray box" style="margin-left: 20px; margin-top: 10px; position: absolute; top: 20px; z-index: 5;"></div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -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
|
Loading…
Add table
Add a link
Reference in a new issue