From 564ba5969f1fb7a609e1ffc2a8f1b7ae48744ee8 Mon Sep 17 00:00:00 2001 From: Martin Robinson Date: Mon, 12 Aug 2024 14:15:20 +0200 Subject: [PATCH] layout: Non-auto `z-index` should always make stacking contexts for flex items (#32961) Fixes #32756. Signed-off-by: Martin Robinson --- .../display_list/stacking_context.rs | 13 ++++++-- components/layout_2020/flexbox/layout.rs | 7 ++-- .../fragment_tree/base_fragment.rs | 14 ++++---- components/layout_2020/style_ext.rs | 23 ++++++++++--- tests/wpt/meta/MANIFEST.json | 13 ++++++++ .../flex-item-z-ordering-001.html.ini | 2 -- ...x-items-as-stacking-contexts-001.xhtml.ini | 2 -- .../flexbox-paint-ordering-002.xhtml.ini | 2 -- .../css-flexbox/flex-item-z-ordering-002.html | 32 +++++++++++++++++++ 9 files changed, 87 insertions(+), 21 deletions(-) delete mode 100644 tests/wpt/meta/css/css-flexbox/flex-item-z-ordering-001.html.ini delete mode 100644 tests/wpt/meta/css/css-flexbox/flexbox-items-as-stacking-contexts-001.xhtml.ini delete mode 100644 tests/wpt/meta/css/css-flexbox/flexbox-paint-ordering-002.xhtml.ini create mode 100644 tests/wpt/tests/css/css-flexbox/flex-item-z-ordering-002.html diff --git a/components/layout_2020/display_list/stacking_context.rs b/components/layout_2020/display_list/stacking_context.rs index 2d17f412bb1..0e66debab81 100644 --- a/components/layout_2020/display_list/stacking_context.rs +++ b/components/layout_2020/display_list/stacking_context.rs @@ -315,9 +315,12 @@ pub struct StackingContext { /// The clip chain id of this stacking context if it has one. Used for filter clipping. clip_chain_id: Option, - /// The fragment that established this stacking context. + /// The style of the fragment that established this stacking context. initializing_fragment_style: Option>, + /// The [`FragmentFlags`] of the [`Fragment`] that established this stacking context. + initializing_fragment_flags: FragmentFlags, + /// The type of this stacking context. Used for collecting and sorting. context_type: StackingContextType, @@ -376,6 +379,7 @@ impl StackingContext { spatial_id: wr::SpatialId, clip_chain_id: wr::ClipChainId, initializing_fragment_style: ServoArc, + initializing_fragment_flags: FragmentFlags, context_type: StackingContextType, ) -> Self { // WebRender has two different ways of expressing "no clip." ClipChainId::INVALID should be @@ -390,6 +394,7 @@ impl StackingContext { spatial_id, clip_chain_id, initializing_fragment_style: Some(initializing_fragment_style), + initializing_fragment_flags, context_type, contents: vec![], real_stacking_contexts_and_positioned_stacking_containers: vec![], @@ -404,6 +409,7 @@ impl StackingContext { spatial_id: wr::SpaceAndClipInfo::root_scroll(wr.pipeline_id).spatial_id, clip_chain_id: None, initializing_fragment_style: None, + initializing_fragment_flags: FragmentFlags::empty(), context_type: StackingContextType::RealStackingContext, contents: vec![], real_stacking_contexts_and_positioned_stacking_containers: vec![], @@ -433,7 +439,9 @@ impl StackingContext { fn z_index(&self) -> i32 { self.initializing_fragment_style .as_ref() - .map_or(0, |style| style.effective_z_index()) + .map_or(0, |style| { + style.effective_z_index(self.initializing_fragment_flags) + }) } pub(crate) fn sort(&mut self) { @@ -1055,6 +1063,7 @@ impl BoxFragment { containing_block.scroll_node_id.spatial_id, containing_block.clip_chain_id, self.style.clone(), + self.base.flags, context_type, ); self.build_stacking_context_tree_for_children( diff --git a/components/layout_2020/flexbox/layout.rs b/components/layout_2020/flexbox/layout.rs index 77bbfa1120e..f4b19cd4194 100644 --- a/components/layout_2020/flexbox/layout.rs +++ b/components/layout_2020/flexbox/layout.rs @@ -31,7 +31,7 @@ use super::{FlexContainer, FlexItemBox, FlexLevelBox}; use crate::cell::ArcRefCell; use crate::context::LayoutContext; use crate::formatting_contexts::{Baselines, IndependentFormattingContext, IndependentLayout}; -use crate::fragment_tree::{BoxFragment, CollapsedBlockMargins, Fragment}; +use crate::fragment_tree::{BoxFragment, CollapsedBlockMargins, Fragment, FragmentFlags}; use crate::geom::{AuOrAuto, LogicalRect, LogicalSides, LogicalVec2}; use crate::positioned::{AbsolutelyPositionedBox, PositioningContext, PositioningContextLength}; use crate::sizing::ContentSizes; @@ -1522,9 +1522,12 @@ impl InitialFlexLineLayout<'_> { all_baselines.last = Some(item_baseline); } + let mut fragment_info = item.box_.base_fragment_info(); + fragment_info.flags.insert(FragmentFlags::IS_FLEX_ITEM); + ( BoxFragment::new( - item.box_.base_fragment_info(), + fragment_info, item.box_.style().clone(), item_layout_result.fragments, content_rect, diff --git a/components/layout_2020/fragment_tree/base_fragment.rs b/components/layout_2020/fragment_tree/base_fragment.rs index a40174cf05e..626b1e15e69 100644 --- a/components/layout_2020/fragment_tree/base_fragment.rs +++ b/components/layout_2020/fragment_tree/base_fragment.rs @@ -88,20 +88,22 @@ bitflags! { const IS_BODY_ELEMENT_OF_HTML_ELEMENT_ROOT = 1 << 0; /// Whether or not the node that created this Fragment is a `
` element. const IS_BR_ELEMENT = 1 << 1; - /// Whether or not the node that created was a ``, `
` or - /// `` element. Note that this does *not* include elements with - /// `display: table` or `display: table-cell`. - const IS_TABLE_TH_OR_TD_ELEMENT = 1 << 2; + /// Whether or not this Fragment is a flex item. + const IS_FLEX_ITEM = 1 << 2; /// Whether or not this Fragment was created to contain a replaced element or is /// a replaced element. const IS_REPLACED = 1 << 3; + /// Whether or not the node that created was a ``, `
` or + /// `` element. Note that this does *not* include elements with + /// `display: table` or `display: table-cell`. + const IS_TABLE_TH_OR_TD_ELEMENT = 1 << 4; /// Whether or not this Fragment was created to contain a list item marker /// with a used value of `list-style-position: outside`. - const IS_OUTSIDE_LIST_ITEM_MARKER = 1 << 4; + const IS_OUTSIDE_LIST_ITEM_MARKER = 1 << 5; /// Avoid painting the borders, backgrounds, and drop shadow for this fragment, this is used /// for empty table cells when 'empty-cells' is 'hide' and also table wrappers. This flag /// doesn't avoid hit-testing nor does it prevent the painting outlines. - const DO_NOT_PAINT = 1 << 5; + const DO_NOT_PAINT = 1 << 6; } } diff --git a/components/layout_2020/style_ext.rs b/components/layout_2020/style_ext.rs index ad00d31ad03..077a725b733 100644 --- a/components/layout_2020/style_ext.rs +++ b/components/layout_2020/style_ext.rs @@ -244,7 +244,7 @@ pub(crate) trait ComputedValuesExt { containing_block_writing_mode: WritingMode, ) -> LogicalSides>; fn has_transform_or_perspective(&self, fragment_flags: FragmentFlags) -> bool; - fn effective_z_index(&self) -> i32; + fn effective_z_index(&self, fragment_flags: FragmentFlags) -> i32; fn establishes_block_formatting_context(&self) -> bool; fn establishes_stacking_context(&self, fragment_flags: FragmentFlags) -> bool; fn establishes_scroll_container(&self) -> bool; @@ -532,9 +532,14 @@ impl ComputedValuesExt for ComputedValues { /// Get the effective z-index of this fragment. Z-indices only apply to positioned elements /// per CSS 2 9.9.1 (), so this value may differ /// from the value specified in the style. - fn effective_z_index(&self) -> i32 { + fn effective_z_index(&self, fragment_flags: FragmentFlags) -> i32 { + // From : + // > Flex items paint exactly the same as inline blocks [CSS2], except that order-modified + // > document order is used in place of raw document order, and z-index values other than auto + // > create a stacking context even if position is static (behaving exactly as if position + // > were relative). match self.get_box().position { - ComputedPosition::Static => 0, + ComputedPosition::Static if !fragment_flags.contains(FragmentFlags::IS_FLEX_ITEM) => 0, _ => self.get_position().z_index.integer_or(0), } } @@ -597,8 +602,16 @@ impl ComputedValuesExt for ComputedValues { // Statically positioned fragments don't establish stacking contexts if the previous // conditions are not fulfilled. Furthermore, z-index doesn't apply to statically - // positioned fragments. - if self.get_box().position == ComputedPosition::Static { + // positioned fragments (except for flex items, see below). + // + // From : + // > Flex items paint exactly the same as inline blocks [CSS2], except that order-modified + // > document order is used in place of raw document order, and z-index values other than auto + // > create a stacking context even if position is static (behaving exactly as if position + // > were relative). + if self.get_box().position == ComputedPosition::Static && + !fragment_flags.contains(FragmentFlags::IS_FLEX_ITEM) + { return false; } diff --git a/tests/wpt/meta/MANIFEST.json b/tests/wpt/meta/MANIFEST.json index 83d33aac8ea..118f0770a06 100644 --- a/tests/wpt/meta/MANIFEST.json +++ b/tests/wpt/meta/MANIFEST.json @@ -167777,6 +167777,19 @@ {} ] ], + "flex-item-z-ordering-002.html": [ + "349c508e7c544ed31dc73f81e11e9058cf8febdb", + [ + null, + [ + [ + "/css/reference/ref-filled-green-100px-square.xht", + "==" + ] + ], + {} + ] + ], "flex-lines": { "multi-line-wrap-reverse-column-reverse.html": [ "48c18fc5362af607854e03fc79fab9abc408fe36", diff --git a/tests/wpt/meta/css/css-flexbox/flex-item-z-ordering-001.html.ini b/tests/wpt/meta/css/css-flexbox/flex-item-z-ordering-001.html.ini deleted file mode 100644 index f84b7757cb6..00000000000 --- a/tests/wpt/meta/css/css-flexbox/flex-item-z-ordering-001.html.ini +++ /dev/null @@ -1,2 +0,0 @@ -[flex-item-z-ordering-001.html] - expected: FAIL diff --git a/tests/wpt/meta/css/css-flexbox/flexbox-items-as-stacking-contexts-001.xhtml.ini b/tests/wpt/meta/css/css-flexbox/flexbox-items-as-stacking-contexts-001.xhtml.ini deleted file mode 100644 index 1368cdf2367..00000000000 --- a/tests/wpt/meta/css/css-flexbox/flexbox-items-as-stacking-contexts-001.xhtml.ini +++ /dev/null @@ -1,2 +0,0 @@ -[flexbox-items-as-stacking-contexts-001.xhtml] - expected: FAIL diff --git a/tests/wpt/meta/css/css-flexbox/flexbox-paint-ordering-002.xhtml.ini b/tests/wpt/meta/css/css-flexbox/flexbox-paint-ordering-002.xhtml.ini deleted file mode 100644 index 4b1519eda79..00000000000 --- a/tests/wpt/meta/css/css-flexbox/flexbox-paint-ordering-002.xhtml.ini +++ /dev/null @@ -1,2 +0,0 @@ -[flexbox-paint-ordering-002.xhtml] - expected: FAIL diff --git a/tests/wpt/tests/css/css-flexbox/flex-item-z-ordering-002.html b/tests/wpt/tests/css/css-flexbox/flex-item-z-ordering-002.html new file mode 100644 index 00000000000..349c508e7c5 --- /dev/null +++ b/tests/wpt/tests/css/css-flexbox/flex-item-z-ordering-002.html @@ -0,0 +1,32 @@ + +Flex painting order with z-index + + + + + + + +

Test passes if there is a filled green square and no red. +

+ +
+
+
+