Explorar o código

LibWeb: Use clip rectangles assigned to paintables in hit-testing

This change makes hit-testing more consistent in the handling of hidden
overflow by reusing the same clip-rectangles.

Also, it fixes bugs where the box is visible for hit-testing even
though it is clipped by the hidden overflow of the containing block.
Aliaksandr Kalenik hai 1 ano
pai
achega
16f1962f10

+ 1 - 0
Tests/LibWeb/Text/expected/hit_testing/click-outside-of-box-with-hidden-overflow.txt

@@ -0,0 +1 @@
+    <HTML >

+ 1 - 0
Tests/LibWeb/Text/expected/hit_testing/click-outside-of-box-with-lines-and-hidden-overflow.txt

@@ -0,0 +1 @@
+  Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse accumsan semper massa ut pharetra. Nunc vitae lorem at odio facilisis vehicula nec ut nibh. Duis ornare nibh orci, nec mollis est mollis ac. Aenean ultricies condimentum interdum. In eu elit vestibulum, sollicitudin lectus vel, congue nibh. Morbi vitae nunc in tortor ultrices aliquet sit amet a turpis. Phasellus ut dui sodales, dictum sem ut, efficitur tellus. Etiam eu orci et magna suscipit dignissim nec et sem. Aenean quam erat, euismod eu faucibus et, placerat ut dolor.    <HTML >

+ 23 - 0
Tests/LibWeb/Text/input/hit_testing/click-outside-of-box-with-hidden-overflow.html

@@ -0,0 +1,23 @@
+<script src="../include.js"></script>
+<style>
+    .overflow-hidden {
+        overflow: hidden;
+        border: 1px solid black;
+        width: 200px;
+        height: 200px;
+    }
+
+    #box {
+        width: 400px;
+        height: 400px;
+        background-color: red;
+    }
+</style>
+<div class="overflow-hidden">
+    <div id="box"></div>
+</div>
+<script type="text/javascript">
+    test(() => {
+        printElement(internals.hitTest(300, 300).node);
+    });
+</script>

+ 29 - 0
Tests/LibWeb/Text/input/hit_testing/click-outside-of-box-with-lines-and-hidden-overflow.html

@@ -0,0 +1,29 @@
+<script src="../include.js"></script>
+<style>
+    .overflow-hidden {
+        overflow: hidden;
+        border: 1px solid black;
+        width: 200px;
+        height: 200px;
+    }
+
+    #box {
+        width: 400px;
+        height: 400px;
+    }
+</style>
+<div class="overflow-hidden">
+    <div id="box">
+        Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse accumsan semper massa
+        ut pharetra. Nunc vitae lorem at odio facilisis vehicula nec ut nibh. Duis ornare nibh orci,
+        nec mollis est mollis ac. Aenean ultricies condimentum interdum. In eu elit vestibulum,
+        sollicitudin lectus vel, congue nibh. Morbi vitae nunc in tortor ultrices aliquet sit amet a
+        turpis. Phasellus ut dui sodales, dictum sem ut, efficitur tellus. Etiam eu orci et magna
+        suscipit dignissim nec et sem. Aenean quam erat, euismod eu faucibus et, placerat ut dolor.
+    </div>
+</div>
+<script type="text/javascript">
+    test(() => {
+        printElement(internals.hitTest(300, 300).node);
+    });
+</script>

+ 3 - 0
Userland/Libraries/LibWeb/Painting/InlinePaintable.cpp

@@ -182,6 +182,9 @@ void InlinePaintable::for_each_fragment(Callback callback) const
 
 
 Optional<HitTestResult> InlinePaintable::hit_test(CSSPixelPoint position, HitTestType type) const
 Optional<HitTestResult> InlinePaintable::hit_test(CSSPixelPoint position, HitTestType type) const
 {
 {
+    if (m_clip_rect.has_value() && !m_clip_rect.value().contains(position))
+        return {};
+
     auto position_adjusted_by_scroll_offset = position;
     auto position_adjusted_by_scroll_offset = position;
     if (m_enclosing_scroll_frame_offset.has_value())
     if (m_enclosing_scroll_frame_offset.has_value())
         position_adjusted_by_scroll_offset.translate_by(-m_enclosing_scroll_frame_offset.value());
         position_adjusted_by_scroll_offset.translate_by(-m_enclosing_scroll_frame_offset.value());

+ 6 - 0
Userland/Libraries/LibWeb/Painting/PaintableBox.cpp

@@ -676,6 +676,9 @@ Layout::BlockContainer& PaintableWithLines::layout_box()
 
 
 Optional<HitTestResult> PaintableBox::hit_test(CSSPixelPoint position, HitTestType type) const
 Optional<HitTestResult> PaintableBox::hit_test(CSSPixelPoint position, HitTestType type) const
 {
 {
+    if (clip_rect().has_value() && !clip_rect()->contains(position))
+        return {};
+
     auto position_adjusted_by_scroll_offset = position;
     auto position_adjusted_by_scroll_offset = position;
     if (enclosing_scroll_frame_offset().has_value())
     if (enclosing_scroll_frame_offset().has_value())
         position_adjusted_by_scroll_offset.translate_by(-enclosing_scroll_frame_offset().value());
         position_adjusted_by_scroll_offset.translate_by(-enclosing_scroll_frame_offset().value());
@@ -712,6 +715,9 @@ Optional<HitTestResult> PaintableBox::hit_test(CSSPixelPoint position, HitTestTy
 
 
 Optional<HitTestResult> PaintableWithLines::hit_test(CSSPixelPoint position, HitTestType type) const
 Optional<HitTestResult> PaintableWithLines::hit_test(CSSPixelPoint position, HitTestType type) const
 {
 {
+    if (clip_rect().has_value() && !clip_rect()->contains(position))
+        return {};
+
     auto position_adjusted_by_scroll_offset = position;
     auto position_adjusted_by_scroll_offset = position;
     if (enclosing_scroll_frame_offset().has_value())
     if (enclosing_scroll_frame_offset().has_value())
         position_adjusted_by_scroll_offset.translate_by(-enclosing_scroll_frame_offset().value());
         position_adjusted_by_scroll_offset.translate_by(-enclosing_scroll_frame_offset().value());

+ 1 - 0
Userland/Libraries/LibWeb/Painting/PaintableBox.h

@@ -210,6 +210,7 @@ protected:
     virtual CSSPixelRect compute_absolute_paint_rect() const;
     virtual CSSPixelRect compute_absolute_paint_rect() const;
 
 
     Optional<CSSPixelPoint> enclosing_scroll_frame_offset() const { return m_enclosing_scroll_frame_offset; }
     Optional<CSSPixelPoint> enclosing_scroll_frame_offset() const { return m_enclosing_scroll_frame_offset; }
+    Optional<CSSPixelRect> clip_rect() const { return m_clip_rect; }
 
 
 private:
 private:
     [[nodiscard]] virtual bool is_paintable_box() const final { return true; }
     [[nodiscard]] virtual bool is_paintable_box() const final { return true; }

+ 0 - 28
Userland/Libraries/LibWeb/Painting/StackingContext.cpp

@@ -372,14 +372,6 @@ Optional<HitTestResult> StackingContext::hit_test(CSSPixelPoint position, HitTes
         transformed_position.translate_by(-scroll_offset);
         transformed_position.translate_by(-scroll_offset);
     }
     }
 
 
-    // FIXME: Support more overflow variations.
-    if (paintable().computed_values().overflow_x() == CSS::Overflow::Hidden && paintable().computed_values().overflow_y() == CSS::Overflow::Hidden) {
-        if (paintable().is_paintable_box()) {
-            if (!paintable_box().absolute_border_box_rect().contains(transformed_position.x(), transformed_position.y()))
-                return {};
-        }
-    }
-
     // NOTE: Hit testing basically happens in reverse painting order.
     // NOTE: Hit testing basically happens in reverse painting order.
     // https://www.w3.org/TR/CSS22/visuren.html#z-index
     // https://www.w3.org/TR/CSS22/visuren.html#z-index
 
 
@@ -402,12 +394,6 @@ Optional<HitTestResult> StackingContext::hit_test(CSSPixelPoint position, HitTes
 
 
         auto const& paintable_box = verify_cast<PaintableBox>(paintable);
         auto const& paintable_box = verify_cast<PaintableBox>(paintable);
 
 
-        // FIXME: Support more overflow variations.
-        if (paintable_box.computed_values().overflow_x() == CSS::Overflow::Hidden && paintable_box.computed_values().overflow_y() == CSS::Overflow::Hidden) {
-            if (!paintable_box.absolute_border_box_rect().contains(transformed_position.x(), transformed_position.y()))
-                return TraversalDecision::SkipChildrenAndContinue;
-        }
-
         auto const& z_index = paintable_box.computed_values().z_index();
         auto const& z_index = paintable_box.computed_values().z_index();
         if (z_index.value_or(0) == 0 && paintable_box.is_positioned() && !paintable_box.stacking_context()) {
         if (z_index.value_or(0) == 0 && paintable_box.is_positioned() && !paintable_box.stacking_context()) {
             auto candidate = paintable_box.hit_test(transformed_position, type);
             auto candidate = paintable_box.hit_test(transformed_position, type);
@@ -445,13 +431,6 @@ Optional<HitTestResult> StackingContext::hit_test(CSSPixelPoint position, HitTes
             return TraversalDecision::Continue;
             return TraversalDecision::Continue;
 
 
         auto const& paintable_box = verify_cast<PaintableBox>(paintable);
         auto const& paintable_box = verify_cast<PaintableBox>(paintable);
-
-        // FIXME: Support more overflow variations.
-        if (paintable_box.computed_values().overflow_x() == CSS::Overflow::Hidden && paintable_box.computed_values().overflow_y() == CSS::Overflow::Hidden) {
-            if (!paintable_box.absolute_border_box_rect().contains(transformed_position.x(), transformed_position.y()))
-                return TraversalDecision::SkipChildrenAndContinue;
-        }
-
         if (paintable_box.is_floating()) {
         if (paintable_box.is_floating()) {
             if (auto candidate = paintable_box.hit_test(transformed_position, type); candidate.has_value()) {
             if (auto candidate = paintable_box.hit_test(transformed_position, type); candidate.has_value()) {
                 result = move(candidate);
                 result = move(candidate);
@@ -470,13 +449,6 @@ Optional<HitTestResult> StackingContext::hit_test(CSSPixelPoint position, HitTes
                 return TraversalDecision::Continue;
                 return TraversalDecision::Continue;
 
 
             auto const& paintable_box = verify_cast<PaintableBox>(paintable);
             auto const& paintable_box = verify_cast<PaintableBox>(paintable);
-
-            // FIXME: Support more overflow variations.
-            if (paintable_box.computed_values().overflow_x() == CSS::Overflow::Hidden && paintable_box.computed_values().overflow_y() == CSS::Overflow::Hidden) {
-                if (!paintable_box.absolute_border_box_rect().contains(transformed_position.x(), transformed_position.y()))
-                    return TraversalDecision::SkipChildrenAndContinue;
-            }
-
             if (!paintable_box.is_absolutely_positioned() && !paintable_box.is_floating()) {
             if (!paintable_box.is_absolutely_positioned() && !paintable_box.is_floating()) {
                 if (auto candidate = paintable_box.hit_test(transformed_position, type); candidate.has_value()) {
                 if (auto candidate = paintable_box.hit_test(transformed_position, type); candidate.has_value()) {
                     result = move(candidate);
                     result = move(candidate);