Browse Source

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 months ago
parent
commit
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());
     VERIFY(!m_clip_rects.is_empty());
     auto rect = m_clip_rects[0].rect;
     auto rect = m_clip_rects[0].rect;
     if (m_clip_rects[0].enclosing_scroll_frame) {
     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) {
     for (size_t i = 1; i < m_clip_rects.size(); ++i) {
         auto clip_rect = m_clip_rects[i].rect;
         auto clip_rect = m_clip_rects[i].rect;
         if (m_clip_rects[i].enclosing_scroll_frame) {
         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);
         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
 CSSPixelPoint ClippableAndScrollable::cumulative_offset_of_enclosing_scroll_frame() const
 {
 {
     if (m_enclosing_scroll_frame)
     if (m_enclosing_scroll_frame)
-        return m_enclosing_scroll_frame->cumulative_offset;
+        return m_enclosing_scroll_frame->cumulative_offset();
     return {};
     return {};
 }
 }
 
 

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

@@ -23,6 +23,7 @@ public:
     [[nodiscard]] CSSPixelPoint cumulative_offset_of_enclosing_scroll_frame() const;
     [[nodiscard]] CSSPixelPoint cumulative_offset_of_enclosing_scroll_frame() const;
     [[nodiscard]] Optional<CSSPixelRect> clip_rect_for_hit_testing() 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]] Optional<int> own_scroll_frame_id() const;
     [[nodiscard]] CSSPixelPoint own_scroll_frame_offset() 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()) {
         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(
             command.visit(
                 [&](auto& command) {
                 [&](auto& command) {
                     if constexpr (requires { command.translate_by(scroll_offset); }) {
                     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;
     virtual void resolve_paint_properties() override;
 
 
+    RefPtr<ScrollFrame const> nearest_scroll_frame() const;
+
 protected:
 protected:
     explicit PaintableBox(Layout::Box const&);
     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> {
 struct ScrollFrame : public RefCounted<ScrollFrame> {
     i32 id { -1 };
     i32 id { -1 };
-    CSSPixelPoint cumulative_offset;
     CSSPixelPoint own_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)) {
         if (paintable_box.has_scrollable_overflow() || is<ViewportPaintable>(paintable_box)) {
             auto scroll_frame = adopt_ref(*new ScrollFrame());
             auto scroll_frame = adopt_ref(*new ScrollFrame());
             scroll_frame->id = next_id++;
             scroll_frame->id = next_id++;
+            scroll_frame->parent = paintable_box.nearest_scroll_frame();
             paintable_box.set_own_scroll_frame(scroll_frame);
             paintable_box.set_own_scroll_frame(scroll_frame);
             scroll_state.set(paintable_box, move(scroll_frame));
             scroll_state.set(paintable_box, move(scroll_frame));
         }
         }
@@ -82,13 +83,13 @@ void ViewportPaintable::assign_scroll_frames()
             return TraversalDecision::Continue;
             return TraversalDecision::Continue;
         }
         }
         for (auto block = paintable.containing_block(); block; block = block->containing_block()) {
         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()) {
                 if (paintable.is_paintable_box()) {
                     auto const& paintable_box = static_cast<PaintableBox const&>(paintable);
                     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()) {
                 } else if (paintable.is_inline_paintable()) {
                     auto const& inline_paintable = static_cast<InlinePaintable const&>(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;
                 return TraversalDecision::Continue;
             }
             }
@@ -162,14 +163,6 @@ void ViewportPaintable::refresh_scroll_state()
     for (auto& it : scroll_state) {
     for (auto& it : scroll_state) {
         auto const& paintable_box = *it.key;
         auto const& paintable_box = *it.key;
         auto& scroll_frame = *it.value;
         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();
         scroll_frame.own_offset = -paintable_box.scroll_offset();
     }
     }
 }
 }