diff --git a/Tests/LibWeb/Text/input/hit_testing/positioned-z-index-0-and-floats.html b/Tests/LibWeb/Text/input/hit_testing/positioned-z-index-0-and-floats.html
new file mode 100644
index 0000000000..baaae3c755
--- /dev/null
+++ b/Tests/LibWeb/Text/input/hit_testing/positioned-z-index-0-and-floats.html
@@ -0,0 +1,66 @@
+
+
+
+
diff --git a/Userland/Libraries/LibWeb/Painting/StackingContext.cpp b/Userland/Libraries/LibWeb/Painting/StackingContext.cpp
index 4a63d1a3c3..8152049016 100644
--- a/Userland/Libraries/LibWeb/Painting/StackingContext.cpp
+++ b/Userland/Libraries/LibWeb/Painting/StackingContext.cpp
@@ -328,34 +328,6 @@ void StackingContext::paint(PaintContext& context) const
context.recording_painter().restore();
}
-template
-static TraversalDecision for_each_in_inclusive_subtree_within_same_stacking_context_in_reverse(Paintable const& paintable, Callback callback)
-{
- if (paintable.stacking_context()) {
- // Note: Include the stacking context (so we can hit test it), but don't recurse into it.
- if (auto decision = callback(paintable); decision != TraversalDecision::Continue)
- return decision;
- return TraversalDecision::SkipChildrenAndContinue;
- }
- for (auto* child = paintable.last_child(); child; child = child->previous_sibling()) {
- if (for_each_in_inclusive_subtree_within_same_stacking_context_in_reverse(*child, callback) == TraversalDecision::Break)
- return TraversalDecision::Break;
- }
- if (auto decision = callback(paintable); decision != TraversalDecision::Continue)
- return decision;
- return TraversalDecision::Continue;
-}
-
-template
-static TraversalDecision for_each_in_subtree_within_same_stacking_context_in_reverse(Paintable const& paintable, Callback callback)
-{
- for (auto* child = paintable.last_child(); child; child = child->previous_sibling()) {
- if (for_each_in_inclusive_subtree_within_same_stacking_context_in_reverse(*child, callback) == TraversalDecision::Break)
- return TraversalDecision::Break;
- }
- return TraversalDecision::Continue;
-}
-
TraversalDecision StackingContext::hit_test(CSSPixelPoint position, HitTestType type, Function const& callback) const
{
if (!paintable().is_visible())
@@ -389,39 +361,16 @@ TraversalDecision StackingContext::hit_test(CSSPixelPoint position, HitTestType
return TraversalDecision::Break;
}
- bool should_exit = false;
-
// 6. the child stacking contexts with stack level 0 and the positioned descendants with stack level 0.
- for_each_in_subtree_within_same_stacking_context_in_reverse(paintable(), [&](Paintable const& paintable) {
- VERIFY(!should_exit);
- if (!paintable.is_paintable_box())
- return TraversalDecision::Continue;
-
- auto const& paintable_box = verify_cast(paintable);
-
- auto const& z_index = paintable_box.computed_values().z_index();
- auto positioned_element_without_stacking_context = paintable_box.is_positioned() && !paintable_box.stacking_context();
- if (z_index.value_or(0) == 0 && (positioned_element_without_stacking_context || paintable_box.layout_node().is_grid_item())) {
- if (paintable_box.hit_test(transformed_position, type, callback) == TraversalDecision::Break) {
- should_exit = true;
+ for (auto const& paintable : m_positioned_descendants_with_stack_level_0_and_stacking_contexts.in_reverse()) {
+ if (paintable.stacking_context()) {
+ if (paintable.stacking_context()->hit_test(transformed_position, type, callback) == TraversalDecision::Break)
+ return TraversalDecision::Break;
+ } else {
+ if (paintable.hit_test(transformed_position, type, callback) == TraversalDecision::Break)
return TraversalDecision::Break;
- }
}
-
- if (paintable_box.stacking_context()) {
- if (z_index.value_or(0) == 0) {
- if (paintable_box.stacking_context()->hit_test(transformed_position, type, callback) == TraversalDecision::Break) {
- should_exit = true;
- return TraversalDecision::Break;
- }
- }
- }
-
- return TraversalDecision::Continue;
- });
-
- if (should_exit)
- return TraversalDecision::Break;
+ }
// 5. the in-flow, inline-level, non-positioned descendants, including inline tables and inline blocks.
if (paintable().layout_node().children_are_inline() && is(paintable().layout_node())) {
@@ -434,23 +383,10 @@ TraversalDecision StackingContext::hit_test(CSSPixelPoint position, HitTestType
}
// 4. the non-positioned floats.
- for_each_in_subtree_within_same_stacking_context_in_reverse(paintable(), [&](Paintable const& paintable) {
- VERIFY(!should_exit);
- if (!paintable.is_paintable_box())
- return TraversalDecision::Continue;
-
- auto const& paintable_box = verify_cast(paintable);
- if (paintable_box.is_floating()) {
- if (paintable_box.hit_test(transformed_position, type, callback) == TraversalDecision::Break) {
- should_exit = true;
- return TraversalDecision::Break;
- }
- }
- return TraversalDecision::Continue;
- });
-
- if (should_exit)
- return TraversalDecision::Break;
+ for (auto const& paintable : m_non_positioned_floating_descendants.in_reverse()) {
+ if (paintable.hit_test(transformed_position, type, callback) == TraversalDecision::Break)
+ return TraversalDecision::Break;
+ }
// 3. the in-flow, non-inline-level, non-positioned descendants.
if (!paintable().layout_node().children_are_inline()) {
diff --git a/Userland/Libraries/LibWeb/Painting/StackingContext.h b/Userland/Libraries/LibWeb/Painting/StackingContext.h
index 0289573b7d..79bdaa4b4e 100644
--- a/Userland/Libraries/LibWeb/Painting/StackingContext.h
+++ b/Userland/Libraries/LibWeb/Painting/StackingContext.h
@@ -14,6 +14,8 @@
namespace Web::Painting {
class StackingContext {
+ friend class ViewportPaintable;
+
public:
StackingContext(Paintable&, StackingContext* parent, size_t index_in_tree_order);
@@ -50,6 +52,9 @@ private:
Vector m_children;
size_t m_index_in_tree_order { 0 };
+ Vector m_positioned_descendants_with_stack_level_0_and_stacking_contexts;
+ Vector m_non_positioned_floating_descendants;
+
static void paint_child(PaintContext&, StackingContext const&);
void paint_internal(PaintContext&) const;
};
diff --git a/Userland/Libraries/LibWeb/Painting/ViewportPaintable.cpp b/Userland/Libraries/LibWeb/Painting/ViewportPaintable.cpp
index 7f92c60376..d99a74de09 100644
--- a/Userland/Libraries/LibWeb/Painting/ViewportPaintable.cpp
+++ b/Userland/Libraries/LibWeb/Painting/ViewportPaintable.cpp
@@ -38,11 +38,16 @@ void ViewportPaintable::build_stacking_context_tree()
size_t index_in_tree_order = 1;
for_each_in_subtree([&](Paintable const& paintable) {
const_cast(paintable).invalidate_stacking_context();
- if (!paintable.layout_node().establishes_stacking_context()) {
+ auto* parent_context = const_cast(paintable).enclosing_stacking_context();
+ auto establishes_stacking_context = paintable.layout_node().establishes_stacking_context();
+ if ((paintable.is_positioned() || establishes_stacking_context) && paintable.computed_values().z_index().value_or(0) == 0)
+ parent_context->m_positioned_descendants_with_stack_level_0_and_stacking_contexts.append(paintable);
+ if (!paintable.is_positioned() && paintable.is_floating())
+ parent_context->m_non_positioned_floating_descendants.append(paintable);
+ if (!establishes_stacking_context) {
VERIFY(!paintable.stacking_context());
return TraversalDecision::Continue;
}
- auto* parent_context = const_cast(paintable).enclosing_stacking_context();
VERIFY(parent_context);
const_cast(paintable).set_stacking_context(make(const_cast(paintable), parent_context, index_in_tree_order++));
return TraversalDecision::Continue;