Skip to content

Make derived baselines an output of formatting context runs - #11050

Merged
kalenikaliaksandr merged 1 commit into
LadybirdBrowser:masterfrom
kalenikaliaksandr:layout-baselines-run-output
Aug 9, 2026
Merged

Make derived baselines an output of formatting context runs#11050
kalenikaliaksandr merged 1 commit into
LadybirdBrowser:masterfrom
kalenikaliaksandr:layout-baselines-run-output

Conversation

@kalenikaliaksandr

Copy link
Copy Markdown
Member

Baselines derived by a formatting context run were stored only on the run root's box record, and consumers read them back out of it after the run returned: line building for vertical-align on atomic inlines, flex baseline alignment, table row sizing, and measure_cell, which re-derived what the cell's run had already computed.

ChildLayoutResult now carries the derived first and last baselines, and those consumers read the result of the run they just invoked; boxes whose run was skipped or that are laid out by the enclosing run keep the stored-value path. The box_baseline helpers take the box's record as a parameter, so which box's record is read is visible at the call site.

A child's baselines are computed by its own run, so the result is their natural channel. This is groundwork for making a completed run's boxes private to that run: a run's result becomes a self-contained description of its subtree, which caching a run also requires, since on a cache hit the child's interior records never exist.

Baselines derived by a formatting context run were stored only on the
run root's box record, and consumers read them back out of it after
the run returned: line building for vertical-align on atomic inlines,
flex baseline alignment, table row sizing, and measure_cell, which
re-derived what the cell's run had already computed.

ChildLayoutResult now carries the derived first and last baselines,
and those consumers read the result of the run they just invoked;
boxes whose run was skipped or that are laid out by the enclosing run
keep the stored-value path. The box_baseline helpers take the box's
record as a parameter, so which box's record is read is visible at
the call site.

A child's baselines are computed by its own run, so the result is their
natural channel. This is groundwork for making a completed run's boxes
private to that run: a run's result becomes a self-contained description
of its subtree, which caching a run also requires, since on a cache hit
the child's interior records never exist.
@coderabbitai

coderabbitai Bot commented Aug 9, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The layout engine now propagates DerivedBaselines through child layout results, cached measurements, inline fragments, flex items, and table cells. Baseline calculations use explicit content baselines when available.

Changes

Derived baseline propagation

Layer / File(s) Summary
Baseline contract and layout-result propagation
Libraries/LibWeb/Rust/src/layout/formatting_context.rs, Libraries/LibWeb/Rust/src/layout/sizing_context.rs, Libraries/LibWeb/Rust/src/layout/used_values.rs, Libraries/LibWeb/Rust/src/layout/replaced_with_children_formatting_context.rs
Baseline helpers accept explicit DerivedBaselines. ChildLayoutResult and cached intrinsic measurements return baseline data.
Inline baseline capture and fragment storage
Libraries/LibWeb/Rust/src/layout/inline_formatting_context.rs, Libraries/LibWeb/Rust/src/layout/inline_level_iterator.rs, Libraries/LibWeb/Rust/src/layout/line_box_fragment.rs, Libraries/LibWeb/Rust/src/layout/line_builder.rs
Inline child layouts return baselines. Items and line fragments store them. Line updates use stored baselines for non-text fragments.
Flex item baseline alignment
Libraries/LibWeb/Rust/src/layout/flex_formatting_context.rs
Flex items retain child baselines. Baseline alignment uses them for maximum-baseline and offset calculations.
Table cell baseline resolution
Libraries/LibWeb/Rust/src/layout/table_formatting_context.rs
Measured and committed cell layouts return derived baselines. Cell baseline selection uses those values when available.

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

Sequence Diagram(s)

sequenceDiagram
  participant FormattingContext
  participant InlineFormattingContext
  participant LineBuilder
  participant FlexFormattingContext
  participant TableFormattingContext
  FormattingContext->>InlineFormattingContext: request child layout
  InlineFormattingContext->>LineBuilder: pass DerivedBaselines
  LineBuilder->>FormattingContext: use stored baselines
  FormattingContext->>FlexFormattingContext: provide child layout baselines
  FlexFormattingContext->>FormattingContext: compute baseline alignment
  FormattingContext->>TableFormattingContext: provide measured or committed baselines
  TableFormattingContext->>FormattingContext: resolve cell baseline
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description check ✅ Passed The description directly explains the baseline propagation changes and the affected layout consumers.
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.

Actionable comments posted: 1

🤖 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.

Inline comments:
In
`@Libraries/LibWeb/Rust/src/layout/replaced_with_children_formatting_context.rs`:
- Around line 69-73: Update layout_replaced_with_children and the
ReplacedWithChildren path in run_formatting_context to derive and store the
wrapper child’s baselines instead of returning DerivedBaselines::default().
Ensure the derived baselines are forwarded or explicitly passed to
store_derived_baselines so input elements with block shadow-tree children retain
the wrapper baseline.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 83f8082b-4363-4151-abde-27a40ba55099

📥 Commits

Reviewing files that changed from the base of the PR and between 6c50af5 and 40ac538.

📒 Files selected for processing (10)
  • Libraries/LibWeb/Rust/src/layout/flex_formatting_context.rs
  • Libraries/LibWeb/Rust/src/layout/formatting_context.rs
  • Libraries/LibWeb/Rust/src/layout/inline_formatting_context.rs
  • Libraries/LibWeb/Rust/src/layout/inline_level_iterator.rs
  • Libraries/LibWeb/Rust/src/layout/line_box_fragment.rs
  • Libraries/LibWeb/Rust/src/layout/line_builder.rs
  • Libraries/LibWeb/Rust/src/layout/replaced_with_children_formatting_context.rs
  • Libraries/LibWeb/Rust/src/layout/sizing_context.rs
  • Libraries/LibWeb/Rust/src/layout/table_formatting_context.rs
  • Libraries/LibWeb/Rust/src/layout/used_values.rs

Comment on lines 69 to 73
ChildLayoutResult {
automatic_content_inline_size: content_inline_size,
automatic_content_block_size: wrapper_layout.automatic_content_block_size,
baselines: DerivedBaselines::default(),
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Trace whether ReplacedWithChildren roots ever get derived baselines stored
# through a path other than layout_replaced_with_children.
set -euo pipefail

rg -n 'ReplacedWithChildren' Libraries/LibWeb/Rust/src/layout -g '*.rs' -C5

echo '--- is_html_input_element / input_derives_from_children usage ---'
rg -n 'is_html_input_element|input_derives_from_children' Libraries/LibWeb/Rust/src/layout -g '*.rs' -C5

echo '--- finalize_block_level_root definition ---'
rg -n 'fn finalize_block_level_root' Libraries/LibWeb/Rust/src/layout -g '*.rs' -A40

echo '--- store_derived_baselines call sites ---'
rg -n 'store_derived_baselines\(' Libraries/LibWeb/Rust/src/layout -g '*.rs' -B3 -A1

Repository: LadybirdBrowser/ladybird

Length of output: 19900


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- formatting_context.rs baseline functions ---'
sed -n '540,610p' Libraries/LibWeb/Rust/src/layout/formatting_context.rs

echo '--- run_formatting_context relevant section ---'
sed -n '1500,1630p' Libraries/LibWeb/Rust/src/layout/formatting_context.rs

echo '--- replaced_with_children_formatting_context.rs ---'
sed -n '1,130p' Libraries/LibWeb/Rust/src/layout/replaced_with_children_formatting_context.rs

echo '--- all DerivedBaselines default/store/receive occurrences ---'
rg -n 'DerivedBaselines::default|derive_baselines|derived_baselines_of_root_box|content_baselines_from_cells|has_first_baseline|first_baseline|baselines:' Libraries/LibWeb/Rust/src/layout -g '*.rs' -C2

Repository: LadybirdBrowser/ladybird

Length of output: 50380


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
from pathlib import Path
import re

root = Path("Libraries/LibWeb/Rust/src/layout")

def text(path):
    return path.read_text()

ctx = text(root / "formatting_context.rs")
repr = text(root / "used_values.rs")
bfc = text(root / "block_formatting_context.rs")
replaced = text(root / "replaced_with_children_formatting_context.rs")
inline = text(root / "inline_formatting_context.rs")
flex = text(root / "flex_formatting_context.rs")
grid = text(root / "grid_formatting_context.rs")
table = text(root / "table_formatting_context.rs")

for name, pattern, file in [
    ("derive_baselines", r"pub\(crate\) fn derive_baselines", root/"formatting_context.rs"),
    ("FinalizeAtomicRootBlock", r"fn finalize_atomic_root_block_size", root/"formatting_context.rs"),
    ("compute_and_store_baselines Call", r"self\.compute_and_store_baselines\s*\(", root/"block_formatting_context.rs"),
    ("DeriveBaselines Call", r"crate::layout::derive_baselines\s*\(", file),
]:
    idx = file.find(pattern)
    print(f"--- {name}: {file} ---")
    if idx == -1:
        print("not found")
        continue
    start = file.rfind("\n", 0, max(0, idx - 1000)) + 1
    end = min(idx + 2500, len(file))
    print(file[start:end])

# Read-only invariants focused on the baseline storage data flow.
print("--- invariants ---")

# In run_formatting_context ReplacedWithChildren branch, the result is returned directly and
# the only post-run block-level finalize is finalize_block_level_root().
rf = ctx.find("FormattingContextImplementation::ReplacedWithChildren => layout_replaced_with_children(run, body_input),")
final = ctx.find("fn finalize_block_level_root(", rf)
print(f"run_formatting_context ReplacedWithChildren returns before finalize_block_level_root after child layout: {0 < rf < final}")

# Locate finalize_block_level_root and check it does not call store_derived_baselines.
start = ctx.find("fn finalize_block_level_root(")
end = ctx.find("\nfn size_skipped_independent_root", start)
frag = ctx[start:end]
print(f"finalize_block_level_root calls store_derived_baselines: {'store_derived_baselines(' in frag}")
print(f"finalize_block_level_root records_replaced_block_size: {'record_replaced_block_size(' in frag}")

# Locate derive_baselines and check ReplacedWithChildren children are never treated as derived baselines.
start = ctx.find("// pub(crate) fn derive_baselines", None if "pub(crate) fn derive_baselines" not in ctx else None)
start = ctx.find("pub(crate) fn derive_baselines")
end = ctx.find("pub(crate) struct ChildLayoutResult", start)
ds = ctx[start:end]
print(f"derive_baselines handles child formatting-context boxes: {'is_flex_container' in ds or 'GridFormattingContext' in ds or 'TableFormattingContext' in ds or 'block_container' in ds}")
print(f"derivation first baseline source line count around child-derived baselines:")
for m in re.finditer(r"(has_first_baseline|first_baseline|children_are_inline|BlockFormattingContext)", ds):
    line = ds[:m.start()].count("\n") + 1
    print(f"  line {line}: {m.group()}")
PY

Repository: LadybirdBrowser/ladybird

Length of output: 274


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
from pathlib import Path
import re

root = Path("Libraries/LibWeb/Rust/src/layout")

def text(path):
    return path.read_text()

ctx = text(root / "formatting_context.rs")
repr = text(root / "used_values.rs")
bfc = text(root / "block_formatting_context.rs")
replaced = text(root / "replaced_with_children_formatting_context.rs")
inline = text(root / "inline_formatting_context.rs")
flex = text(root / "flex_formatting_context.rs")
grid = text(root / "grid_formatting_context.rs")
table = text(root / "table_formatting_context.rs")

fragments = [
    ("derive_baselines", root / "formatting_context.rs"),
    ("finalize_atomic_root_block_size", root / "formatting_context.rs"),
    ("compute_and_store_baselines in block FC", root / "block_formatting_context.rs"),
    ("derive_baselines calls", root / "block_formatting_context.rs"),
    ("box_baseline_with_content_baselines", root / "formatting_context.rs"),
    ("input_derives_from_children handling", root / "formatting_context.rs"),
    ("derive_baselines implementation", None),
]
for (name, file) in fragments:
    print(f"--- {name}: {file or 'formatting_context.rs'} ---")
    target = file.read_text() if file else ctx
    if name == "derive_baselines implementation":
        start = target.find("pub(crate) fn derive_baselines")
        end = target.find("pub(crate) struct ChildLayoutResult", start)
    else:
        start = target.find("pub(crate) fn " + name)
        if start == -1:
            start = target.find("fn " + name)
        if start == -1:
            print("not found")
            continue
        if name == "derive_baselines implementation":
            end = target.find("pub(crate) struct ChildLayoutResult", start)
        else:
            next_new = target.find("\nfn ", start + 1)
            end = target.find("\n}", target.rfind("{", start, next_new if next_new != -1 else len(target))) + 2 if next_new != -1 else len(target)
            if target.find("\n)", end) != -1:
                end = target.find("\n)", end) + 2
            end = min(end, target.find("\n}", end) + 2)
    print(target[start:end])

print("--- invariants ---")

rf = ctx.find("FormattingContextImplementation::ReplacedWithChildren => layout_replaced_with_children(run, body_input),")
final_start = ctx.find("fn finalize_block_level_root(", rf)
print(f"run_formatting_context ReplacedWithChildren branch exists: {rf != -1}")
print(f"finalize_block_level_root exists after ReplacedWithChildren case: {final_start != -1}")
print(f"finalize_block_level_root contains store_derived_baselines: {'store_derived_baselines(' in ctx[final_start:ctx.find('fn size_skipped_independent_root', final_start)]}")
print(f"finalize_block_level_root contains record_replaced_block_size: {'record_replaced_block_size(' in ctx[final_start:ctx.find('fn size_skipped_independent_root', final_start)]}")

ds_start = ctx.find("pub(crate) fn derive_baselines")
ds_end = ctx.find("pub(crate) struct ChildLayoutResult", ds_start)
ds = ctx[ds_start:ds_end]
print(f"derive_baselines handles block container children: {'child_state.content_baselines_from_cells' in ds}")
print(f"derive_baselines handles flex container children: {'fctx.derived_baselines_of_root_box' in ds}")
print(f"derive_baselines handles grid container children: {'gctx.derived_baselines_of_root_box' in ds}")
print(f"derive_baselines handles table formatting context: {'TableFormattingContext' in ds}")

stored_calls = []
for p in [root / "block_formatting_context.rs", root / "inline_formatting_context.rs", root / "flex_formatting_context.rs", root / "grid_formatting_context.rs", root / "table_formatting_context.rs"]:
    pc = p.read_text()
    if "crate::layout::store_derived_baselines(" in pc:
        for m in re.finditer(r"crate::layout::store_derived_baselines\s*\(", pc):
            line = pc[:m.start()].count("\n") + 1
            stored_calls.append((p, line, pc[pc.rfind("\n", 0, m.start()-1):pc.find("\n", m.start()) + 1].strip()))
print("store_derived_baselines call sites:")
for p,line,snippet in stored_calls:
    print(f"  {p}:{line} {snippet}")
PY

Repository: LadybirdBrowser/ladybird

Length of output: 50380


Store baselines for ReplacedWithChildren roots.

layout_replaced_with_children always returns DerivedBaselines::default(), and run_formatting_context does not call store_derived_baselines for ReplacedWithChildren roots. Derive and forward the wrapper baseline result from layout_replaced_with_children, or explicitly derive/store baselines from the wrapper child instead of relying on finalize_block_level_root, so <input> elements with block shadow-tree children do not collapse to the margin-box bottom edge.

🤖 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/replaced_with_children_formatting_context.rs`
around lines 69 - 73, Update layout_replaced_with_children and the
ReplacedWithChildren path in run_formatting_context to derive and store the
wrapper child’s baselines instead of returning DerivedBaselines::default().
Ensure the derived baselines are forwarded or explicitly passed to
store_derived_baselines so input elements with block shadow-tree children retain
the wrapper baseline.

@kalenikaliaksandr
kalenikaliaksandr merged commit 825fe44 into LadybirdBrowser:master Aug 9, 2026
13 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