Skip to content

Place every box before its formatting context returns - #11044

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

Place every box before its formatting context returns#11044
kalenikaliaksandr merged 4 commits into
LadybirdBrowser:masterfrom
kalenikaliaksandr:fc-run-placement

Conversation

@kalenikaliaksandr

Copy link
Copy Markdown
Member

This branch enforces new layout invariant: a formatting context run must place every box it's responsible for before its run() returns and every box that has used values has to be placed by the time layout commits.

We want a formatting context run to place every box it is responsible
for before run() returns. Floats violated this: the BFC accumulated
FloatingBox records during the run, and run_formatting_context placed
them via place_floats_after_run() only after run() had returned.

The deferral no longer serves a purpose: every input to the deferred
math is fixed once the record is registered, and the stored offsets are
containing-block-relative, so later subtree translations move the
containing block and the float together.

Place the float at the tail of layout_floating_box, right after the
last write to its sealed metrics, and delete place_floats_after_run().
Since placement seals the float's metrics, a post-registration write
now trips an assert instead of silently changing geometry.

No behavior change.
The inline-level iterator entered <br> through the box-model path, so
every break node acquired used values that nothing ever placed. They
were pure iterator scratch: a break node produces no line-box fragment
and no paintable, so the metrics computed for it at commit were
discarded without reaching the C++ side.

A <br> is a line-break control item, not a box. Skip it in the
iterator's box-model enter path so it never gets used values; an
overlooked reader now fails loudly on the missing-used-values assert.
Floating and absolutely positioned <br> are unaffected.

Box-model properties on a <br> no longer displace the first item of
the following line; the one affected expectation (a universal selector
put a 1px border on the <br>) is rebaselined.
A layout pass creates used values for its root boxes before any
formatting context runs, so no run is responsible for placing them: the
viewport and a standalone SVG document root reached commit with
has_content_offset unset, relying on default-initialized (0,0) offsets.
Subtree relayout already places its root when materializing it from the
previous paintable; extend that rule to every pass entry.

The full-layout entry now places its run root right after
run_formatting_context returns (the run still finalizes the root's used
sizes, so placement cannot happen earlier), and before the abspos pass
so the seal covers it. The viewport in the SVG-document case and the
subtree-relayout auxiliary viewport have final metrics at creation and
are placed on the spot.

Placement seals the roots' committed metrics, so a stray late write to
viewport geometry now trips an assert instead of passing silently. No
behavior change.
foreignObject content is rendered under the CSS model: an SVG element
inside it has no SVG rendering context until a nested <svg> establishes
a new one. The tree builder only tracked entering <svg> roots, so such
elements got layout boxes anyway; a block-level sibling then made the
BFC treat one as a block-level child, create used values for it, and
bail on the not-a-block-container path without ever placing it —
tripping the every-created-box-is-placed placement audit. The shape is
reachable via XML parsing or createElementNS (in HTML parsing,
foreignObject is an integration point).

Clear the SVG-context flag when entering a foreignObject, so its
SVG-namespace descendants are skipped by the existing suppression for
SVG elements outside an <svg> root. Referenced mask and clip-path
subtrees are SVG content in their own right wherever the referencing
element sits, so the resource walk now establishes SVG context
explicitly; a masked foreignObject previously relied on the leaked
flag.
@coderabbitai

coderabbitai Bot commented Aug 7, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 619ad993-f66d-4b62-9664-645d261e0f86

📥 Commits

Reviewing files that changed from the base of the PR and between f03d57b and 1ee1d44.

📒 Files selected for processing (8)
  • Libraries/LibWeb/Layout/TreeBuilder.cpp
  • Libraries/LibWeb/Rust/src/layout/block_formatting_context.rs
  • Libraries/LibWeb/Rust/src/layout/formatting_context.rs
  • Libraries/LibWeb/Rust/src/layout/inline_level_iterator.rs
  • Libraries/LibWeb/Rust/src/layout/tree_builder.rs
  • Tests/LibWeb/Layout/expected/block-and-inline/atomic-inline-with-percentage-vertical-align.txt
  • Tests/LibWeb/Layout/expected/svg/foreignObject-with-svg-namespace-child.txt
  • Tests/LibWeb/Layout/input/svg/foreignObject-with-svg-namespace-child.html

📝 Walkthrough

Walkthrough

The layout tree builder now handles SVG foreignObject traversal and restores SVG-root state. Float placement occurs during floating-box layout. Root layout paths place children at default offsets. Break nodes no longer add box-model metrics.

Changes

Layout updates

Layer / File(s) Summary
SVG foreign-content traversal
Libraries/LibWeb/Layout/TreeBuilder.cpp, Libraries/LibWeb/Rust/src/layout/tree_builder.rs, Tests/LibWeb/Layout/input/svg/..., Tests/LibWeb/Layout/expected/svg/...
Principal node facts identify SVG foreignObject elements. SVG traversal enters foreign content and restores prior root state. Tests cover namespace-aware descendants and traversal decisions.
Float and root offset placement
Libraries/LibWeb/Rust/src/layout/block_formatting_context.rs, Libraries/LibWeb/Rust/src/layout/formatting_context.rs, Tests/LibWeb/Layout/expected/block-and-inline/...
Float placement is committed during floating-box layout. Root, SVG-root, and subtree paths place children at default offsets. Layout expectations reflect the coordinate changes.
Inline break metric handling
Libraries/LibWeb/Rust/src/layout/inline_level_iterator.rs
Break nodes no longer add box-model metrics to the inline metric stack.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant LayoutTreeBuilder
  participant FfiPrincipalNodeEntryFacts
  participant RustTreeBuilder
  participant SvgTraversalState
  LayoutTreeBuilder->>FfiPrincipalNodeEntryFacts: provide is_svg_foreign_object
  FfiPrincipalNodeEntryFacts->>RustTreeBuilder: evaluate SVG entry decision
  RustTreeBuilder->>SvgTraversalState: clear SVG-root state for EnterForeignContent
  SvgTraversalState-->>RustTreeBuilder: restore prior state after traversal
Loading

Possibly related PRs

Suggested reviewers: tcl3

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description check ✅ Passed The description accurately states the layout invariant enforced by the float placement, root placement, and used-value changes.
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.

@kalenikaliaksandr
kalenikaliaksandr enabled auto-merge (rebase) August 7, 2026 21:02
@kalenikaliaksandr
kalenikaliaksandr merged commit 5ebd3f9 into LadybirdBrowser:master Aug 7, 2026
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