Skip to content

LibWeb: Seal a box's committed metrics when it is placed - #11036

Merged
kalenikaliaksandr merged 4 commits into
LadybirdBrowser:masterfrom
kalenikaliaksandr:placement-seal
Aug 7, 2026
Merged

LibWeb: Seal a box's committed metrics when it is placed#11036
kalenikaliaksandr merged 4 commits into
LadybirdBrowser:masterfrom
kalenikaliaksandr:placement-seal

Conversation

@kalenikaliaksandr

Copy link
Copy Markdown
Member

"When is this box's geometry final?" used to have a different answer
per field and per box class, and nothing checked any of them. Every
consumer of placed geometry was correct only by global ordering
arguments nobody could verify locally, and a production timing mistake
would ship as a silent misrender.

Establish the invariant that placement is the seal point: after
place_child() returns, no field of that box's UsedValues that commit
emits may change again. The sealed set is exactly the
FfiCommittedBoxMetrics payload — content offset, content sizes,
margins, borders, paddings, insets, and the containing-line-box
coordinate. Working state commit never reads (definiteness flags, size
constraints, baselines, the collapsing-borders flag) stays freely
mutable, and rare data is a separate store with its own rules. A box
materialized from a previous paintable is sealed at materialization,
which is that box's placement. The seal point is placement, not the end
of the box's inside layout: everything the engine finishes between
those two moments (float root finalization, abspos alignment insets,
table cell stretch) remains legal.

Block-level boxes and grid items had their relative-position insets
resolved by compute_inset() immediately after place_child(), so the
inset fields of an already-placed box kept changing after placement.
Every other caller (floats, flex items, inline-level boxes) already
resolves insets before its boxes are placed, and nothing reads the
insets between the two points, so the late resolution was invisible —
but it blocks the placement-seal invariant where a placed box's
committed metrics are final.

Hoist the compute_inset() call above the placement in
layout_block_level_box() and in the grid item placement loop. The
call's inputs (style insets, the containing block's content sizes, the
definiteness walk for percentage insets) are untouched by the
statements it crosses in both functions, so committed geometry is
unchanged, in measurement passes as well as committing ones.
A block-level box that interrupts an inline formatting context gets a
line box of its own, and append_block_level_box() recorded that
containing-line-box coordinate on the box after its placement, so the
coordinate settled after the box was already placed. Commit is the only
reader, so the late write was invisible today — but it blocks the
placement-seal invariant where a placed box's committed metrics are
final.

The box's used values do not exist yet when the interrupting-block
wrapper starts, so the writes cannot simply move there. Instead the
wrapper reserves the line index up front (the freshly begun line the
box will occupy), threads the full coordinate into
layout_block_level_box(), which records it right before place_child(),
and passes the same index into append_block_level_box(), which now
asserts it matches the line it appends to instead of re-deriving and
writing it.

The reserved index always equals the previously derived one: the last
line always exists and is fragment-empty at the wrapper's entry, and
nothing during the block's own layout can mutate the outer containing
block's line boxes.
A BFC run whose margin state ended nonzero used to overwrite the
committed margin_bottom of its last real in-flow child with the run's
trailing collapsed margin: an aggregate folding in the margins of every
trailing collapse-through sibling and, through nested runs, of
descendants' trailing margins. The aggregate only exists at run end, so
the overwrite mutated a box that was already placed — the one committed
metric that settled after placement — and the root's baselines had to
be re-derived afterwards because the write moved that child's margin
box.

The trailing collapsed margin is now run output, stored on the
formatting context together with the child it hangs below, and consumed
only by the root's automatic block size, which still extends to the same
bottom margin edge as before, so geometry is unchanged everywhere.
Committed metrics and baseline derivation read the box's own margins,
and the run-tail baseline re-derivation is gone.

Observably, the last child of such a run now reports its own used
margin-bottom, matching other engines, instead of the aggregate: 46 test
expectations change, every one only in a box's reported margin-bottom
(45 layout dumps and one getComputedStyle listing); no offset, size,
or reference test changes.
"When is this box's geometry final?" used to have a different answer
per field and per box class, and nothing checked any of them. Every
consumer of placed geometry was correct only by global ordering
arguments nobody could verify locally, and a production timing mistake
would ship as a silent misrender.

Establish the invariant that placement is the seal point: after
place_child() returns, no field of that box's UsedValues that commit
emits may change again. The sealed set is exactly the
FfiCommittedBoxMetrics payload — content offset, content sizes,
margins, borders, paddings, insets, and the containing-line-box
coordinate. Working state commit never reads (definiteness flags, size
constraints, baselines, the collapsing-borders flag) stays freely
mutable, and rare data is a separate store with its own rules. A box
materialized from a previous paintable is sealed at materialization,
which is that box's placement. The seal point is placement, not the end
of the box's inside layout: everything the engine finishes between
those two moments (float root finalization, abspos alignment insets,
table cell stretch) remains legal.
@coderabbitai

coderabbitai Bot commented Aug 7, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Block layout now seals committed geometry, preserves line-box fragment coordinates for interrupting blocks, and tracks trailing collapsed margins separately for root automatic block-size calculation. Layout expectations and computed-style expectations update accordingly.

Changes

Layout metrics and margin propagation

Layer / File(s) Summary
Seal committed metrics
Libraries/LibWeb/Rust/src/layout/used_values.rs, Libraries/LibWeb/Rust/src/layout/formatting_context.rs, Libraries/LibWeb/Rust/src/layout/grid_formatting_context.rs, Libraries/LibWeb/Rust/src/layout/layout_state.rs
SealableCell prevents writes after metric commitment. Placement and paintable population seal committed geometry after recording offsets. Grid placement resolves relative-position insets before placement.
Propagate line-box coordinates
Libraries/LibWeb/Rust/src/layout/line_builder.rs, Libraries/LibWeb/Rust/src/layout/block_formatting_context.rs
Interrupting blocks retain their line-box fragment coordinates. The line builder accepts and validates the reserved line index.
Compute root size from trailing margins
Libraries/LibWeb/Rust/src/layout/block_formatting_context.rs, Tests/LibWeb/Layout/expected/..., Tests/LibWeb/Text/expected/...
Trailing collapsed margins are stored as run output. Root automatic block-size calculation applies the aggregate margin without replacing the final child’s own bottom margin. Expected layout and computed-style values reflect the revised metrics.

Estimated code review effort: 4 (Complex) | ~45 minutes

Possibly related PRs

Suggested reviewers: tcl3

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description check ✅ Passed The description directly explains the placement-sealing invariant and committed geometry changes implemented in the layout code.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
Libraries/LibWeb/Rust/src/layout/block_formatting_context.rs (1)

2220-2228: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Skip list marker boxes in the trailing-margin scan.

self.children(self.root) includes list marker boxes. Non-floating list markers return before create_used_values(). If this loop reaches one, margins_collapse_through() calls self.used(node) and panics.

Add facts.is_list_item_marker_box() to the skip condition. This also preserves the intended “last real in-flow child” selection.

Proposed fix
-                if facts.is_absolutely_positioned() || facts.is_floating() {
+                if facts.is_list_item_marker_box() || facts.is_absolutely_positioned() || facts.is_floating() {
                     continue;
                 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Libraries/LibWeb/Rust/src/layout/block_formatting_context.rs` around lines
2220 - 2228, Update the skip condition in the trailing-margin scan over
flow_children_bottom_up to also continue when facts.is_list_item_marker_box() is
true. Keep the existing absolutely-positioned, floating, and margins-collapse
checks unchanged so list marker boxes never reach margins_collapse_through().
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@Libraries/LibWeb/Rust/src/layout/block_formatting_context.rs`:
- Around line 2220-2228: Update the skip condition in the trailing-margin scan
over flow_children_bottom_up to also continue when
facts.is_list_item_marker_box() is true. Keep the existing
absolutely-positioned, floating, and margins-collapse checks unchanged so list
marker boxes never reach margins_collapse_through().

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 305033d4-52d2-498e-90b7-766ee21f606d

📥 Commits

Reviewing files that changed from the base of the PR and between 3e04eb6 and 7b76e9f.

📒 Files selected for processing (52)
  • Libraries/LibWeb/Rust/src/layout/block_formatting_context.rs
  • Libraries/LibWeb/Rust/src/layout/formatting_context.rs
  • Libraries/LibWeb/Rust/src/layout/grid_formatting_context.rs
  • Libraries/LibWeb/Rust/src/layout/layout_state.rs
  • Libraries/LibWeb/Rust/src/layout/line_builder.rs
  • Libraries/LibWeb/Rust/src/layout/used_values.rs
  • Tests/LibWeb/Layout/expected/abspos-inline-containing-block-first-last-line-rule.txt
  • Tests/LibWeb/Layout/expected/block-and-inline/box-with-clearance-and-margin-top.txt
  • Tests/LibWeb/Layout/expected/block-and-inline/float-clear-by-line-break-followed-by-block.txt
  • Tests/LibWeb/Layout/expected/block-and-inline/float-vertical-offset-by-preceding-float.txt
  • Tests/LibWeb/Layout/expected/block-and-inline/forced-break-stops-non-whitespace-sequence.txt
  • Tests/LibWeb/Layout/expected/block-and-inline/intrinsic-sizing-stress-test.txt
  • Tests/LibWeb/Layout/expected/block-and-inline/list-markers-intruded-by-float.txt
  • Tests/LibWeb/Layout/expected/block-and-inline/margin-collapse-1.txt
  • Tests/LibWeb/Layout/expected/block-and-inline/relpos-inline-element-js-offsets.txt
  • Tests/LibWeb/Layout/expected/block-size.txt
  • Tests/LibWeb/Layout/expected/css-counters/basic.txt
  • Tests/LibWeb/Layout/expected/css-counters/hidden-elements.txt
  • Tests/LibWeb/Layout/expected/css-namespace-rule-matches.txt
  • Tests/LibWeb/Layout/expected/css-namespace-rule-no-match.txt
  • Tests/LibWeb/Layout/expected/css-text-transform-math-auto.txt
  • Tests/LibWeb/Layout/expected/css/content-for-marker-in-list.txt
  • Tests/LibWeb/Layout/expected/div_align.txt
  • Tests/LibWeb/Layout/expected/document-write-incomplete-tag.txt
  • Tests/LibWeb/Layout/expected/empty-list-items.txt
  • Tests/LibWeb/Layout/expected/fieldset-legend-variations.txt
  • Tests/LibWeb/Layout/expected/flex/list-container-display-contents.txt
  • Tests/LibWeb/Layout/expected/grid/justify-content-cols.txt
  • Tests/LibWeb/Layout/expected/import-after-namespace.txt
  • Tests/LibWeb/Layout/expected/inline-flex-baseline-with-wrapped-hidden-text.txt
  • Tests/LibWeb/Layout/expected/inline-size.txt
  • Tests/LibWeb/Layout/expected/inside-list-item-content-offset.txt
  • Tests/LibWeb/Layout/expected/layout-tree-update/inline-element-position-change.txt
  • Tests/LibWeb/Layout/expected/layout-tree-update/transform-longhands-clear.txt
  • Tests/LibWeb/Layout/expected/leading-metrics.txt
  • Tests/LibWeb/Layout/expected/list-item-marker-content-height.txt
  • Tests/LibWeb/Layout/expected/list-marker-rtl.txt
  • Tests/LibWeb/Layout/expected/misc/invalid-slotted-selector.txt
  • Tests/LibWeb/Layout/expected/multi-code-point-graphemes.txt
  • Tests/LibWeb/Layout/expected/ol-render-deep-hybrid-list-item-list.txt
  • Tests/LibWeb/Layout/expected/ol-render-item-values.txt
  • Tests/LibWeb/Layout/expected/ol-render-style-list-item.txt
  • Tests/LibWeb/Layout/expected/ordered-list.txt
  • Tests/LibWeb/Layout/expected/pdf-viewer.txt
  • Tests/LibWeb/Layout/expected/position-empty-pseudo-elements.txt
  • Tests/LibWeb/Layout/expected/quirks/input-in-pre-quirks-mode.txt
  • Tests/LibWeb/Layout/expected/set-margin-of-floating-box.txt
  • Tests/LibWeb/Layout/expected/table-caption-auto-height.txt
  • Tests/LibWeb/Layout/expected/table/table-align-center-with-margin.txt
  • Tests/LibWeb/Layout/expected/ul-render.txt
  • Tests/LibWeb/Layout/expected/utf-16-be-xhtml-file-should-decode-correctly.txt
  • Tests/LibWeb/Text/expected/css/getComputedStyle-print-all.txt

@kalenikaliaksandr
kalenikaliaksandr merged commit 40e7614 into LadybirdBrowser:master Aug 7, 2026
14 of 15 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant