LibWeb: Port layout tree construction to Rust - #10838
Merged
Merged
Conversation
Add a dedicated Rust crate for layout and wire its generated FFI into LibWeb. Move the replaced-element table display adjustment into Rust as the first production TreeBuilder decision, with unit and layout test coverage establishing the new boundary.
Move the CSS table-model fixup traversal and decisions into the Rust layout crate. Keep C++ callbacks limited to querying layout-node facts, mutating the existing tree, constructing style-backed anonymous boxes, and exposing TableGrid occupancy. This covers irrelevant-box removal, missing wrapper and parent generation, table-wrapper reuse, and missing-cell generation. Existing table layout coverage exercises the migrated path.
Move insertion-parent selection and inline/block normalization into the Rust tree builder. Expose ownership-safe C++ callbacks for anonymous wrapper construction, sibling migration, insertion, and tree update confinement tracking. Preserve special handling for fieldsets, flex and grid containers, SVG layout, out-of-flow boxes, pseudo-elements, and table-internal boxes.
Move the CSS first-letter text pattern and layout-descendant search to Rust. Keep Unicode category lookup, grapheme boundaries, and concrete text-slice and wrapper construction behind the C++ bridge. Preserve marker exclusion, nested block boundaries, generated text, preserved-newline behavior, and trailing punctuation handling.
Store the active insertion ancestor stack and generated-content quote depth in the Rust tree builder. Keep narrow C++ accessors for current callers while making the state available to the upcoming recursive Rust construction pass. Destroy the state with its owning TreeBuilder and prevent accidental copies of the opaque Rust allocation.
Move ordered traversal of DOM children, shadow-root children, assigned slot nodes, and SVG switch children into the Rust tree builder. Keep the per-node update and DOM access operations behind typed C++ callbacks. Preserve forced slot-subtree creation, shadow-root flag clearing, and cleanup of non-rendered SVG switch children.
Move generated pseudo-element suppression, normal marker handling, content replacement, and display-contents decisions into the Rust tree builder. Keep style lookup and concrete box construction in C++. Cover the decision matrix with Rust unit tests while retaining existing pseudo-element, marker, and CSS content rendering behavior.
Move button-content and fieldset-content wrapper selection into the Rust tree builder. Retain C++ callbacks for computed-style construction and ownership-safe migration of the existing child nodes. Preserve button flex centering and fieldset legend exclusion behavior.
Move the complete display-contents update lifecycle into the Rust tree builder. Rust now coordinates top-layer masking, stale subtree cleanup, counter resolution, generated pseudo-elements, shadow and DOM children, assigned slot nodes, and dirty-flag clearing. Keep DOM access and pseudo-element construction behind typed C++ callbacks.
Move layout-node rebuild selection, top-layer admission, and SVG container gating into the Rust tree builder. Preserve C++ operations for zone-rebuild scheduling and temporary SVG context storage. Cover incremental, top-layer, and SVG entry combinations with a Rust unit test.
Move principal box suppression, display-contents selection, partial replacement classification, rebuild-root tracking, top-layer flags, and insertion selection into the Rust layout tree builder. Keep ownership-sensitive box replacement and insertion in typed C++ callbacks.
Move the principal box descendant walk, top-layer processing, slot projection, containment quote scoping, and final dirty-flag cleanup into the Rust tree builder. Keep DOM and layout object access behind a C++ callback table while Rust owns the traversal order and decisions. Snapshot the document top-layer set in one C++ iteration before Rust walks it, keeping rebuilds linear in the number of top-layer members.
Move generated pseudo-element ordering, SVG resource recursion and cycle checks, pattern deduplication, first-letter creation, and specialized wrapper dispatch into the Rust tree builder. Remove the obsolete C++ before-children and after-children methods.
Let an opaque C++ frame retain old and new layout nodes. Rust now owns entry filtering, box generation, SVG-root scoping, and placement. Rust also owns rebuild-root confinement and stale cleanup while preserving restoration order. The C++ method only sets up the frame and dispatches.
Move document build setup and table fixup dispatch into Rust. Delete the C++ traversal and fixup forwarding methods that became unused after the central update port.
Move pseudo-element generation, insertion, counters, content resolution, quote updates, and generated child sequencing into Rust. Retain computed styles and C++ nodes in an opaque frame. Expose them through focused callbacks.
Move content replacement and SVG resource box selection into Rust. Move the one-shot SVG construction context updates there as well. Delete the superseded C++ element construction helpers and an unused declaration.
Move top-layer detachment, assigned-slot cleanup, SVG resource ancestor checks, and remaining wrapper and backdrop decisions into Rust. Keep C++ callbacks focused on object access and mutation, while retaining new backdrop nodes across split creation and insertion.
Keep traversal and decision helpers private now that the complete tree build is orchestrated within Rust. This removes unused functions and decision types from the generated C++ interface.
Select and order the children of anonymous button and fieldset wrappers in Rust. Keep C++ responsible for creating styled layout nodes and moving retained nodes across ownership boundaries.
Return the layout root and incremental rebuild report together from a free layout-tree build function. Stop making Document manage a C++ TreeBuilder object solely to retrieve the report after construction.
Make Rust own recursive traversal and transient context instead of bouncing through C++ for every node. Keep reusable C++ frames in a stack whose lifetime follows the Rust recursion.
Own the ancestor stack and quote nesting inside Rust. Pass only the current parent to C++ operations that need it, and remove the state lifecycle and accessor FFI.
Expose layout tree construction as a free function driven by Rust. Keep C++ ownership and mutation callbacks in a private bridge, with a narrow authority type for protected DOM and SVG operations.
Run first-letter traversal, button and fieldset wrapping, and table fixup directly from the active Rust tree build. Remove the redundant C++ callbacks and Rust entry points that re-entered these operations.
Create pseudo-elements and insert principal, generated, and content nodes without re-entering Rust through C++. Retain C++ frame storage only for objects whose ownership cannot cross the FFI boundary.
Traverse assigned slottables directly from the active Rust operation. Share the C++ assigned-node accessors between normal tree building and top-layer detachment instead of re-entering a separate Rust function.
Prepare Unicode segmentation through the layout-tree host, then perform first-letter matching without re-entering a standalone Rust function. Keep the prepared context alive for the duration of the Rust scan.
Collect the style and whitespace facts needed for text layout through the active tree-build host. Decide whether display-contents text needs an inline style wrapper without calling a standalone Rust function.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (17)
📝 WalkthroughWalkthroughChangesLayout tree builder Rust port
Estimated code review effort: 5 (Critical) | ~120 minutes Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
Comment |
This was referenced Jul 25, 2026
This was referenced Aug 4, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
We are incrementally moving LibWeb's style, layout, and display-list recording pipeline to Rust. This PR ports
Layout::TreeBuilder, the stage that turns the styled DOM and flat tree into the layout tree, as the first complete layout-side step.Rust now owns the traversal, transient builder state, incremental subtree reconstruction, principal and pseudo-element box decisions, shadow and slot traversal, top-layer and SVG handling, inline/block normalization,
::first-letterselection, specialized wrappers, and table fixup. C++ remains responsible for accessing existing DOM and style objects, constructing concrete layout nodes, and preservingRefPtrownership through narrow callbacks.The public C++
TreeBuilderclass is removed, and an active build enters Rust once and stays there through recursive construction, insertion, pseudo-element creation, and postprocessing. Existing behavior is preserved, with Rust unit tests covering the ported decisions and LibWeb tests covering the resulting trees and incremental updates.This gives future Rust formatting-context ports a Rust-owned tree construction pipeline. From there we can continue moving layout state and algorithms to Rust, followed by painting and display-list recording.