瀏覽代碼

LibWeb: Add parent-child relationship between scroll frames

This allows the calculation of the cumulative scroll offset for a scroll
frame by adding its scroll offset to the parent’s scroll offset, rather
than traversing the containing block chain. While it doesn't greatly
simplify calculations for typical scroll frames, it serves as a
preparation for supporting "position: sticky".
Aliaksandr Kalenik 11 月之前
父節點
當前提交
866608532a

+ 2 - 2
Userland/Libraries/LibWeb/Painting/ClipFrame.cpp

@@ -24,12 +24,12 @@ CSSPixelRect ClipFrame::clip_rect_for_hit_testing() const
     VERIFY(!m_clip_rects.is_empty());
     auto rect = m_clip_rects[0].rect;
     if (m_clip_rects[0].enclosing_scroll_frame) {
-        rect.translate_by(m_clip_rects[0].enclosing_scroll_frame->cumulative_offset);
+        rect.translate_by(m_clip_rects[0].enclosing_scroll_frame->cumulative_offset());
     }
     for (size_t i = 1; i < m_clip_rects.size(); ++i) {
         auto clip_rect = m_clip_rects[i].rect;
         if (m_clip_rects[i].enclosing_scroll_frame) {
-            clip_rect.translate_by(m_clip_rects[i].enclosing_scroll_frame->cumulative_offset);
+            clip_rect.translate_by(m_clip_rects[i].enclosing_scroll_frame->cumulative_offset());
         }
         rect.intersect(clip_rect);
     }

+ 1 - 1
Userland/Libraries/LibWeb/Painting/ClippableAndScrollable.cpp

@@ -26,7 +26,7 @@ Optional<int> ClippableAndScrollable::scroll_frame_id() const
 CSSPixelPoint ClippableAndScrollable::cumulative_offset_of_enclosing_scroll_frame() const
 {
     if (m_enclosing_scroll_frame)
-        return m_enclosing_scroll_frame->cumulative_offset;
+        return m_enclosing_scroll_frame->cumulative_offset();
     return {};
 }
 

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

@@ -23,6 +23,7 @@ public:
     [[nodiscard]] CSSPixelPoint cumulative_offset_of_enclosing_scroll_frame() const;
     [[nodiscard]] Optional<CSSPixelRect> clip_rect_for_hit_testing() const;
 
+    [[nodiscard]] RefPtr<ScrollFrame const> own_scroll_frame() const { return m_own_scroll_frame; }
     [[nodiscard]] Optional<int> own_scroll_frame_id() const;
     [[nodiscard]] CSSPixelPoint own_scroll_frame_offset() const
     {

+ 1 - 1
Userland/Libraries/LibWeb/Painting/DisplayList.cpp

@@ -48,7 +48,7 @@ void DisplayListPlayer::execute(DisplayList& display_list)
         }
 
         if (scroll_frame_id.has_value()) {
-            auto const& scroll_offset = scroll_state[scroll_frame_id.value()]->cumulative_offset.to_type<double>().scaled(device_pixels_per_css_pixel).to_type<int>();
+            auto const& scroll_offset = scroll_state[scroll_frame_id.value()]->cumulative_offset().to_type<double>().scaled(device_pixels_per_css_pixel).to_type<int>();
             command.visit(
                 [&](auto& command) {
                     if constexpr (requires { command.translate_by(scroll_offset); }) {

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

@@ -1100,4 +1100,15 @@ void PaintableWithLines::resolve_paint_properties()
     }
 }
 
+RefPtr<ScrollFrame const> PaintableBox::nearest_scroll_frame() const
+{
+    auto const* paintable = this->containing_block();
+    while (paintable) {
+        if (paintable->own_scroll_frame())
+            return paintable->own_scroll_frame();
+        paintable = paintable->containing_block();
+    }
+    return nullptr;
+}
+
 }

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

@@ -210,6 +210,8 @@ public:
 
     virtual void resolve_paint_properties() override;
 
+    RefPtr<ScrollFrame const> nearest_scroll_frame() const;
+
 protected:
     explicit PaintableBox(Layout::Box const&);
 

+ 8 - 1
Userland/Libraries/LibWeb/Painting/ScrollFrame.h

@@ -12,8 +12,15 @@ namespace Web::Painting {
 
 struct ScrollFrame : public RefCounted<ScrollFrame> {
     i32 id { -1 };
-    CSSPixelPoint cumulative_offset;
     CSSPixelPoint own_offset;
+    RefPtr<ScrollFrame const> parent;
+
+    CSSPixelPoint cumulative_offset() const
+    {
+        if (parent)
+            return parent->cumulative_offset() + own_offset;
+        return own_offset;
+    }
 };
 
 }

+ 4 - 11
Userland/Libraries/LibWeb/Painting/ViewportPaintable.cpp

@@ -71,6 +71,7 @@ void ViewportPaintable::assign_scroll_frames()
         if (paintable_box.has_scrollable_overflow() || is<ViewportPaintable>(paintable_box)) {
             auto scroll_frame = adopt_ref(*new ScrollFrame());
             scroll_frame->id = next_id++;
+            scroll_frame->parent = paintable_box.nearest_scroll_frame();
             paintable_box.set_own_scroll_frame(scroll_frame);
             scroll_state.set(paintable_box, move(scroll_frame));
         }
@@ -82,13 +83,13 @@ void ViewportPaintable::assign_scroll_frames()
             return TraversalDecision::Continue;
         }
         for (auto block = paintable.containing_block(); block; block = block->containing_block()) {
-            if (auto scroll_frame = scroll_state.get(block); scroll_frame.has_value()) {
+            if (auto scroll_frame = block->own_scroll_frame(); scroll_frame) {
                 if (paintable.is_paintable_box()) {
                     auto const& paintable_box = static_cast<PaintableBox const&>(paintable);
-                    const_cast<PaintableBox&>(paintable_box).set_enclosing_scroll_frame(scroll_frame.value());
+                    const_cast<PaintableBox&>(paintable_box).set_enclosing_scroll_frame(*scroll_frame);
                 } else if (paintable.is_inline_paintable()) {
                     auto const& inline_paintable = static_cast<InlinePaintable const&>(paintable);
-                    const_cast<InlinePaintable&>(inline_paintable).set_enclosing_scroll_frame(scroll_frame.value());
+                    const_cast<InlinePaintable&>(inline_paintable).set_enclosing_scroll_frame(*scroll_frame);
                 }
                 return TraversalDecision::Continue;
             }
@@ -162,14 +163,6 @@ void ViewportPaintable::refresh_scroll_state()
     for (auto& it : scroll_state) {
         auto const& paintable_box = *it.key;
         auto& scroll_frame = *it.value;
-        CSSPixelPoint cumulative_offset;
-        for (auto const* block = &paintable_box.layout_box(); block; block = block->containing_block()) {
-            auto const& block_paintable_box = *block->paintable_box();
-            cumulative_offset.translate_by(block_paintable_box.scroll_offset());
-            if (block->is_fixed_position())
-                break;
-        }
-        scroll_frame.cumulative_offset = -cumulative_offset;
         scroll_frame.own_offset = -paintable_box.scroll_offset();
     }
 }