Bläddra i källkod

LibWeb: Use the StackingContext tree for hit testing

This makes it possible to click links that are above other content due
to stacking context order (e.g via CSS z-index.)
Andreas Kling 5 år sedan
förälder
incheckning
392b055806

+ 2 - 0
Libraries/LibWeb/Layout/LayoutBlock.cpp

@@ -725,6 +725,8 @@ HitTestResult LayoutBlock::hit_test(const Gfx::IntPoint& position) const
     HitTestResult last_good_candidate;
     for (auto& line_box : m_line_boxes) {
         for (auto& fragment : line_box.fragments()) {
+            if (is<LayoutBox>(fragment.layout_node()) && to<LayoutBox>(fragment.layout_node()).stacking_context())
+                continue;
             if (enclosing_int_rect(fragment.absolute_rect()).contains(position)) {
                 if (fragment.layout_node().is_block())
                     return to<LayoutBlock>(fragment.layout_node()).hit_test(position);

+ 2 - 0
Libraries/LibWeb/Layout/LayoutBox.cpp

@@ -231,6 +231,8 @@ HitTestResult LayoutBox::hit_test(const Gfx::IntPoint& position) const
     //        m_rect.contains() since inline text rects can't be trusted..
     HitTestResult result { absolute_rect().contains(position.x(), position.y()) ? this : nullptr };
     for_each_child([&](auto& child) {
+        if (is<LayoutBox>(child) && to<LayoutBox>(child).stacking_context())
+            return;
         auto child_result = child.hit_test(position);
         if (child_result.layout_node)
             result = child_result;

+ 1 - 0
Libraries/LibWeb/Layout/LayoutBox.h

@@ -64,6 +64,7 @@ public:
 
     bool establishes_stacking_context() const;
     StackingContext* stacking_context() { return m_stacking_context; }
+    const StackingContext* stacking_context() const { return m_stacking_context; }
     void set_stacking_context(NonnullOwnPtr<StackingContext> context) { m_stacking_context = move(context); }
     StackingContext* enclosing_stacking_context();
 

+ 5 - 0
Libraries/LibWeb/Layout/LayoutDocument.cpp

@@ -113,4 +113,9 @@ void LayoutDocument::paint(PaintContext& context, PaintPhase phase)
     stacking_context()->paint(context, phase);
 }
 
+HitTestResult LayoutDocument::hit_test(const Gfx::IntPoint& position) const
+{
+    return stacking_context()->hit_test(position);
+}
+
 }

+ 2 - 1
Libraries/LibWeb/Layout/LayoutDocument.h

@@ -41,9 +41,10 @@ public:
     virtual void layout(LayoutMode = LayoutMode::Default) override;
 
     void paint_all_phases(PaintContext&);
-
     virtual void paint(PaintContext&, PaintPhase) override;
 
+    virtual HitTestResult hit_test(const Gfx::IntPoint&) const override;
+
     const LayoutRange& selection() const { return m_selection; }
     LayoutRange& selection() { return m_selection; }
 

+ 4 - 0
Libraries/LibWeb/Layout/LayoutNode.cpp

@@ -106,6 +106,10 @@ HitTestResult LayoutNode::hit_test(const Gfx::IntPoint& position) const
 {
     HitTestResult result;
     for_each_child([&](auto& child) {
+        // Skip over children that establish their own stacking context.
+        // The outer loop who called us will take care of those.
+        if (is<LayoutBox>(child) && to<LayoutBox>(child).stacking_context())
+            return;
         auto child_result = child.hit_test(position);
         if (child_result.layout_node)
             result = child_result;

+ 19 - 0
Libraries/LibWeb/Painting/StackingContext.cpp

@@ -61,6 +61,25 @@ void StackingContext::paint(PaintContext& context, LayoutNode::PaintPhase phase)
     }
 }
 
+HitTestResult StackingContext::hit_test(const Gfx::IntPoint& position) const
+{
+    HitTestResult result;
+    if (!m_box.is_root()) {
+        result = m_box.hit_test(position);
+    } else {
+        // NOTE: LayoutDocument::hit_test() merely calls StackingContext::hit_test()
+        //       so we call its base class instead.
+        result = to<LayoutDocument>(m_box).LayoutBlock::hit_test(position);
+    }
+
+    for (auto* child : m_children) {
+        auto result_here = child->hit_test(position);
+        if (result_here.layout_node)
+            result = result_here;
+    }
+    return result;
+}
+
 void StackingContext::dump(int indent) const
 {
     for (int i = 0; i < indent; ++i)

+ 1 - 0
Libraries/LibWeb/Painting/StackingContext.h

@@ -41,6 +41,7 @@ public:
     const StackingContext* parent() const { return m_parent; }
 
     void paint(PaintContext&, LayoutNode::PaintPhase);
+    HitTestResult hit_test(const Gfx::IntPoint&) const;
 
     void dump(int indent = 0) const;