Преглед на файлове

LibWeb: Implement getBoundingClientRect() for inline paintables

This fixes the issue that occurred when, after clicking an inline
paintable page would always scroll to the top. The problem was that
`scroll_an_element_into_view()` relies on `get_bounding_client_rect()`
to produce the correct scroll position and for inline paintables we
were always returning zero rect before this change.
Aliaksandr Kalenik преди 1 година
родител
ревизия
e464d484c4

+ 2 - 1
Tests/LibWeb/Text/expected/get-bounding-client-rect.txt

@@ -1 +1,2 @@
-   {"x":8,"y":500,"width":784,"height":150,"top":500,"right":792,"bottom":650,"left":8}
+ inline   {"x":8,"y":500,"width":784,"height":150,"top":500,"right":792,"bottom":650,"left":8}
+{"x":8,"y":650,"width":41.296875,"height":17.46875,"top":650,"right":49.296875,"bottom":667.46875,"left":8}

+ 6 - 0
Tests/LibWeb/Text/input/get-bounding-client-rect.html

@@ -9,10 +9,16 @@
     }
 </style>
 <div id="box"></div>
+<a id="inline">inline</a>
 <script src="include.js"></script>
 <script>
     test(() => {
         const rect = document.getElementById("box").getBoundingClientRect();
         println(JSON.stringify(rect));
     });
+
+    test(() => {
+        const rect = document.getElementById("inline").getBoundingClientRect();
+        println(JSON.stringify(rect));
+    });
 </script>

+ 16 - 7
Userland/Libraries/LibWeb/DOM/Element.cpp

@@ -61,6 +61,7 @@
 #include <LibWeb/Layout/Viewport.h>
 #include <LibWeb/Namespace.h>
 #include <LibWeb/Page/Page.h>
+#include <LibWeb/Painting/InlinePaintable.h>
 #include <LibWeb/Painting/PaintableBox.h>
 #include <LibWeb/WebIDL/AbstractOperations.h>
 #include <LibWeb/WebIDL/DOMException.h>
@@ -834,16 +835,24 @@ JS::NonnullGCPtr<Geometry::DOMRect> Element::get_bounding_client_rect() const
 {
     // // NOTE: Ensure that layout is up-to-date before looking at metrics.
     const_cast<Document&>(document()).update_layout();
-
-    // FIXME: Support inline layout nodes as well.
-    auto* paintable_box = this->paintable_box();
-    if (!paintable_box)
-        return Geometry::DOMRect::construct_impl(realm(), 0, 0, 0, 0).release_value_but_fixme_should_propagate_errors();
-
     VERIFY(document().navigable());
     auto viewport_offset = document().navigable()->viewport_scroll_offset();
 
-    return Geometry::DOMRect::create(realm(), paintable_box->absolute_border_box_rect().translated(-viewport_offset.x(), -viewport_offset.y()).to_type<float>());
+    if (auto const* paintable_box = this->paintable_box()) {
+        auto absolute_rect = paintable_box->absolute_border_box_rect();
+        absolute_rect.translate_by(-viewport_offset.x(), -viewport_offset.y());
+        return Geometry::DOMRect::create(realm(), absolute_rect.to_type<float>());
+    }
+
+    if (auto const* paintable = this->paintable(); is<Painting::InlinePaintable>(*paintable)) {
+        auto const& inline_paintable = static_cast<Painting::InlinePaintable const&>(*paintable);
+        auto absolute_rect = inline_paintable.bounding_rect();
+        absolute_rect.translate_by(-viewport_offset.x(), -viewport_offset.y());
+        return Geometry::DOMRect::create(realm(), absolute_rect.to_type<float>());
+    }
+
+    dbgln("FIXME: Failed to get bounding client rect for element ({})", debug_description());
+    return Geometry::DOMRect::construct_impl(realm(), 0, 0, 0, 0).release_value_but_fixme_should_propagate_errors();
 }
 
 // https://drafts.csswg.org/cssom-view/#dom-element-getclientrects

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

@@ -173,4 +173,32 @@ void InlinePaintable::for_each_fragment(Callback callback) const
     }
 }
 
+CSSPixelRect InlinePaintable::bounding_rect() const
+{
+    auto top = CSSPixels::max();
+    auto left = CSSPixels::max();
+    auto right = CSSPixels::min();
+    auto bottom = CSSPixels::min();
+    auto has_fragments = false;
+    for_each_fragment([&](auto const& fragment, bool, bool) {
+        has_fragments = true;
+        auto fragment_absolute_rect = fragment.absolute_rect();
+        if (fragment_absolute_rect.top() < top)
+            top = fragment_absolute_rect.top();
+        if (fragment_absolute_rect.left() < left)
+            left = fragment_absolute_rect.left();
+        if (fragment_absolute_rect.right() > right)
+            right = fragment_absolute_rect.right();
+        if (fragment_absolute_rect.bottom() > bottom)
+            bottom = fragment_absolute_rect.bottom();
+    });
+
+    if (!has_fragments) {
+        // FIXME: This is adhoc, and we should return rect of empty fragment instead.
+        auto containing_block_position_in_absolute_coordinates = containing_block()->paintable_box()->absolute_position();
+        return { containing_block_position_in_absolute_coordinates, { 0, 0 } };
+    }
+    return { left, top, right - left, bottom - top };
+}
+
 }

+ 2 - 0
Userland/Libraries/LibWeb/Painting/InlinePaintable.h

@@ -22,6 +22,8 @@ public:
     Layout::InlineNode const& layout_node() const;
     auto const& box_model() const { return layout_node().box_model(); }
 
+    CSSPixelRect bounding_rect() const;
+
 private:
     InlinePaintable(Layout::InlineNode const&);