Skip to content

Layout underinvalidation fixes - #11105

Merged
kalenikaliaksandr merged 3 commits into
LadybirdBrowser:masterfrom
kalenikaliaksandr:layout-invalidation-fixes
Aug 12, 2026
Merged

Layout underinvalidation fixes#11105
kalenikaliaksandr merged 3 commits into
LadybirdBrowser:masterfrom
kalenikaliaksandr:layout-invalidation-fixes

Conversation

@kalenikaliaksandr

Copy link
Copy Markdown
Member

See commit descriptions

Changing colspan, rowspan, or a column span synchronized the new span
into the layout node's data and stopped there: the attributes are not
presentational hints, so no style recompute follows, and nothing marked
the layout tree dirty. A pass that had nothing else to do would skip
relayout entirely and the table kept its old track assignment; with
cached formatting-context runs the stale spans would also outlive the
next genuine pass.

The span sync reports whether any effective value actually changed and
the attribute-change site invalidates layout only on a report, so a
rewritten attribute string with the same effective spans stays a no-op.
The raw column span is stored only for table column elements, whose
column boxes are its only consumer, keeping span attributes on
arbitrary elements out of the arena map.
The default preferred size of a text control follows the input size
and textarea rows/cols attributes, but those attributes are not
presentational hints and had no invalidation at all: changing them
left layout untouched until an unrelated pass happened to run, which
then rebuilt the replaced-content facts through the enrolled per-pass
sync and picked the new size up incidentally.

Mark the control's layout node for update from the attribute-change
sites, so the change takes effect immediately. Select's size attribute
is outside the facts channel and keeps its pre-existing gap.
Rendered text under a casing text-transform is keyed on the language
for locale-sensitive casing, but lang reaches no computed style group,
so the language invalidator's style-only walk never re-rendered the
text or marked layout dirty: the old casing stayed on screen until an
unrelated relayout happened to rebuild the text through the lazy
text-for-rendering key comparison.

The language invalidator now marks text nodes under casing transforms
for re-rendering and relayout, so the change takes effect immediately,
and rebuilds the layout tree for first-letter slices, whose boundaries
depend on the rendered text.
@coderabbitai

coderabbitai Bot commented Aug 12, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The PR adds targeted layout invalidation for language changes under casing transforms, input size changes, textarea rows and cols changes, and table span changes. Table span synchronization now reports whether effective or raw data changed. New layout and text tests verify casing relayout, form-control growth, table width updates, and equivalent span values that produce no additional layout.

Sequence Diagram(s)

sequenceDiagram
  participant DOMElement
  participant LanguageInvalidator
  participant LayoutTextNode
  participant LayoutTree
  DOMElement->>LanguageInvalidator: change lang attribute
  LanguageInvalidator->>LayoutTextNode: inspect casing transform
  LayoutTextNode->>LayoutTree: request text rendering or layout-tree invalidation
  LayoutTree-->>DOMElement: produce updated layout and paint output
Loading

Possibly related PRs

Suggested reviewers: awesomekling, tcl3

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Description check ❓ Inconclusive The description only says "See commit descriptions" and does not explain the layout invalidation changes. Add a brief summary of the fixes for table spans, text control sizing, and language changes under casing-related text transforms.
✅ Passed checks (2 passed)
Check name Status Explanation
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.

🧹 Nitpick comments (3)
Tests/LibWeb/Text/input/table-colspan-change-relayout.html (1)

18-30: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add coverage for raw <col span> changes.

This test covers only HTMLTableCellElement::col_span(). Add a <col> case that changes span from 1001 to 1002; both effective spans are 1000, but the raw spans differ. Assert the updated table-track geometry and layout delta.

🤖 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 `@Tests/LibWeb/Text/input/table-colspan-change-relayout.html` around lines 18 -
30, Extend the test beyond the existing HTMLTableCellElement colspan case with a
raw <col span> scenario changing from 1001 to 1002. Verify the effective
table-track geometry remains 1000 while asserting the expected layout delta
after forcing layout, and retain the existing no-op coverage for equivalent cell
spans.
Tests/LibWeb/Layout/input/lang-change-casing-text-transform-relayout.html (1)

1-6: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add coverage for the Layout::TextSliceNode path.

This test covers a regular text node under text-transform: uppercase. It does not exercise LanguageInvalidator.cpp Lines 39-43, where a language change must rebuild the layout tree for a first-letter slice. Add a ::first-letter case with a language change and update the expected dump.

🤖 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 `@Tests/LibWeb/Layout/input/lang-change-casing-text-transform-relayout.html`
around lines 1 - 6, Extend the HTML test to include a ::first-letter
text-transform case that changes the element language from English to Turkish,
exercising the Layout::TextSliceNode path and LanguageInvalidator relayout
behavior. Update the corresponding expected layout dump to reflect the rebuilt
first-letter layout after the language change, while preserving the existing
regular-text coverage.
Tests/LibWeb/Text/input/textarea-rows-attribute-relayout.html (1)

11-15: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Cover cols changes in this regression test.

The test changes only rows, but Libraries/LibWeb/HTML/HTMLTextAreaElement.cpp handles both rows and cols on Lines 449-455. Add a cols mutation and verify that the width grows. This prevents a regression in the column-size path from passing unnoticed.

Proposed test extension
         const heightAfter = document.getElementById("field").getBoundingClientRect().height;
+        const widthBefore = document.getElementById("field").getBoundingClientRect().width;
+        document.getElementById("field").setAttribute("cols", "40");
+        const widthAfter = document.getElementById("field").getBoundingClientRect().width;
 
-        println(`grew: ${heightAfter > heightBefore}`);
+        println(`grew: ${heightAfter > heightBefore && widthAfter > widthBefore}`);
🤖 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 `@Tests/LibWeb/Text/input/textarea-rows-attribute-relayout.html` around lines
11 - 15, Extend the regression test around the existing field measurements to
cover the cols mutation as well as rows: record the textarea width before
changing cols, set the cols attribute to a larger value, record the width after,
and print/assert that the width increased. Keep the existing rows height-growth
check unchanged and use the same field element.
🤖 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.

Nitpick comments:
In `@Tests/LibWeb/Layout/input/lang-change-casing-text-transform-relayout.html`:
- Around line 1-6: Extend the HTML test to include a ::first-letter
text-transform case that changes the element language from English to Turkish,
exercising the Layout::TextSliceNode path and LanguageInvalidator relayout
behavior. Update the corresponding expected layout dump to reflect the rebuilt
first-letter layout after the language change, while preserving the existing
regular-text coverage.

In `@Tests/LibWeb/Text/input/table-colspan-change-relayout.html`:
- Around line 18-30: Extend the test beyond the existing HTMLTableCellElement
colspan case with a raw <col span> scenario changing from 1001 to 1002. Verify
the effective table-track geometry remains 1000 while asserting the expected
layout delta after forcing layout, and retain the existing no-op coverage for
equivalent cell spans.

In `@Tests/LibWeb/Text/input/textarea-rows-attribute-relayout.html`:
- Around line 11-15: Extend the regression test around the existing field
measurements to cover the cols mutation as well as rows: record the textarea
width before changing cols, set the cols attribute to a larger value, record the
width after, and print/assert that the width increased. Keep the existing rows
height-growth check unchanged and use the same field element.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: c5a0ee51-be79-4a3c-852a-dd6768bfb4c8

📥 Commits

Reviewing files that changed from the base of the PR and between 61f47d8 and 71b7400.

📒 Files selected for processing (16)
  • Libraries/LibWeb/CSS/Invalidation/LanguageInvalidator.cpp
  • Libraries/LibWeb/DOM/Element.cpp
  • Libraries/LibWeb/DOM/Node.h
  • Libraries/LibWeb/HTML/HTMLInputElement.cpp
  • Libraries/LibWeb/HTML/HTMLTextAreaElement.cpp
  • Libraries/LibWeb/Layout/Node.cpp
  • Libraries/LibWeb/Layout/Node.h
  • Libraries/LibWeb/Rust/src/layout/layout_node_arena.rs
  • Tests/LibWeb/Layout/expected/lang-change-casing-text-transform-relayout.txt
  • Tests/LibWeb/Layout/input/lang-change-casing-text-transform-relayout.html
  • Tests/LibWeb/Text/expected/input-size-attribute-relayout.txt
  • Tests/LibWeb/Text/expected/table-colspan-change-relayout.txt
  • Tests/LibWeb/Text/expected/textarea-rows-attribute-relayout.txt
  • Tests/LibWeb/Text/input/input-size-attribute-relayout.html
  • Tests/LibWeb/Text/input/table-colspan-change-relayout.html
  • Tests/LibWeb/Text/input/textarea-rows-attribute-relayout.html

@kalenikaliaksandr
kalenikaliaksandr merged commit ca63334 into LadybirdBrowser:master Aug 12, 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