1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-26 08:07:44 +00:00

LibWeb: Make StackingContext point to paint tree instead of layout tree

Eventually we should not need the layout tree for anything when painting
and this code will only look at the paint tree. For now, this is just
another step in that direction.
This commit is contained in:
Andreas Kling 2023-08-19 15:20:45 +02:00
parent 1e0ea45fe5
commit d296992fb3
6 changed files with 86 additions and 100 deletions

View file

@ -56,6 +56,13 @@ class Paintable
public: public:
virtual ~Paintable() = default; virtual ~Paintable() = default;
[[nodiscard]] bool is_positioned() const { return layout_node().is_positioned(); }
[[nodiscard]] bool is_fixed_position() const { return layout_node().is_fixed_position(); }
[[nodiscard]] bool is_absolutely_positioned() const { return layout_node().is_absolutely_positioned(); }
[[nodiscard]] bool is_floating() const { return layout_node().is_floating(); }
[[nodiscard]] bool is_inline() const { return layout_node().is_inline(); }
[[nodiscard]] CSS::Display display() const { return layout_node().display(); }
template<typename U, typename Callback> template<typename U, typename Callback>
TraversalDecision for_each_in_inclusive_subtree_of_type(Callback callback) const TraversalDecision for_each_in_inclusive_subtree_of_type(Callback callback) const
{ {

View file

@ -24,15 +24,14 @@
namespace Web::Painting { namespace Web::Painting {
static void paint_node(Layout::Node const& layout_node, PaintContext& context, PaintPhase phase) static void paint_node(Paintable const& paintable, PaintContext& context, PaintPhase phase)
{ {
if (auto const* paintable = layout_node.paintable()) paintable.paint(context, phase);
paintable->paint(context, phase);
} }
StackingContext::StackingContext(Layout::Box& box, StackingContext* parent, size_t index_in_tree_order) StackingContext::StackingContext(PaintableBox& paintable_box, StackingContext* parent, size_t index_in_tree_order)
: m_box(box) : m_paintable_box(paintable_box)
, m_transform(combine_transformations(m_box->computed_values().transformations())) , m_transform(combine_transformations(paintable_box.computed_values().transformations()))
, m_transform_origin(compute_transform_origin()) , m_transform_origin(compute_transform_origin())
, m_parent(parent) , m_parent(parent)
, m_index_in_tree_order(index_in_tree_order) , m_index_in_tree_order(index_in_tree_order)
@ -45,8 +44,8 @@ StackingContext::StackingContext(Layout::Box& box, StackingContext* parent, size
void StackingContext::sort() void StackingContext::sort()
{ {
quick_sort(m_children, [](auto& a, auto& b) { quick_sort(m_children, [](auto& a, auto& b) {
auto a_z_index = a->m_box->computed_values().z_index().value_or(0); auto a_z_index = a->paintable_box().computed_values().z_index().value_or(0);
auto b_z_index = b->m_box->computed_values().z_index().value_or(0); auto b_z_index = b->paintable_box().computed_values().z_index().value_or(0);
if (a_z_index == b_z_index) if (a_z_index == b_z_index)
return a->m_index_in_tree_order < b->m_index_in_tree_order; return a->m_index_in_tree_order < b->m_index_in_tree_order;
return a_z_index < b_z_index; return a_z_index < b_z_index;
@ -73,23 +72,13 @@ static PaintPhase to_paint_phase(StackingContext::StackingContextPaintPhase phas
} }
} }
void StackingContext::paint_descendants(PaintContext& context, Layout::Node const& box, StackingContextPaintPhase phase) const void StackingContext::paint_descendants(PaintContext& context, Paintable const& paintable, StackingContextPaintPhase phase) const
{ {
if (auto* paintable = box.paintable()) { paintable.before_children_paint(context, to_paint_phase(phase));
paintable->before_children_paint(context, to_paint_phase(phase)); paintable.apply_clip_overflow_rect(context, to_paint_phase(phase));
paintable->apply_clip_overflow_rect(context, to_paint_phase(phase));
}
auto child_stacking_context = [&](auto& child) -> StackingContext const* { paintable.for_each_child([this, &context, phase](auto& child) {
if (auto* paintable = child.paintable()) { auto* stacking_context = child.stacking_context_rooted_here();
if (auto* stacking_context = paintable->stacking_context_rooted_here())
return stacking_context;
}
return nullptr;
};
box.for_each_child([&](auto& child) {
auto* stacking_context = child_stacking_context(child);
if (child.is_positioned()) { if (child.is_positioned()) {
// If `child` is positioned with a z-index of `0` or `auto`, skip over it. // If `child` is positioned with a z-index of `0` or `auto`, skip over it.
@ -120,7 +109,7 @@ void StackingContext::paint_descendants(PaintContext& context, Layout::Node cons
paint_node(child, context, PaintPhase::Border); paint_node(child, context, PaintPhase::Border);
paint_descendants(context, child, phase); paint_descendants(context, child, phase);
if (child.display().is_table_inside() || child.computed_values().border_collapse() == CSS::BorderCollapse::Collapse) { if (child.display().is_table_inside() || child.computed_values().border_collapse() == CSS::BorderCollapse::Collapse) {
paint_table_borders(context, child); paint_table_borders(context, verify_cast<PaintableBox>(child));
} }
} }
break; break;
@ -137,7 +126,7 @@ void StackingContext::paint_descendants(PaintContext& context, Layout::Node cons
paint_node(child, context, PaintPhase::Background); paint_node(child, context, PaintPhase::Background);
paint_node(child, context, PaintPhase::Border); paint_node(child, context, PaintPhase::Border);
if (child.display().is_table_inside() && child.computed_values().border_collapse() == CSS::BorderCollapse::Separate) if (child.display().is_table_inside() && child.computed_values().border_collapse() == CSS::BorderCollapse::Separate)
paint_table_borders(context, child); paint_table_borders(context, verify_cast<PaintableBox>(child));
paint_descendants(context, child, StackingContextPaintPhase::BackgroundAndBorders); paint_descendants(context, child, StackingContextPaintPhase::BackgroundAndBorders);
} }
paint_descendants(context, child, phase); paint_descendants(context, child, phase);
@ -154,19 +143,16 @@ void StackingContext::paint_descendants(PaintContext& context, Layout::Node cons
} }
}); });
if (auto* paintable = box.paintable()) { paintable.clear_clip_overflow_rect(context, to_paint_phase(phase));
paintable->clear_clip_overflow_rect(context, to_paint_phase(phase)); paintable.after_children_paint(context, to_paint_phase(phase));
paintable->after_children_paint(context, to_paint_phase(phase));
}
} }
void StackingContext::paint_child(PaintContext& context, StackingContext const& child) const void StackingContext::paint_child(PaintContext& context, StackingContext const& child) const
{ {
auto parent = child.m_box->parent(); auto parent_paintable = child.paintable_box().parent();
auto* parent_paintable = parent ? parent->paintable() : nullptr;
if (parent_paintable) if (parent_paintable)
parent_paintable->before_children_paint(context, PaintPhase::Foreground); parent_paintable->before_children_paint(context, PaintPhase::Foreground);
auto containing_block = child.m_box->containing_block(); auto containing_block = child.paintable_box().containing_block();
auto* containing_block_paintable = containing_block ? containing_block->paintable() : nullptr; auto* containing_block_paintable = containing_block ? containing_block->paintable() : nullptr;
if (containing_block_paintable) if (containing_block_paintable)
containing_block_paintable->apply_clip_overflow_rect(context, PaintPhase::Foreground); containing_block_paintable->apply_clip_overflow_rect(context, PaintPhase::Foreground);
@ -183,34 +169,33 @@ void StackingContext::paint_internal(PaintContext& context) const
{ {
// For a more elaborate description of the algorithm, see CSS 2.1 Appendix E // For a more elaborate description of the algorithm, see CSS 2.1 Appendix E
// Draw the background and borders for the context root (steps 1, 2) // Draw the background and borders for the context root (steps 1, 2)
paint_node(m_box, context, PaintPhase::Background); paint_node(paintable_box(), context, PaintPhase::Background);
paint_node(m_box, context, PaintPhase::Border); paint_node(paintable_box(), context, PaintPhase::Border);
// Stacking contexts formed by positioned descendants with negative z-indices (excluding 0) in z-index order // Stacking contexts formed by positioned descendants with negative z-indices (excluding 0) in z-index order
// (most negative first) then tree order. (step 3) // (most negative first) then tree order. (step 3)
for (auto* child : m_children) { for (auto* child : m_children) {
if (!child->paintable_box().layout_box().is_positioned()) if (!child->paintable_box().is_positioned())
continue; continue;
if (child->paintable_box().computed_values().z_index().has_value() && child->m_box->computed_values().z_index().value() < 0) if (child->paintable_box().computed_values().z_index().has_value() && child->paintable_box().computed_values().z_index().value() < 0)
paint_child(context, *child); paint_child(context, *child);
} }
// Draw the background and borders for block-level children (step 4) // Draw the background and borders for block-level children (step 4)
paint_descendants(context, m_box, StackingContextPaintPhase::BackgroundAndBorders); paint_descendants(context, paintable_box(), StackingContextPaintPhase::BackgroundAndBorders);
// Draw the non-positioned floats (step 5) // Draw the non-positioned floats (step 5)
paint_descendants(context, m_box, StackingContextPaintPhase::Floats); paint_descendants(context, paintable_box(), StackingContextPaintPhase::Floats);
// Draw inline content, replaced content, etc. (steps 6, 7) // Draw inline content, replaced content, etc. (steps 6, 7)
paint_descendants(context, m_box, StackingContextPaintPhase::BackgroundAndBordersForInlineLevelAndReplaced); paint_descendants(context, paintable_box(), StackingContextPaintPhase::BackgroundAndBordersForInlineLevelAndReplaced);
paint_node(m_box, context, PaintPhase::Foreground); paint_node(paintable_box(), context, PaintPhase::Foreground);
paint_descendants(context, m_box, StackingContextPaintPhase::Foreground); paint_descendants(context, paintable_box(), StackingContextPaintPhase::Foreground);
// Draw positioned descendants with z-index `0` or `auto` in tree order. (step 8) // Draw positioned descendants with z-index `0` or `auto` in tree order. (step 8)
// FIXME: There's more to this step that we have yet to understand and implement. // FIXME: There's more to this step that we have yet to understand and implement.
m_box->paintable_box()->for_each_in_subtree([&](Paintable const& paintable) { paintable_box().for_each_in_subtree([this, &context](Paintable const& paintable) {
auto const& layout_node = paintable.layout_node();
auto const& z_index = paintable.computed_values().z_index(); auto const& z_index = paintable.computed_values().z_index();
if (!layout_node.is_positioned() || (z_index.has_value() && z_index.value() != 0)) { if (!paintable.is_positioned() || (z_index.has_value() && z_index.value() != 0)) {
return paintable.stacking_context_rooted_here() return paintable.stacking_context_rooted_here()
? TraversalDecision::SkipChildrenAndContinue ? TraversalDecision::SkipChildrenAndContinue
: TraversalDecision::Continue; : TraversalDecision::Continue;
@ -218,11 +203,10 @@ void StackingContext::paint_internal(PaintContext& context) const
// At this point, `paintable_box` is a positioned descendant with z-index: auto. // 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. // FIXME: This is basically duplicating logic found elsewhere in this same function. Find a way to make this more elegant.
auto parent = layout_node.parent(); auto* parent_paintable = paintable.parent();
auto* parent_paintable = parent ? parent->paintable() : nullptr;
if (parent_paintable) if (parent_paintable)
parent_paintable->before_children_paint(context, PaintPhase::Foreground); parent_paintable->before_children_paint(context, PaintPhase::Foreground);
auto containing_block = layout_node.containing_block(); auto containing_block = paintable.containing_block();
auto* containing_block_paintable = containing_block ? containing_block->paintable() : nullptr; auto* containing_block_paintable = containing_block ? containing_block->paintable() : nullptr;
if (containing_block_paintable) if (containing_block_paintable)
containing_block_paintable->apply_clip_overflow_rect(context, PaintPhase::Foreground); containing_block_paintable->apply_clip_overflow_rect(context, PaintPhase::Foreground);
@ -230,16 +214,16 @@ void StackingContext::paint_internal(PaintContext& context) const
paint_child(context, *child); paint_child(context, *child);
return TraversalDecision::SkipChildrenAndContinue; return TraversalDecision::SkipChildrenAndContinue;
} else { } else {
paint_node(layout_node, context, PaintPhase::Background); paint_node(paintable, context, PaintPhase::Background);
paint_node(layout_node, context, PaintPhase::Border); paint_node(paintable, context, PaintPhase::Border);
paint_descendants(context, layout_node, StackingContextPaintPhase::BackgroundAndBorders); paint_descendants(context, paintable, StackingContextPaintPhase::BackgroundAndBorders);
paint_descendants(context, layout_node, StackingContextPaintPhase::Floats); paint_descendants(context, paintable, StackingContextPaintPhase::Floats);
paint_descendants(context, layout_node, StackingContextPaintPhase::BackgroundAndBordersForInlineLevelAndReplaced); paint_descendants(context, paintable, StackingContextPaintPhase::BackgroundAndBordersForInlineLevelAndReplaced);
paint_node(layout_node, context, PaintPhase::Foreground); paint_node(paintable, context, PaintPhase::Foreground);
paint_descendants(context, layout_node, StackingContextPaintPhase::Foreground); paint_descendants(context, paintable, StackingContextPaintPhase::Foreground);
paint_node(layout_node, context, PaintPhase::Outline); paint_node(paintable, context, PaintPhase::Outline);
paint_node(layout_node, context, PaintPhase::Overlay); paint_node(paintable, context, PaintPhase::Overlay);
paint_descendants(context, layout_node, StackingContextPaintPhase::FocusAndOverlay); paint_descendants(context, paintable, StackingContextPaintPhase::FocusAndOverlay);
} }
if (parent_paintable) if (parent_paintable)
parent_paintable->after_children_paint(context, PaintPhase::Foreground); parent_paintable->after_children_paint(context, PaintPhase::Foreground);
@ -252,15 +236,15 @@ void StackingContext::paint_internal(PaintContext& context) const
// Stacking contexts formed by positioned descendants with z-indices greater than or equal to 1 in z-index order // Stacking contexts formed by positioned descendants with z-indices greater than or equal to 1 in z-index order
// (smallest first) then tree order. (Step 9) // (smallest first) then tree order. (Step 9)
for (auto* child : m_children) { for (auto* child : m_children) {
if (!child->paintable_box().layout_box().is_positioned()) if (!child->paintable_box().is_positioned())
continue; continue;
if (child->paintable_box().computed_values().z_index().has_value() && child->m_box->computed_values().z_index().value() >= 1) if (child->paintable_box().computed_values().z_index().has_value() && child->paintable_box().computed_values().z_index().value() >= 1)
paint_child(context, *child); paint_child(context, *child);
} }
paint_node(m_box, context, PaintPhase::Outline); paint_node(paintable_box(), context, PaintPhase::Outline);
paint_node(m_box, context, PaintPhase::Overlay); paint_node(paintable_box(), context, PaintPhase::Overlay);
paint_descendants(context, m_box, StackingContextPaintPhase::FocusAndOverlay); paint_descendants(context, paintable_box(), StackingContextPaintPhase::FocusAndOverlay);
} }
Gfx::FloatMatrix4x4 StackingContext::get_transformation_matrix(CSS::Transformation const& transformation) const Gfx::FloatMatrix4x4 StackingContext::get_transformation_matrix(CSS::Transformation const& transformation) const
@ -269,10 +253,10 @@ Gfx::FloatMatrix4x4 StackingContext::get_transformation_matrix(CSS::Transformati
auto value = [this, transformation](size_t index, CSS::Length const& reference_length = CSS::Length::make_px(0)) -> float { auto value = [this, transformation](size_t index, CSS::Length const& reference_length = CSS::Length::make_px(0)) -> float {
return transformation.values[index].visit( return transformation.values[index].visit(
[this, reference_length](CSS::LengthPercentage const& value) -> double { [this, reference_length](CSS::LengthPercentage const& value) -> double {
return value.resolved(m_box, reference_length).to_px(m_box).to_float(); return value.resolved(paintable_box().layout_node(), reference_length).to_px(paintable_box().layout_box()).to_float();
}, },
[this](CSS::AngleOrCalculated const& value) { [this](CSS::AngleOrCalculated const& value) {
return value.resolved(m_box).to_degrees() * M_DEG2RAD; return value.resolved(paintable_box().layout_node()).to_degrees() * M_DEG2RAD;
}, },
[](double value) { [](double value) {
return value; return value;
@ -422,11 +406,11 @@ Gfx::AffineTransform StackingContext::affine_transform_matrix() const
void StackingContext::paint(PaintContext& context) const void StackingContext::paint(PaintContext& context) const
{ {
Gfx::PainterStateSaver saver(context.painter()); Gfx::PainterStateSaver saver(context.painter());
if (m_box->is_fixed_position()) { if (paintable_box().is_fixed_position()) {
context.painter().translate(-context.painter().translation()); context.painter().translate(-context.painter().translation());
} }
auto opacity = m_box->computed_values().opacity(); auto opacity = paintable_box().computed_values().opacity();
if (opacity == 0.0f) if (opacity == 0.0f)
return; return;
@ -473,7 +457,7 @@ void StackingContext::paint(PaintContext& context) const
if (destination_rect.size() == bitmap->size()) { if (destination_rect.size() == bitmap->size()) {
context.painter().blit(destination_rect.location(), *bitmap, bitmap->rect(), opacity); context.painter().blit(destination_rect.location(), *bitmap, bitmap->rect(), opacity);
} else { } else {
auto scaling_mode = CSS::to_gfx_scaling_mode(m_box->computed_values().image_rendering(), bitmap->rect(), destination_rect); auto scaling_mode = CSS::to_gfx_scaling_mode(paintable_box().computed_values().image_rendering(), bitmap->rect(), destination_rect);
context.painter().draw_scaled_bitmap(destination_rect, *bitmap, bitmap->rect(), opacity, scaling_mode); context.painter().draw_scaled_bitmap(destination_rect, *bitmap, bitmap->rect(), opacity, scaling_mode);
} }
} else { } else {
@ -485,11 +469,11 @@ void StackingContext::paint(PaintContext& context) const
Gfx::FloatPoint StackingContext::compute_transform_origin() const Gfx::FloatPoint StackingContext::compute_transform_origin() const
{ {
auto style_value = m_box->computed_values().transform_origin(); auto style_value = paintable_box().computed_values().transform_origin();
// FIXME: respect transform-box property // FIXME: respect transform-box property
auto reference_box = paintable_box().absolute_border_box_rect(); auto reference_box = paintable_box().absolute_border_box_rect();
auto x = reference_box.left() + style_value.x.to_px(m_box, reference_box.width()); auto x = reference_box.left() + style_value.x.to_px(paintable_box().layout_node(), reference_box.width());
auto y = reference_box.top() + style_value.y.to_px(m_box, reference_box.height()); auto y = reference_box.top() + style_value.y.to_px(paintable_box().layout_node(), reference_box.height());
return { x.to_float(), y.to_float() }; return { x.to_float(), y.to_float() };
} }
@ -525,7 +509,7 @@ static TraversalDecision for_each_in_subtree_of_type_within_same_stacking_contex
Optional<HitTestResult> StackingContext::hit_test(CSSPixelPoint position, HitTestType type) const Optional<HitTestResult> StackingContext::hit_test(CSSPixelPoint position, HitTestType type) const
{ {
if (!m_box->is_visible()) if (!paintable_box().is_visible())
return {}; return {};
auto transform_origin = this->transform_origin().to_type<CSSPixels>(); auto transform_origin = this->transform_origin().to_type<CSSPixels>();
@ -536,7 +520,7 @@ Optional<HitTestResult> StackingContext::hit_test(CSSPixelPoint position, HitTes
}; };
auto transformed_position = affine_transform_matrix().inverse().value_or({}).map(offset_position).to_type<CSSPixels>() + transform_origin; auto transformed_position = affine_transform_matrix().inverse().value_or({}).map(offset_position).to_type<CSSPixels>() + transform_origin;
if (paintable_box().layout_box().is_fixed_position()) { if (paintable_box().is_fixed_position()) {
auto scroll_offset = paintable_box().document().browsing_context()->viewport_scroll_offset(); auto scroll_offset = paintable_box().document().browsing_context()->viewport_scroll_offset();
transformed_position.translate_by(-scroll_offset); transformed_position.translate_by(-scroll_offset);
} }
@ -554,7 +538,7 @@ Optional<HitTestResult> StackingContext::hit_test(CSSPixelPoint position, HitTes
// NOTE: Hit testing follows reverse painting order, that's why the conditions here are reversed. // NOTE: Hit testing follows reverse painting order, that's why the conditions here are reversed.
for (ssize_t i = m_children.size() - 1; i >= 0; --i) { for (ssize_t i = m_children.size() - 1; i >= 0; --i) {
auto const& child = *m_children[i]; auto const& child = *m_children[i];
if (child.m_box->computed_values().z_index().value_or(0) <= 0) if (child.paintable_box().computed_values().z_index().value_or(0) <= 0)
break; break;
auto result = child.hit_test(transformed_position, type); auto result = child.hit_test(transformed_position, type);
if (result.has_value() && result->paintable->visible_for_hit_testing()) if (result.has_value() && result->paintable->visible_for_hit_testing())
@ -571,8 +555,7 @@ Optional<HitTestResult> StackingContext::hit_test(CSSPixelPoint position, HitTes
} }
auto const& z_index = paintable_box.computed_values().z_index(); auto const& z_index = paintable_box.computed_values().z_index();
auto& layout_box = paintable_box.layout_box(); if (z_index.value_or(0) == 0 && paintable_box.is_positioned() && !paintable_box.stacking_context()) {
if (z_index.value_or(0) == 0 && layout_box.is_positioned() && !paintable_box.stacking_context()) {
auto candidate = paintable_box.hit_test(transformed_position, type); auto candidate = paintable_box.hit_test(transformed_position, type);
if (candidate.has_value() && candidate->paintable->visible_for_hit_testing()) { if (candidate.has_value() && candidate->paintable->visible_for_hit_testing()) {
result = move(candidate); result = move(candidate);
@ -596,7 +579,7 @@ Optional<HitTestResult> StackingContext::hit_test(CSSPixelPoint position, HitTes
return result; return result;
// 5. the in-flow, inline-level, non-positioned descendants, including inline tables and inline blocks. // 5. the in-flow, inline-level, non-positioned descendants, including inline tables and inline blocks.
if (m_box->children_are_inline() && is<Layout::BlockContainer>(*m_box)) { if (paintable_box().layout_box().children_are_inline() && is<Layout::BlockContainer>(paintable_box().layout_box())) {
auto result = paintable_box().hit_test(transformed_position, type); auto result = paintable_box().hit_test(transformed_position, type);
if (result.has_value() && result->paintable->visible_for_hit_testing()) if (result.has_value() && result->paintable->visible_for_hit_testing())
return result; return result;
@ -610,8 +593,7 @@ Optional<HitTestResult> StackingContext::hit_test(CSSPixelPoint position, HitTes
return TraversalDecision::SkipChildrenAndContinue; return TraversalDecision::SkipChildrenAndContinue;
} }
auto& layout_box = paintable_box.layout_box(); if (paintable_box.is_floating()) {
if (layout_box.is_floating()) {
if (auto candidate = paintable_box.hit_test(transformed_position, type); candidate.has_value()) { if (auto candidate = paintable_box.hit_test(transformed_position, type); candidate.has_value()) {
result = move(candidate); result = move(candidate);
return TraversalDecision::Break; return TraversalDecision::Break;
@ -623,7 +605,7 @@ Optional<HitTestResult> StackingContext::hit_test(CSSPixelPoint position, HitTes
return result; return result;
// 3. the in-flow, non-inline-level, non-positioned descendants. // 3. the in-flow, non-inline-level, non-positioned descendants.
if (!m_box->children_are_inline()) { if (!paintable_box().layout_box().children_are_inline()) {
for_each_in_subtree_of_type_within_same_stacking_context_in_reverse<PaintableBox>(paintable_box(), [&](PaintableBox const& paintable_box) { for_each_in_subtree_of_type_within_same_stacking_context_in_reverse<PaintableBox>(paintable_box(), [&](PaintableBox const& paintable_box) {
// FIXME: Support more overflow variations. // FIXME: Support more overflow variations.
if (paintable_box.computed_values().overflow_x() == CSS::Overflow::Hidden && paintable_box.computed_values().overflow_y() == CSS::Overflow::Hidden) { if (paintable_box.computed_values().overflow_x() == CSS::Overflow::Hidden && paintable_box.computed_values().overflow_y() == CSS::Overflow::Hidden) {
@ -631,8 +613,7 @@ Optional<HitTestResult> StackingContext::hit_test(CSSPixelPoint position, HitTes
return TraversalDecision::SkipChildrenAndContinue; return TraversalDecision::SkipChildrenAndContinue;
} }
auto& layout_box = paintable_box.layout_box(); if (!paintable_box.is_absolutely_positioned() && !paintable_box.is_floating()) {
if (!layout_box.is_absolutely_positioned() && !layout_box.is_floating()) {
if (auto candidate = paintable_box.hit_test(transformed_position, type); candidate.has_value()) { if (auto candidate = paintable_box.hit_test(transformed_position, type); candidate.has_value()) {
result = move(candidate); result = move(candidate);
return TraversalDecision::Break; return TraversalDecision::Break;
@ -648,7 +629,7 @@ Optional<HitTestResult> StackingContext::hit_test(CSSPixelPoint position, HitTes
// NOTE: Hit testing follows reverse painting order, that's why the conditions here are reversed. // NOTE: Hit testing follows reverse painting order, that's why the conditions here are reversed.
for (ssize_t i = m_children.size() - 1; i >= 0; --i) { for (ssize_t i = m_children.size() - 1; i >= 0; --i) {
auto const& child = *m_children[i]; auto const& child = *m_children[i];
if (child.m_box->computed_values().z_index().value_or(0) >= 0) if (child.paintable_box().computed_values().z_index().value_or(0) >= 0)
break; break;
auto result = child.hit_test(transformed_position, type); auto result = child.hit_test(transformed_position, type);
if (result.has_value() && result->paintable->visible_for_hit_testing()) if (result.has_value() && result->paintable->visible_for_hit_testing())
@ -670,9 +651,9 @@ void StackingContext::dump(int indent) const
StringBuilder builder; StringBuilder builder;
for (int i = 0; i < indent; ++i) for (int i = 0; i < indent; ++i)
builder.append(' '); builder.append(' ');
builder.appendff("SC for {} {} [children: {}] (z-index: ", m_box->debug_description(), paintable_box().absolute_rect(), m_children.size()); builder.appendff("SC for {} {} [children: {}] (z-index: ", paintable_box().layout_box().debug_description(), paintable_box().absolute_rect(), m_children.size());
if (m_box->computed_values().z_index().has_value()) if (paintable_box().computed_values().z_index().has_value())
builder.appendff("{}", m_box->computed_values().z_index().value()); builder.appendff("{}", paintable_box().computed_values().z_index().value());
else else
builder.append("auto"sv); builder.append("auto"sv);
builder.append(')'); builder.append(')');

View file

@ -8,19 +8,18 @@
#include <AK/Vector.h> #include <AK/Vector.h>
#include <LibGfx/Matrix4x4.h> #include <LibGfx/Matrix4x4.h>
#include <LibWeb/Layout/Node.h>
#include <LibWeb/Painting/Paintable.h> #include <LibWeb/Painting/Paintable.h>
namespace Web::Painting { namespace Web::Painting {
class StackingContext { class StackingContext {
public: public:
StackingContext(Layout::Box&, StackingContext* parent, size_t index_in_tree_order); StackingContext(PaintableBox&, StackingContext* parent, size_t index_in_tree_order);
StackingContext* parent() { return m_parent; } StackingContext* parent() { return m_parent; }
StackingContext const* parent() const { return m_parent; } StackingContext const* parent() const { return m_parent; }
PaintableBox const& paintable_box() const { return *m_box->paintable_box(); } PaintableBox const& paintable_box() const { return *m_paintable_box; }
enum class StackingContextPaintPhase { enum class StackingContextPaintPhase {
BackgroundAndBorders, BackgroundAndBorders,
@ -30,7 +29,7 @@ public:
FocusAndOverlay, FocusAndOverlay,
}; };
void paint_descendants(PaintContext&, Layout::Node const&, StackingContextPaintPhase) const; void paint_descendants(PaintContext&, Paintable const&, StackingContextPaintPhase) const;
void paint(PaintContext&) const; void paint(PaintContext&) const;
Optional<HitTestResult> hit_test(CSSPixelPoint, HitTestType) const; Optional<HitTestResult> hit_test(CSSPixelPoint, HitTestType) const;
@ -42,7 +41,7 @@ public:
void sort(); void sort();
private: private:
JS::NonnullGCPtr<Layout::Box> m_box; JS::NonnullGCPtr<PaintableBox> m_paintable_box;
Gfx::FloatMatrix4x4 m_transform; Gfx::FloatMatrix4x4 m_transform;
Gfx::FloatPoint m_transform_origin; Gfx::FloatPoint m_transform_origin;
StackingContext* const m_parent { nullptr }; StackingContext* const m_parent { nullptr };

View file

@ -30,16 +30,15 @@ struct Traits<CellCoordinates> : public GenericTraits<CellCoordinates> {
namespace Web::Painting { namespace Web::Painting {
static void collect_cell_boxes(Vector<PaintableBox const*>& cell_boxes, Layout::Node const& box) static void collect_cell_boxes(Vector<PaintableBox const*>& cell_boxes, PaintableBox const& table_paintable)
{ {
box.for_each_child([&](auto& child) { table_paintable.for_each_child_of_type<PaintableBox>([&](auto& child) {
if (child.display().is_table_cell()) { if (child.display().is_table_cell()) {
VERIFY(is<Layout::Box>(child)); cell_boxes.append(&child);
if (child.paintable())
cell_boxes.append(static_cast<Layout::Box const&>(child).paintable_box());
} else { } else {
collect_cell_boxes(cell_boxes, child); collect_cell_boxes(cell_boxes, child);
} }
return TraversalDecision::Continue;
}); });
} }
@ -356,12 +355,12 @@ static void paint_separate_cell_borders(PaintableBox const* cell_box, HashMap<Ce
paint_all_borders(context, cell_rect, cell_box->normalized_border_radii_data(), borders_data); paint_all_borders(context, cell_rect, cell_box->normalized_border_radii_data(), borders_data);
} }
void paint_table_borders(PaintContext& context, Layout::Node const& box) void paint_table_borders(PaintContext& context, PaintableBox const& table_paintable)
{ {
// Partial implementation of painting according to the collapsing border model: // Partial implementation of painting according to the collapsing border model:
// https://www.w3.org/TR/CSS22/tables.html#collapsing-borders // https://www.w3.org/TR/CSS22/tables.html#collapsing-borders
Vector<PaintableBox const*> cell_boxes; Vector<PaintableBox const*> cell_boxes;
collect_cell_boxes(cell_boxes, box); collect_cell_boxes(cell_boxes, table_paintable);
Vector<BorderEdgePaintingInfo> border_edge_painting_info_list; Vector<BorderEdgePaintingInfo> border_edge_painting_info_list;
HashMap<CellCoordinates, PaintableBox const*> cell_coordinates_to_box; HashMap<CellCoordinates, PaintableBox const*> cell_coordinates_to_box;
size_t row_count = 0; size_t row_count = 0;

View file

@ -10,6 +10,6 @@
namespace Web::Painting { namespace Web::Painting {
void paint_table_borders(PaintContext&, Layout::Node const&); void paint_table_borders(PaintContext&, PaintableBox const& table_paintable);
} }

View file

@ -31,7 +31,7 @@ void ViewportPaintable::build_stacking_context_tree_if_needed()
void ViewportPaintable::build_stacking_context_tree() void ViewportPaintable::build_stacking_context_tree()
{ {
set_stacking_context(make<StackingContext>(layout_box(), nullptr, 0)); set_stacking_context(make<StackingContext>(*this, nullptr, 0));
size_t index_in_tree_order = 1; size_t index_in_tree_order = 1;
for_each_in_subtree_of_type<PaintableBox>([&](PaintableBox const& paintable) { for_each_in_subtree_of_type<PaintableBox>([&](PaintableBox const& paintable) {
@ -43,7 +43,7 @@ void ViewportPaintable::build_stacking_context_tree()
} }
auto* parent_context = paintable_box.enclosing_stacking_context(); auto* parent_context = paintable_box.enclosing_stacking_context();
VERIFY(parent_context); VERIFY(parent_context);
paintable_box.set_stacking_context(make<Painting::StackingContext>(paintable_box.layout_box(), parent_context, index_in_tree_order++)); paintable_box.set_stacking_context(make<Painting::StackingContext>(paintable_box, parent_context, index_in_tree_order++));
return TraversalDecision::Continue; return TraversalDecision::Continue;
}); });