1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-27 18:37:34 +00:00

LibWeb: Use offset of nearest scrollable ancestor for positioned boxes

Because positioned descendants are painted out-of-order we need to
separately apply offset of nearest scrollable box for them.

Fixes https://github.com/SerenityOS/serenity/issues/20554
This commit is contained in:
Aliaksandr Kalenik 2023-12-11 16:57:47 +01:00 committed by Andreas Kling
parent 2952f01e84
commit 6b79508c08
5 changed files with 167 additions and 0 deletions

View file

@ -850,4 +850,15 @@ Optional<HitTestResult> PaintableWithLines::hit_test(CSSPixelPoint position, Hit
return {};
}
PaintableBox const* PaintableBox::nearest_scrollable_ancestor() const
{
auto* ancestor = parent();
while (ancestor) {
if (ancestor->is_paintable_box() && static_cast<PaintableBox const*>(ancestor)->has_scrollable_overflow())
return static_cast<PaintableBox const*>(ancestor);
ancestor = ancestor->parent();
}
return nullptr;
}
}

View file

@ -191,6 +191,8 @@ public:
BorderRadiiData const& border_radii_data() const { return m_border_radii_data; }
void set_border_radii_data(BorderRadiiData const& border_radii_data) { m_border_radii_data = border_radii_data; }
PaintableBox const* nearest_scrollable_ancestor() const;
protected:
explicit PaintableBox(Layout::Box const&);

View file

@ -226,6 +226,13 @@ void StackingContext::paint_internal(PaintContext& context) const
: TraversalDecision::Continue;
}
// Apply scroll offset of nearest scrollable ancestor before painting the positioned descendant.
PaintableBox const* nearest_scrollable_ancestor = nullptr;
if (paintable.is_paintable_box())
nearest_scrollable_ancestor = static_cast<PaintableBox const&>(paintable).nearest_scrollable_ancestor();
if (nearest_scrollable_ancestor)
nearest_scrollable_ancestor->apply_scroll_offset(context, PaintPhase::Foreground);
// At this point, `paintable_box` is a positioned descendant with z-index: auto.
// FIXME: This is basically duplicating logic found elsewhere in this same function. Find a way to make this more elegant.
auto exit_decision = TraversalDecision::Continue;
@ -247,6 +254,9 @@ void StackingContext::paint_internal(PaintContext& context) const
if (containing_block_paintable)
containing_block_paintable->clear_clip_overflow_rect(context, PaintPhase::Foreground);
if (nearest_scrollable_ancestor)
nearest_scrollable_ancestor->reset_scroll_offset(context, PaintPhase::Foreground);
return exit_decision;
});