LibWeb: Resolve relpos fragment offsets only for inline paintables

Prior to this change, we iterated through all fragments within each
PaintableWithLines to resolve the relative position offset. This
happened before transferring the fragments to their corresponding
inline paintables.

With this change, we significantly reduce amount of work by attempting
to resolve relative position offsets only for those contained within
inline paintables.

Performance improvement on https://html.spec.whatwg.org/
This commit is contained in:
Aliaksandr Kalenik 2024-01-18 14:03:30 +01:00 committed by Andreas Kling
parent de9fa25e11
commit b317620486
Notes: sideshowbarker 2024-07-16 22:26:05 +09:00
2 changed files with 46 additions and 36 deletions

View file

@ -139,48 +139,22 @@ static CSSPixelRect measure_scrollable_overflow(Box const& box)
return scrollable_overflow_rect;
}
void LayoutState::resolve_relative_positions(Vector<Painting::PaintableWithLines&> const& paintables_with_lines)
void LayoutState::resolve_relative_positions()
{
// This function resolves relative position offsets of all the boxes & fragments in the paint tree.
// This function resolves relative position offsets of fragments that belong to inline paintables.
// It runs *after* the paint tree has been constructed, so it modifies paintable node & fragment offsets directly.
// Regular boxes (not line box fragments):
for (auto& it : used_values_per_layout_node) {
auto& used_values = *it.value;
auto& node = const_cast<NodeWithStyle&>(used_values.node());
if (!node.is_box())
auto* paintable = node.paintable();
if (!paintable)
continue;
if (!is<Painting::InlinePaintable>(*paintable))
continue;
auto& paintable = static_cast<Painting::PaintableBox&>(*node.paintable());
CSSPixelPoint offset;
if (used_values.containing_line_box_fragment.has_value()) {
// Atomic inline case:
// We know that `node` is an atomic inline because `containing_line_box_fragments` refers to the
// line box fragment in the parent block container that contains it.
auto const& containing_line_box_fragment = used_values.containing_line_box_fragment.value();
auto const& containing_block = *node.containing_block();
auto const& containing_block_used_values = get(containing_block);
auto const& fragment = containing_block_used_values.line_boxes[containing_line_box_fragment.line_box_index].fragments()[containing_line_box_fragment.fragment_index];
// The fragment has the final offset for the atomic inline, so we just need to copy it from there.
offset = fragment.offset();
} else {
// Not an atomic inline, much simpler case.
offset = used_values.offset;
}
// Apply relative position inset if appropriate.
if (node.computed_values().position() == CSS::Positioning::Relative && is<NodeWithStyleAndBoxModelMetrics>(node)) {
auto& inset = static_cast<NodeWithStyleAndBoxModelMetrics const&>(node).box_model().inset;
offset.translate_by(inset.left, inset.top);
}
paintable.set_offset(offset);
}
// Line box fragments:
for (auto const& paintable_with_lines : paintables_with_lines) {
for (auto& fragment : paintable_with_lines.fragments()) {
auto const& inline_paintable = static_cast<Painting::InlinePaintable&>(*paintable);
for (auto& fragment : inline_paintable.fragments()) {
auto const& fragment_node = fragment.layout_node();
if (!is<Layout::NodeWithStyleAndBoxModelMetrics>(*fragment_node.parent()))
continue;
@ -489,7 +463,41 @@ void LayoutState::commit(Box& root)
}
}
resolve_relative_positions(paintables_with_lines);
// Resolve relative positions for regular boxes (not line box fragments):
// NOTE: This needs to occur before fragments are transferred into the corresponding inline paintables, because
// after this transfer, the containing_line_box_fragment will no longer be valid.
for (auto& it : used_values_per_layout_node) {
auto& used_values = *it.value;
auto& node = const_cast<NodeWithStyle&>(used_values.node());
if (!node.is_box())
continue;
auto& paintable = static_cast<Painting::PaintableBox&>(*node.paintable());
CSSPixelPoint offset;
if (used_values.containing_line_box_fragment.has_value()) {
// Atomic inline case:
// We know that `node` is an atomic inline because `containing_line_box_fragments` refers to the
// line box fragment in the parent block container that contains it.
auto const& containing_line_box_fragment = used_values.containing_line_box_fragment.value();
auto const& containing_block = *node.containing_block();
auto const& containing_block_used_values = get(containing_block);
auto const& fragment = containing_block_used_values.line_boxes[containing_line_box_fragment.line_box_index].fragments()[containing_line_box_fragment.fragment_index];
// The fragment has the final offset for the atomic inline, so we just need to copy it from there.
offset = fragment.offset();
} else {
// Not an atomic inline, much simpler case.
offset = used_values.offset;
}
// 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;
offset.translate_by(inset.left, inset.top);
}
paintable.set_offset(offset);
}
// Make a pass over all the line boxes to:
// - Collect all text nodes, so we can create paintables for them later.
@ -522,6 +530,8 @@ void LayoutState::commit(Box& root)
build_paint_tree(root);
resolve_relative_positions();
// Measure overflow in scroll containers.
for (auto& it : used_values_per_layout_node) {
auto& used_values = *it.value;

View file

@ -188,7 +188,7 @@ struct LayoutState {
LayoutState const& m_root;
private:
void resolve_relative_positions(Vector<Painting::PaintableWithLines&> const&);
void resolve_relative_positions();
void resolve_border_radii();
void resolve_box_shadow_data();
void resolve_text_shadows(Vector<Painting::PaintableWithLines&> const& paintables_with_lines);