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".
This commit is contained in:
Aliaksandr Kalenik 2024-08-24 19:11:01 +02:00 committed by Alexander Kalenik
parent 2565757c7a
commit 866608532a
Notes: github-actions[bot] 2024-08-30 17:04:01 +00:00
8 changed files with 30 additions and 16 deletions

View file

@ -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);
}

View file

@ -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 {};
}

View file

@ -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
{

View file

@ -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); }) {

View file

@ -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;
}
}

View file

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

View file

@ -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;
}
};
}

View file

@ -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();
}
}