LibWeb: Store final box model metrics in paint tree, not layout tree

This was a weird case of layout results being stored in the layout tree
instead of in the paint tree like everything else.
This commit is contained in:
Andreas Kling 2025-02-17 13:54:36 +01:00 committed by Andreas Kling
commit fb020a3c8f
Notes: github-actions[bot] 2025-02-17 17:29:27 +00:00
8 changed files with 65 additions and 58 deletions

View file

@ -1,41 +0,0 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <andreas@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibWeb/Layout/BoxModelMetrics.h>
namespace Web::Layout {
PixelBox BoxModelMetrics::margin_box() const
{
return {
margin.top + border.top + padding.top,
margin.right + border.right + padding.right,
margin.bottom + border.bottom + padding.bottom,
margin.left + border.left + padding.left,
};
}
PixelBox BoxModelMetrics::padding_box() const
{
return {
padding.top,
padding.right,
padding.bottom,
padding.left,
};
}
PixelBox BoxModelMetrics::border_box() const
{
return {
border.top + padding.top,
border.right + padding.right,
border.bottom + padding.bottom,
border.left + padding.left,
};
}
}

View file

@ -1,33 +0,0 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <andreas@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <LibGfx/Size.h>
#include <LibWeb/PixelUnits.h>
namespace Web::Layout {
struct PixelBox {
CSSPixels top { 0 };
CSSPixels right { 0 };
CSSPixels bottom { 0 };
CSSPixels left { 0 };
};
struct BoxModelMetrics {
public:
PixelBox margin;
PixelBox padding;
PixelBox border;
PixelBox inset;
PixelBox margin_box() const;
PixelBox padding_box() const;
PixelBox border_box() const;
};
}

View file

@ -141,7 +141,7 @@ static CSSPixelRect measure_scrollable_overflow(Box const& box)
// to enable a scroll position that satisfies the requirements of place-content: end alignment.
auto has_scrollable_overflow = !paintable_box.absolute_padding_box_rect().contains(scrollable_overflow_rect);
if (has_scrollable_overflow) {
scrollable_overflow_rect.set_height(max(scrollable_overflow_rect.height(), content_overflow_rect.height() + box.box_model().padding.bottom));
scrollable_overflow_rect.set_height(max(scrollable_overflow_rect.height(), content_overflow_rect.height() + paintable_box.box_model().padding.bottom));
}
paintable_box.set_overflow_data(Painting::PaintableBox::OverflowData {
@ -177,7 +177,8 @@ void LayoutState::resolve_relative_positions()
if (!ancestor->display().is_inline_outside() || !ancestor->display().is_flow_inside())
break;
if (ancestor->computed_values().position() == CSS::Positioning::Relative) {
auto const& ancestor_node = static_cast<Layout::NodeWithStyleAndBoxModelMetrics const&>(*ancestor);
VERIFY(ancestor->first_paintable());
auto const& ancestor_node = as<Painting::PaintableBox>(*ancestor->first_paintable());
auto const& inset = ancestor_node.box_model().inset;
offset.translate_by(inset.left, inset.top);
}
@ -235,12 +236,21 @@ void LayoutState::commit(Box& root)
HashTable<Layout::TextNode*> text_nodes;
HashTable<Painting::PaintableWithLines*> inline_node_paintables;
auto transfer_box_model_metrics = [&](Painting::BoxModelMetrics& box_model, UsedValues const& used_values) {
box_model.inset = { used_values.inset_top, used_values.inset_right, used_values.inset_bottom, used_values.inset_left };
box_model.padding = { used_values.padding_top, used_values.padding_right, used_values.padding_bottom, used_values.padding_left };
box_model.border = { used_values.border_top, used_values.border_right, used_values.border_bottom, used_values.border_left };
box_model.margin = { used_values.margin_top, used_values.margin_right, used_values.margin_bottom, used_values.margin_left };
};
auto try_to_relocate_fragment_in_inline_node = [&](auto& fragment, size_t line_index) -> bool {
for (auto const* parent = fragment.layout_node().parent(); parent; parent = parent->parent()) {
if (is<InlineNode>(*parent)) {
auto& inline_node = const_cast<InlineNode&>(static_cast<InlineNode const&>(*parent));
auto line_paintable = inline_node.create_paintable_for_line_with_index(line_index);
line_paintable->add_fragment(fragment);
if (auto const* used_values = used_values_per_layout_node.get(inline_node).value_or(nullptr))
transfer_box_model_metrics(line_paintable->box_model(), *used_values);
if (!inline_node_paintables.contains(line_paintable.ptr())) {
inline_node_paintables.set(line_paintable.ptr());
inline_node.add_paintable(line_paintable);
@ -255,21 +265,15 @@ void LayoutState::commit(Box& root)
auto& used_values = *it.value;
auto& node = const_cast<NodeWithStyle&>(used_values.node());
if (is<NodeWithStyleAndBoxModelMetrics>(node)) {
// Transfer box model metrics.
auto& box_model = static_cast<NodeWithStyleAndBoxModelMetrics&>(node).box_model();
box_model.inset = { used_values.inset_top, used_values.inset_right, used_values.inset_bottom, used_values.inset_left };
box_model.padding = { used_values.padding_top, used_values.padding_right, used_values.padding_bottom, used_values.padding_left };
box_model.border = { used_values.border_top, used_values.border_right, used_values.border_bottom, used_values.border_left };
box_model.margin = { used_values.margin_top, used_values.margin_right, used_values.margin_bottom, used_values.margin_left };
}
auto paintable = node.create_paintable();
node.add_paintable(paintable);
// For boxes, transfer all the state needed for painting.
if (paintable && is<Painting::PaintableBox>(*paintable)) {
auto& paintable_box = static_cast<Painting::PaintableBox&>(*paintable);
transfer_box_model_metrics(paintable_box.box_model(), used_values);
paintable_box.set_offset(used_values.offset);
paintable_box.set_content_size(used_values.content_width(), used_values.content_height());
if (used_values.override_borders_data().has_value()) {
@ -319,6 +323,8 @@ void LayoutState::commit(Box& root)
auto line_paintable = inline_node->create_paintable_for_line_with_index(0);
inline_node->add_paintable(line_paintable);
inline_node_paintables.set(line_paintable.ptr());
if (auto const* used_values = used_values_per_layout_node.get(*inline_node).value_or(nullptr))
transfer_box_model_metrics(line_paintable->box_model(), *used_values);
}
// Resolve relative positions for regular boxes (not line box fragments):
@ -331,7 +337,7 @@ void LayoutState::commit(Box& root)
if (!node.is_box())
continue;
auto& paintable = static_cast<Painting::PaintableBox&>(*node.first_paintable());
auto& paintable = as<Painting::PaintableBox>(*node.first_paintable());
CSSPixelPoint offset;
if (used_values.containing_line_box_fragment.has_value()) {
@ -351,7 +357,7 @@ void LayoutState::commit(Box& root)
}
// Apply relative position inset if appropriate.
if (node.computed_values().position() == CSS::Positioning::Relative && is<NodeWithStyleAndBoxModelMetrics>(node)) {
auto const& inset = static_cast<NodeWithStyleAndBoxModelMetrics const&>(node).box_model().inset;
auto const& inset = paintable.box_model().inset;
offset.translate_by(inset.left, inset.top);
}
paintable.set_offset(offset);

View file

@ -15,7 +15,6 @@
#include <LibWeb/CSS/StyleValues/ImageStyleValue.h>
#include <LibWeb/DOM/Document.h>
#include <LibWeb/Forward.h>
#include <LibWeb/Layout/BoxModelMetrics.h>
#include <LibWeb/Painting/PaintContext.h>
#include <LibWeb/Painting/Paintable.h>
#include <LibWeb/TreeNode.h>
@ -263,9 +262,6 @@ class NodeWithStyleAndBoxModelMetrics : public NodeWithStyle {
GC_CELL(NodeWithStyleAndBoxModelMetrics, NodeWithStyle);
public:
BoxModelMetrics& box_model() { return m_box_model; }
BoxModelMetrics const& box_model() const { return m_box_model; }
GC::Ptr<NodeWithStyleAndBoxModelMetrics> continuation_of_node() const { return m_continuation_of_node; }
void set_continuation_of_node(Badge<TreeBuilder>, GC::Ptr<NodeWithStyleAndBoxModelMetrics> node) { m_continuation_of_node = node; }
@ -287,7 +283,6 @@ protected:
private:
virtual bool is_node_with_style_and_box_model_metrics() const final { return true; }
BoxModelMetrics m_box_model;
GC::Ptr<NodeWithStyleAndBoxModelMetrics> m_continuation_of_node;
};