diff --git a/Tests/LibWeb/Ref/abspos-z-index-painting-order.html b/Tests/LibWeb/Ref/abspos-z-index-painting-order.html
new file mode 100644
index 0000000000..7a6c7f9238
--- /dev/null
+++ b/Tests/LibWeb/Ref/abspos-z-index-painting-order.html
@@ -0,0 +1,19 @@
+
hello friends
diff --git a/Tests/LibWeb/Ref/reference/abspos-z-index-painting-order-ref.html b/Tests/LibWeb/Ref/reference/abspos-z-index-painting-order-ref.html
new file mode 100644
index 0000000000..8b5c27fbea
--- /dev/null
+++ b/Tests/LibWeb/Ref/reference/abspos-z-index-painting-order-ref.html
@@ -0,0 +1,18 @@
+
hello friends
diff --git a/Userland/Libraries/LibWeb/Painting/StackingContext.cpp b/Userland/Libraries/LibWeb/Painting/StackingContext.cpp
index f9acba66be..521611ebd8 100644
--- a/Userland/Libraries/LibWeb/Painting/StackingContext.cpp
+++ b/Userland/Libraries/LibWeb/Painting/StackingContext.cpp
@@ -96,32 +96,14 @@ void StackingContext::paint_descendants(PaintContext& context, Paintable const&
paintable.for_each_child([&context, phase](auto& child) {
auto* stacking_context = child.stacking_context_rooted_here();
-
- if (child.is_positioned()) {
- // If `child` is positioned with a z-index of `0` or `auto`, skip over it.
- auto const& z_index = child.computed_values().z_index();
- if (!z_index.has_value() || z_index.value() == 0)
- return;
-
- // Skip positioned children with stacking contexts, these are handled in paint_internal().
- if (stacking_context)
- return;
- }
-
- if (stacking_context) {
- // FIXME: This may not be fully correct with respect to the paint phases.
- if (phase == StackingContextPaintPhase::Foreground)
- paint_child(context, *stacking_context);
- // Note: Don't further recuse into descendants as paint_child() will do that.
- return;
- }
+ auto const& z_index = child.computed_values().z_index();
// NOTE: Grid specification https://www.w3.org/TR/css-grid-2/#z-order says that grid items should be treated
// the same way as CSS2 defines for inline-blocks:
// "For each one of these, treat the element as if it created a new stacking context, but any positioned
// descendants and descendants which actually create a new stacking context should be considered part of
// the parent stacking context, not this new one."
- auto should_be_treated_as_stacking_context = child.layout_node().is_grid_item();
+ auto should_be_treated_as_stacking_context = child.layout_node().is_grid_item() && !z_index.has_value();
if (should_be_treated_as_stacking_context) {
// FIXME: This may not be fully correct with respect to the paint phases.
if (phase == StackingContextPaintPhase::Foreground)
@@ -129,6 +111,20 @@ void StackingContext::paint_descendants(PaintContext& context, Paintable const&
return;
}
+ if (stacking_context && z_index.has_value())
+ return;
+ if (child.is_positioned() && !z_index.has_value())
+ return;
+
+ if (stacking_context) {
+ // FIXME: This may not be fully correct with respect to the paint phases.
+ if (phase == StackingContextPaintPhase::Foreground) {
+ paint_child(context, *stacking_context);
+ }
+ // Note: Don't further recurse into descendants as paint_child() will do that.
+ return;
+ }
+
bool child_is_inline_or_replaced = child.is_inline() || is(child);
switch (phase) {
case StackingContextPaintPhase::BackgroundAndBorders:
@@ -207,9 +203,8 @@ void StackingContext::paint_internal(PaintContext& context) const
// Stacking contexts formed by positioned descendants with negative z-indices (excluding 0) in z-index order
// (most negative first) then tree order. (step 3)
+ // NOTE: This doesn't check if a descendant is positioned as modern CSS allows for alternative methods to establish stacking contexts.
for (auto* child : m_children) {
- if (!child->paintable_box().is_positioned())
- continue;
if (child->paintable_box().computed_values().z_index().has_value() && child->paintable_box().computed_values().z_index().value() < 0)
paint_child(context, *child);
}
@@ -270,10 +265,8 @@ void StackingContext::paint_internal(PaintContext& context) const
// Stacking contexts formed by positioned descendants with z-indices greater than or equal to 1 in z-index order
// (smallest first) then tree order. (Step 9)
+ // NOTE: This doesn't check if a descendant is positioned as modern CSS allows for alternative methods to establish stacking contexts.
for (auto* child : m_children) {
- if (!child->paintable_box().is_positioned())
- continue;
-
PaintableBox const* nearest_scrollable_ancestor = child->paintable_box().nearest_scrollable_ancestor_within_stacking_context();
if (nearest_scrollable_ancestor)