LibWeb: Paint relatively positioned inline-level elements

Since we deliberately skip positioned elements in paint_descendants(),
we have to make sure we actually paint them in the subsequent
paint_internal() pass.

Before this change, we were only painting positioned elements whose
paintable was a PaintableBox, neglecting inline-level relpos elements.
This commit is contained in:
Andreas Kling 2023-07-14 10:03:14 +02:00
parent 268492413a
commit 2d6c1bbf88
Notes: sideshowbarker 2024-07-16 23:51:07 +09:00
2 changed files with 40 additions and 16 deletions

View file

@ -83,6 +83,28 @@ public:
return TraversalDecision::Continue;
}
template<typename Callback>
TraversalDecision for_each_in_inclusive_subtree(Callback callback) const
{
if (auto decision = callback(*this); decision != TraversalDecision::Continue)
return decision;
for (auto* child = first_child(); child; child = child->next_sibling()) {
if (child->for_each_in_inclusive_subtree(callback) == TraversalDecision::Break)
return TraversalDecision::Break;
}
return TraversalDecision::Continue;
}
template<typename Callback>
TraversalDecision for_each_in_subtree(Callback callback) const
{
for (auto* child = first_child(); child; child = child->next_sibling()) {
if (child->for_each_in_inclusive_subtree(callback) == TraversalDecision::Break)
return TraversalDecision::Break;
}
return TraversalDecision::Continue;
}
virtual void paint(PaintContext&, PaintPhase) const { }
virtual void before_children_paint(PaintContext&, PaintPhase) const { }

View file

@ -182,38 +182,40 @@ void StackingContext::paint_internal(PaintContext& context) const
// Draw positioned descendants with z-index `0` or `auto` in tree order. (step 8)
// NOTE: Non-positioned descendants that establish stacking contexts with z-index `0` or `auto` are also painted here.
// FIXME: There's more to this step that we have yet to understand and implement.
m_box->paintable_box()->for_each_in_subtree_of_type<PaintableBox>([&](PaintableBox const& paintable_box) {
auto const& z_index = paintable_box.computed_values().z_index();
if (auto* child = paintable_box.stacking_context()) {
m_box->paintable_box()->for_each_in_subtree([&](Paintable const& paintable) {
auto const& layout_node = paintable.layout_node();
auto const& z_index = paintable.computed_values().z_index();
if (auto const* child = paintable.stacking_context_rooted_here()) {
if (!z_index.has_value() || z_index.value() == 0)
paint_child(child);
return TraversalDecision::SkipChildrenAndContinue;
}
if (z_index.has_value() && z_index.value() != 0)
return TraversalDecision::Continue;
if (!paintable_box.layout_box().is_positioned())
if (!layout_node.is_positioned())
return TraversalDecision::Continue;
// At this point, `paintable_box` is a positioned descendant with z-index: auto
// but no stacking context of its own.
// FIXME: This is basically duplicating logic found elsewhere in this same function. Find a way to make this more elegant.
auto parent = paintable_box.layout_node().parent();
auto parent = layout_node.parent();
auto* parent_paintable = parent ? parent->paintable() : nullptr;
if (parent_paintable)
parent_paintable->before_children_paint(context, PaintPhase::Foreground);
auto containing_block = paintable_box.layout_node().containing_block();
auto containing_block = layout_node.containing_block();
auto* containing_block_paintable = containing_block ? containing_block->paintable() : nullptr;
if (containing_block_paintable)
containing_block_paintable->apply_clip_overflow_rect(context, PaintPhase::Foreground);
paint_node(paintable_box.layout_box(), context, PaintPhase::Background);
paint_node(paintable_box.layout_box(), context, PaintPhase::Border);
paint_descendants(context, paintable_box.layout_box(), StackingContextPaintPhase::BackgroundAndBorders);
paint_descendants(context, paintable_box.layout_box(), StackingContextPaintPhase::Floats);
paint_descendants(context, paintable_box.layout_box(), StackingContextPaintPhase::BackgroundAndBordersForInlineLevelAndReplaced);
paint_node(paintable_box.layout_box(), context, PaintPhase::Foreground);
paint_descendants(context, paintable_box.layout_box(), StackingContextPaintPhase::Foreground);
paint_node(paintable_box.layout_box(), context, PaintPhase::FocusOutline);
paint_node(paintable_box.layout_box(), context, PaintPhase::Overlay);
paint_descendants(context, paintable_box.layout_box(), StackingContextPaintPhase::FocusAndOverlay);
paint_node(layout_node, context, PaintPhase::Background);
paint_node(layout_node, context, PaintPhase::Border);
paint_descendants(context, layout_node, StackingContextPaintPhase::BackgroundAndBorders);
paint_descendants(context, layout_node, StackingContextPaintPhase::Floats);
paint_descendants(context, layout_node, StackingContextPaintPhase::BackgroundAndBordersForInlineLevelAndReplaced);
paint_node(layout_node, context, PaintPhase::Foreground);
paint_descendants(context, layout_node, StackingContextPaintPhase::Foreground);
paint_node(layout_node, context, PaintPhase::FocusOutline);
paint_node(layout_node, context, PaintPhase::Overlay);
paint_descendants(context, layout_node, StackingContextPaintPhase::FocusAndOverlay);
if (parent_paintable)
parent_paintable->after_children_paint(context, PaintPhase::Foreground);
if (containing_block_paintable)