commit 6699673df2d47a864ad4c5ed4f81086911c68c67
parent 1f82730a85acc22c822dd339c92f074c649fd0ce
Author: sickl8 <sickl8@protonmail.com>
Date: Tue, 2 Dec 2025 14:06:08 +0000
Bug 1960699 - Fix blockification and deblockification of flex/grid items when wrapped inside `display: contents` elements. r=emilio,firefox-style-system-reviewers
- Added new inheritable style builder flag `CASCADE_BEYOND_DISPLAY_CONTENTS` with it's corresponding adjuster function
- Modified tests to also include the case where the `display: contents` wrapper stays the same but the container switches `display` type after the first style resolution
- Moved adjustment code to `set_bits`
- Renamed flag to `DISPLAY_CONTENTS_IN_ITEM_CONTAINER`
Differential Revision: https://phabricator.services.mozilla.com/D274679
Diffstat:
3 files changed, 59 insertions(+), 1 deletion(-)
diff --git a/servo/components/style/properties/computed_value_flags.rs b/servo/components/style/properties/computed_value_flags.rs
@@ -132,6 +132,12 @@ bitflags! {
/// Whether this style considered a scope style rule.
const CONSIDERED_NONTRIVIAL_SCOPED_STYLE = 1 << 26;
+
+ /// Whether this style is that of a `display: contents` element that is either a direct
+ /// child of an item container or another `display: contents` element, the style of which
+ /// has this flag set, marked in order to cascade beyond them to the descendants of the
+ /// the item container that do generate a box.
+ const DIPLAY_CONTENTS_IN_ITEM_CONTAINER = 1 << 27;
}
}
@@ -158,7 +164,9 @@ impl ComputedValueFlags {
/// Flags that may be propagated to descendants.
#[inline]
fn maybe_inherited_flags() -> Self {
- Self::inherited_flags() | Self::SHOULD_SUPPRESS_LINEBREAK
+ Self::inherited_flags()
+ | Self::SHOULD_SUPPRESS_LINEBREAK
+ | Self::DIPLAY_CONTENTS_IN_ITEM_CONTAINER
}
/// Flags that are an input to the cascade.
diff --git a/servo/components/style/style_adjuster.rs b/servo/components/style/style_adjuster.rs
@@ -276,6 +276,18 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> {
self.style
.add_flags(ComputedValueFlags::IS_IN_OPACITY_ZERO_SUBTREE);
}
+ } else if self
+ .style
+ .get_parent_box()
+ .clone_display()
+ .is_item_container()
+ || self
+ .style
+ .get_parent_flags()
+ .contains(ComputedValueFlags::DIPLAY_CONTENTS_IN_ITEM_CONTAINER)
+ {
+ self.style
+ .add_flags(ComputedValueFlags::DIPLAY_CONTENTS_IN_ITEM_CONTAINER);
}
if self.style.pseudo.is_some_and(|p| p.is_first_line()) {
diff --git a/testing/web-platform/tests/css/css-display/display-contents-blockify-dynamic.html b/testing/web-platform/tests/css/css-display/display-contents-blockify-dynamic.html
@@ -15,6 +15,22 @@
<span></span>
</div>
</div>
+<div id="grid-to-block" style="display: grid">
+ <div style="display: contents">
+ <div style="display: contents">
+ <button>button1</button>
+ <button id="deblockified">button2</button>
+ </div>
+ </div>
+</div>
+<div id="block-to-grid" style="display: block">
+ <div style="display: contents">
+ <div style="display: contents">
+ <button>button1</button>
+ <button id="blockified">button2</button>
+ </div>
+ </div>
+</div>
<script>
function display(el) {
return getComputedStyle(el).display;
@@ -31,4 +47,26 @@ test(function() {
assert_equals(display(child), "block", "Grid child should get blockified");
assert_equals(display(grandChild), "inline", "Grid grand-child should get un-blockified when its parent's display stops being `contents`");
}, "Dynamic changes to `display` causing blockification of children are handled correctly");
+
+test(() => {
+ let gridToBlock = document.getElementById("grid-to-block");
+ let itemGrandChild = document.getElementById("deblockified");
+
+ assert_equals(display(gridToBlock), "grid", "Container should be a grid");
+ assert_equals(display(itemGrandChild), "block", "Item should have been blockified");
+ gridToBlock.style.display = "block";
+ assert_equals(display(gridToBlock), "block", "Container should become a block");
+ assert_equals(display(itemGrandChild), "inline-block", "Item should get de-blockified");
+}, "Dynamic changes to `display` from `grid` to `block` should cause children to get de-blockified despite being children of `display: contents` elements");
+
+test(() => {
+ let blockToGrid = document.getElementById("block-to-grid");
+ let itemGrandChild = document.getElementById("blockified");
+
+ assert_equals(display(blockToGrid), "block", "Container should be a block");
+ assert_equals(display(itemGrandChild), "inline-block", "Item should not have been blockified");
+ blockToGrid.style.display = "grid";
+ assert_equals(display(blockToGrid), "grid", "Container should become a grid");
+ assert_equals(display(itemGrandChild), "block", "Item should get blockified");
+}, "Dynamic changes to `display` from `block` to `grid` should cause children to get blockified despite being children of `display: contents` elements")
</script>