Auto merge of #25786 - mrobinson:stacking-contexts-v2, r=SimonSapin

Split stacking context fragments into sections in layout_2020

This allows rendering stacking context content in the proper order. We
also need to add a new pseudo stacking context for atomic inlines in
order to preserve proper rendering order in some cases.

<!-- Please describe your changes on the following line: -->

---
<!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `___` with appropriate data: -->
- [x] `./mach build -d` does not report any errors
- [x] `./mach test-tidy` does not report any errors
- [ ] These changes fix #___ (GitHub issue number if applicable)

<!-- Either: -->
- [x] There are tests for these changes OR
- [ ] These changes do not require tests because ___

<!-- Also, please make sure that "Allow edits from maintainers" checkbox is checked, so that we can help you if you get stuck somewhere along the way.-->

<!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. -->
This commit is contained in:
bors-servo 2020-02-17 10:20:36 -05:00 committed by GitHub
commit 5597ccf57d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 80 additions and 33 deletions

View file

@ -14,20 +14,38 @@ use style::computed_values::overflow_x::T as ComputedOverflow;
use style::computed_values::position::T as ComputedPosition;
use style::computed_values::transform_style::T as ComputedTransformStyle;
use style::values::computed::Length;
use style::values::specified::box_::DisplayOutside;
use webrender_api::units::LayoutVector2D;
use webrender_api::{ExternalScrollId, ScrollSensitivity, SpaceAndClipInfo, SpatialId};
#[derive(Clone, Copy, Eq, Ord, PartialEq, PartialOrd)]
pub(crate) enum StackingContextSection {
BackgroundsAndBorders,
BlockBackgroundsAndBorders,
Content,
}
pub(crate) struct StackingContextFragment<'a> {
space_and_clip: SpaceAndClipInfo,
section: StackingContextSection,
containing_block: PhysicalRect<Length>,
fragment: &'a Fragment,
}
impl<'a> StackingContextFragment<'a> {
fn build_display_list(&self, builder: &mut DisplayListBuilder) {
builder.current_space_and_clip = self.space_and_clip;
self.fragment
.build_display_list(builder, &self.containing_block);
}
}
#[derive(Clone, Copy, Eq, PartialEq)]
pub(crate) enum StackingContextType {
Real,
PseudoPositioned,
PseudoFloat,
PseudoAtomicInline,
}
pub(crate) struct StackingContext<'a> {
@ -59,7 +77,9 @@ impl<'a> StackingContext<'a> {
}
}
pub(crate) fn sort_stacking_contexts(&mut self) {
pub(crate) fn sort(&mut self) {
self.fragments.sort_by(|a, b| a.section.cmp(&b.section));
self.stacking_contexts.sort_by(|a, b| {
if a.z_index != 0 || b.z_index != 0 {
return a.z_index.cmp(&b.z_index);
@ -79,12 +99,16 @@ impl<'a> StackingContext<'a> {
pub(crate) fn build_display_list(&'a self, builder: &'a mut DisplayListBuilder) {
// Properly order display items that make up a stacking context. "Steps" here
// refer to the steps in CSS 2.1 Appendix E.
//
// TODO(mrobinson): The fragment content of the stacking context needs to be
// organized or sorted into the different sections according to the appropriate
// paint order.
// Step 3: Positioned descendants with negative z-indices.
// Steps 1 and 2: Borders and background for the root
let mut child_fragments = self.fragments.iter().peekable();
while child_fragments.peek().map_or(false, |child| {
child.section == StackingContextSection::BackgroundsAndBorders
}) {
child_fragments.next().unwrap().build_display_list(builder);
}
// Step 3: Positioned descendants with negative z-indices
let mut child_stacking_contexts = self.stacking_contexts.iter().peekable();
while child_stacking_contexts
.peek()
@ -94,21 +118,27 @@ impl<'a> StackingContext<'a> {
child_context.build_display_list(builder);
}
// Step 4: Block backgrounds and borders.
for child in &self.fragments {
builder.current_space_and_clip = child.space_and_clip;
child
.fragment
.build_display_list(builder, &child.containing_block);
// Step 4: Block backgrounds and borders
while child_fragments.peek().map_or(false, |child| {
child.section == StackingContextSection::BlockBackgroundsAndBorders
}) {
child_fragments.next().unwrap().build_display_list(builder);
}
// Step 5: Floats.
// Step 5: Floats
for child_context in &self.float_stacking_contexts {
child_context.build_display_list(builder);
}
// Step 6: Content
while child_fragments.peek().map_or(false, |child| {
child.section == StackingContextSection::Content
}) {
child_fragments.next().unwrap().build_display_list(builder);
}
// Step 7, 8 & 9: Inlines that generate stacking contexts and positioned
// descendants with nonnegative, numeric z-indices.
// descendants with nonnegative, numeric z-indices
for child_context in child_stacking_contexts {
child_context.build_display_list(builder);
}
@ -134,6 +164,7 @@ impl Fragment {
},
Fragment::Text(_) | Fragment::Image(_) => {
stacking_context.fragments.push(StackingContextFragment {
section: StackingContextSection::Content,
space_and_clip: builder.current_space_and_clip,
containing_block: *containing_block,
fragment: self,
@ -149,17 +180,34 @@ impl BoxFragment {
return Some(StackingContextType::Real);
}
if self.style.get_box().position != ComputedPosition::Static {
let box_style = &self.style.get_box();
if box_style.position != ComputedPosition::Static {
return Some(StackingContextType::PseudoPositioned);
}
if self.style.get_box().float != ComputedFloat::None {
if box_style.float != ComputedFloat::None {
return Some(StackingContextType::PseudoFloat);
}
if box_style.display.is_atomic_inline_level() {
return Some(StackingContextType::PseudoAtomicInline);
}
None
}
fn get_stacking_context_section(&self) -> StackingContextSection {
if self.get_stacking_context_type().is_some() {
return StackingContextSection::BackgroundsAndBorders;
}
if self.style.get_box().display.outside() == DisplayOutside::Inline {
return StackingContextSection::Content;
}
StackingContextSection::BlockBackgroundsAndBorders
}
/// Returns true if this fragment establishes a new stacking context and false otherwise.
fn establishes_stacking_context(&self) -> bool {
if self.style.get_effects().opacity != 1.0 {
@ -250,19 +298,21 @@ impl BoxFragment {
&mut child_stacking_context,
);
if context_type == StackingContextType::Real {
child_stacking_context.sort_stacking_contexts();
stacking_context
.stacking_contexts
.push(child_stacking_context);
} else {
let mut children =
mem::replace(&mut child_stacking_context.stacking_contexts, Vec::new());
stacking_context
.stacking_contexts
.push(child_stacking_context);
stacking_context.stacking_contexts.append(&mut children);
let mut stolen_children = vec![];
if context_type != StackingContextType::Real {
stolen_children = mem::replace(
&mut child_stacking_context.stacking_contexts,
stolen_children,
);
}
child_stacking_context.sort();
stacking_context
.stacking_contexts
.push(child_stacking_context);
stacking_context
.stacking_contexts
.append(&mut stolen_children);
});
}
@ -275,6 +325,7 @@ impl BoxFragment {
) {
stacking_context.fragments.push(StackingContextFragment {
space_and_clip: builder.current_space_and_clip,
section: self.get_stacking_context_section(),
containing_block: *containing_block,
fragment,
});

View file

@ -191,7 +191,7 @@ impl FragmentTreeRoot {
);
}
stacking_context.sort_stacking_contexts();
stacking_context.sort();
stacking_context.build_display_list(builder);
}

View file

@ -1,2 +0,0 @@
[stack-floats-003.xht]
expected: FAIL

View file

@ -1,2 +0,0 @@
[stack-floats-004.xht]
expected: FAIL