Every SVG element insertion, removal, attribute change, and children
change walked the entire document looking for use elements to notify
about possible referenced-subtree changes. On pages with large SVG
documents this is quadratic: loading chatgpt.com spent 7% of all CPU
samples in these full-document scans, nearly all of it while parsing
an SVG icon sprite sheet.
Instead, keep every use element connected to a document's node tree in
an intrusive list owned by that document, and only iterate that list
(usually empty or tiny) when an SVG element changes.
Subtleties:
- A use element inserted by the same subtree insertion as its
referenced element, but after it in tree order, used to be found by
the document-wide scan from the referenced element's insertion
steps. Now SVGUseElement::inserted() re-resolves the reference if
the shadow tree is still unpopulated. A new test covers both tree
orders.
- Node.moveBefore() runs moving steps without insertion or removal
hooks. Now SVGUseElement::moved_from() updates list membership when
moving across document-tree and shadow-tree boundaries. A new test
covers both directions.
- Removal hooks run after the subtree has been detached, so use
elements being removed alongside the changed element may still be
registered. Filter them out structurally via root().is_document(),
since Node::is_connected() is a flag that is updated in hook order
and can still be stale at this point.
Keep decoded image resources alive while they are backing a CSS image
resource for the document. Pruning these entries can make background
images temporarily non-paintable during display-list recording, causing
visible blank frames until the resource is requested again.
Move PropertyNameAndID, custom property data, registered custom
properties, and Typed OM associated property names to Utf16FlyString.
This removes the FlyString storage boundary from CSS property-name
handling and lets CSSStyleProperties keep the name it receives from
CSSOM instead of converting it back to UTF-8.
Text blocks used by find-in-page can contain positions for nodes that no
longer form a valid live Range by the time matches are converted back to
DOM ranges. The offset checks handled stale text lengths, but endpoints
could also be disconnected or belong to different roots.
Reject those stale matches before constructing the Range. Add reduced
crash coverage for inserting text after a textarea and immediately using
window.find() to select that text.
Find-in-page builds live ranges from layout text block offsets. If the
DOM text node no longer contains the cached offset, constructing the
Range violates its boundary invariants.
Skip stale matches whose mapped offsets are outside the current text
node length. Add coverage for mutating text before window.find().
A queued document unload can capture child navigables whose active
document is gone by the time the task runs. In that state there is no
child document to unload, but the parent's lifecycle counter still needs
to advance.
Run the unload completion step directly when a child navigable has no
active document. Add coverage for removing an iframe during a child
navigation.
Move the layout tree from GC allocation to refcounted ownership so
removed layout and paint subtrees are destroyed synchronously instead
of waiting for the next GC sweep. This dramatically reduces GC memory
usage peaks after layout tree churn and makes it easier for memory use
to fall back after large document updates.
Update layout factories, tree traversal, SVG layout node creation,
paintable back-pointers, and pseudo-element layout links to use RefPtr
ownership.
Make display: contents follow the same shape as Blink and WebKit: the
element itself does not create a layout node, and its children are
flattened into the nearest layout parent. Wrap direct non-whitespace
text in an anonymous inline node when the boxless element contributes
inherited style to that text.
Use an internal inline wrapper for display: contents pseudo-elements
so generated content can still participate in layout, painting, hit
testing, and pseudo-element queries. Keep CSSOM reporting the computed
display value from the pseudo style, not the internal wrapper.
Remove the retained out-of-tree layout node list and its testing hook,
since the flattened model does not need a side owner for boxless
elements. Add coverage for inherited text style, dynamic insertion
order, pseudo-element hit testing, and computed style queries.
Move ComputedProperties and CascadedProperties out of the GC. They no
longer contain strong references to GC-managed data.
Keep computed styles alive from DOM elements and animation updates with
RefPtr. Pass style into layout constructors by reference, since layout
only copies the values it needs while building nodes.
Use GC::Weak for cascade source links, so entries no longer keep the
style declaration or shadow root alive.
Move the SharedResourceRequest, animation timer, and current frame
state out of ImageStyleValue and into a Document-owned table keyed
by resolved image URL. ImageStyleValue now keeps only URL metadata
and its client list, so image style values no longer need to trace
GC edges themselves.
Thread the Document through AbstractImageStyleValue APIs that need
decoded image data. CSS image fetches snapshot the stylesheet base URL,
referrer behavior, and origin-clean state instead of retaining the
stylesheet.
Remember each client's registered resolved URL when unregistering. This
keeps a later document base change from leaving an animated image
resource alive.
Add text coverage for inline relative image base URLs, stylesheet
referrers, imported stylesheet origin-clean behavior, inline @import
initiator type, and unregistering an animated background image after a
base element change.
Collect the style rules that apply to an inspected element and expose
them through the existing DOM node inspection path. This gives Firefox's
Rules panel real rule forms instead of the previous empty getApplied
response.
Enable -Wexit-time-destructors for all in-tree library targets and
update process-lifetime library statics so they no longer register
exit-time destructors. Long-lived caches, lookup tables, singleton
registries, and generated constants now use NeverDestroyed or leaked
references where the data is intended to live until process exit.
Update LibWeb, LibLine, and the binding generators so regenerated
sources follow the same rule instead of reintroducing destructed
statics.
Visual viewport scroll and pinch zoom used to invalidate the full
accumulated visual context tree and display list, even though those
changes only modify the root visual viewport transform. That forced a
full display list rerecord before sending updated compositor state.
Patch the reserved visual viewport AVC node in place instead.
VisualViewport marks the tree for compositor update without
invalidating the display list, and Navigable sends the replacement tree
through the new IPC path before updating scroll state.
The accumulated visual context tree used index 0 as a null sentinel, so
visual viewport transforms were only represented by adding a normal
transform node when the transform was non-identity. That made callers
treat index 0 as a special no-context value and kept the tree shape
dependent on the visual viewport state.
Reserve index 0 as the visual viewport transform node instead. AVC
traversal, display-list replay, hit testing, debug dumping, and root
paint state now treat that node as a real root. Rebaseline the affected
display-list and async-scrolling text expectations so the explicit root
node appears in AVC dumps.
Make navigable focus depend on whether the page client currently has
system focus. WebContent already receives focus changes from the UI
process, but LibWeb did not consult that state when deciding whether the
top-level traversable was focused.
Repaint the text caret when WebContent focus changes so a focused text
control stops showing an active caret as soon as browser chrome takes
focus, and resets the blink cycle when page focus returns.
Grid and flex inspector payloads are only consumed by DevTools, but
layout currently builds them for every page. Gate that collection on an
active DevTools client so ordinary browsing avoids the extra CPU work
and retained memory.
DevTools may connect after the page has already completed layout, so
force one catch-up layout pass when the first DevTools client attaches.
After that, normal relayouts keep the data fresh until DevTools
disconnects. Inspection requests only flush dirty layout instead of
forcing repeated collection passes.
When DevTools detaches, clear the stored inspection data and overlays.
The accessibility tree builder consults layout nodes while applying
inclusion and exclusion rules. DevTools can request an accessibility
tree after style or layout has been dirtied, which made those
layout-node lookups trip the stale-layout verification.
Update layout before serializing the accessibility tree, matching the
DOM tree dump path. Add an internals regression test that dirties
layout before requesting an accessibility dump.
Cached display list command sequences used to carry their own
DisplayListResourceStorage. That kept resource ID sets and referenced
fonts, images, video frames, and nested display lists alive on every
cached phase, even though the command bytes already contain enough
information to discover those references when they are needed.
This makes cached command sequences store only command bytes. Resource
references are collected transiently from those bytes when a cache entry
is installed or invalidated. The navigable's central display list
resource storage now keeps cache reference counts, so compositor pruning
retains resources used by live cached commands without duplicating
storage in each sequence.
Use selection-specific caret hit testing while starting and extending
mouse selections. The public caret-position API keeps its normal line
ranking, but selection drags now snap below-line movement to line edges
and prefer the previous line when starting in a nearby inter-line gap.
Add coverage for dragging from message text, after-text space, gutters,
author names, avatar-adjacent areas, and row bottoms so these inert
message regions reliably start selection.
Add a debug-menu toggle for caret hit testing at the mouse position.
Paint the insertion rect and log the result so selection bugs can be
inspected without temporary probes.
Request frames and repaint invalidation when the overlay state changes.
Also repaint when the caret rect moves within the same text node.
Add the CSSOM View CaretPosition interface and expose the Document API.
Use retained hit-test data to populate offsetNode, offset, and
getClientRect().
Update IDL coverage and the window property baseline. Add a text test.
Build a hit-test display list while recording paint output. Use it as
source of truth for point hit testing instead of recursively walking the
paintable tree in reverse paint order.
The retained list records target paintables, visual context indices,
border radii, caret rects, and line metadata needed by hit testing. It
also keeps a spatial index so point queries inspect nearby items before
checking containment in paint order.
Refresh scroll state before hit testing so visual context transforms use
current scroll offsets. Add text tests for rounded hit regions and
selection across non-text content.
If a page contains multiple <link rel="icon"> elements, we would send
each of them to the UI process. We would then just use whichever was
sent last as the favicon in the UI.
We now only send the favicon that was chosen for the document. This
will either be the largest icon decoded from a link element, or the
singular fallback icon.
We currently pick the first favicon in reverse tree order. But we are
encouraged by the spec to pick the most appropraite icon. We now
consider the size of the decoded icon, and choose the largest.
Only use the preferred color scheme for a loading top-level canvas
when the root used color scheme has not been computed yet. Once a
layout tree exists, non-opted pages still resolve system colors with
the default light scheme, so forcing a dark canvas makes them disagree
until parsing finishes.
Extend the loading canvas test to cover the non-opted case and reset
the test-only preferred color scheme override before finishing.
Only use the loading-time top-level canvas fallback when the root
color-scheme value is still normal. An explicit light scheme on the
root element already gives the canvas a light used scheme, so replacing
it with the preferred dark scheme makes the viewport disagree with the
computed system colors until readiness advances.
Add internals coverage for the document canvas scheme and a text test
that exercises the loading state with a dark preferred color scheme.
Use document color schemes when choosing the canvas color for a root
whose color-scheme property is still normal. This keeps the transient
canvas aligned with the preferred scheme once a meta color-scheme has
been parsed, before style later recomputes the root used color scheme.
This avoids a light canvas frame while loading dark-capable pages in
dark mode without changing pages that explicitly force the root color
scheme.
Recompute supported color schemes through a setter that invalidates
style and media queries. Use the preferred color scheme for a loading
top-level document until metadata or root style gives it a more specific
canvas scheme.
Add coverage for dynamically inserting a meta color-scheme element.
Opt empty about:blank documents into both light and dark supported
color schemes. This covers both navigation-created about:blank documents
and the initial about:blank document created with a browsing context, so
dark mode can choose the dark Canvas color before page content arrives.
Display lists owned the accumulated visual context tree through a
ref-counted pointer. That tied visual-context state to display-list
lifetime and made compositor updates treat the two as one unit, even
though AVC trees need to become independently updateable compositor
state.
Make accumulated visual context trees plain versioned values, have each
display list store the compatible tree version, and pass the matching
tree alongside display-list updates and replay calls. Replay verifies
that the provided tree matches the display list before executing it.
This prepares the compositor for receiving AVC tree updates separately
from display-list updates: it now accepts the tree as a separate update
parameter, stores it next to the display list, and uses that stored tree
for replay and async-scroll hit testing. Nested display-list resources
carry their own tree snapshots for the same version check.
Add IPC structures and requests for inspecting grid layout data from
WebContent.
WebView forwards the async replies to LibDevTools and exposes the grid
highlighter hooks used by the protocol layer.
Parse Firefox grid highlighter options in WebContent and store them with
the active grid highlight.
Snapshot DevTools DOM mutation payloads immediately, but defer subtree
serialization until the target document's layout is up to date.
This avoids re-entering layout-backed DOM serialization from style and
layout updates, which could crash WebContent while DevTools was
listening for mutations.
Track whether WebContent still needs a beforeunload check and let the
frontends immediately remove a tab or window when no prompt can be
shown. WebContent still receives the close request so pagehide, unload,
and cleanup steps can run.
When the visible view is removed immediately, keep detached ownership of
the WebContent page until it reports that the top-level traversable
closed. If no acknowledgement arrives, release detached ownership and
ask ProcessManager to shut down the unused WebContent process.
Use the existing animated style update path when document style
recalculation reaches an element through inherited style recomputation.
Paused or filled animations can contain inherit, rem, rch, or other
font-relative keyframe values that depend on parent or root metrics.
Those dependencies can change even when the target's inherited computed
values do not otherwise change.
Make the document traversal opt in explicitly so animation-driven
inherited recomputation does not schedule another animation update while
propagating animated inherited values.
Add coverage for a media-gated root font-size change affecting a paused
rem-based animation. Update the font-stretch WPT expectation now that
its inherited-animation case passes.
Use the viewport metric dependency flags to restyle only elements whose
computed values can change after a viewport resize. Descendants that
inherit changed values are reached through the existing inherited-style
update path.
Keep targeted style reads correct by treating pending media query
evaluation as style dirtiness. Seed the style computer with the latest
viewport before resolving pending animated style, so viewport-unit
keyframes do not use stale metrics.
Share pseudo-element recomputation with inherited-style updates, so
pseudos stay current when their originating element changes only via
inherited values.
Schedule animated style updates when inherited style recomputation can
affect existing animations.
Add viewport resize coverage for media queries, inherited font metrics,
monospace font-size recascade, line-height percentages, font-relative
and pending viewport-unit animations, inherited pseudo-elements, direct
pseudo viewport dependencies, and canvas currentColor reads.
Use GC::WeakHashSet for the registry instead of IGNORE_GC. The notify
and layout update paths now snapshot the live weak entries into
RootVector before iterating, preserving the existing protection against
mutation while avoiding an unvisited strong GC container field.
Request a rendering update after document visibility changes back to
visible. Hidden documents can leave animation frame callbacks or CSS
animation work pending after their last rendering update is skipped,
so showing the page needs to schedule a fresh rendering tick.
Async scrolling display-list items were still recorded by helper
functions in LibWeb/Compositor even though they are produced while
walking the paint tree. That kept paint-time knowledge about boxes,
viewport state, and wheel-event regions in the compositor-facing code.
Move viewport setup and final metadata emission to ViewportPaintable,
and move per-box scroll-node, hit-test, scrollbar, wheel-region, and
sticky-area item recording to PaintableBox. AsyncScrollingState now
keeps the compositor-side conversion and lookup logic for consuming the
recorded metadata.
Notify the UI process with the solid canvas background color recorded
for the top-level document. This is the Canvas system color with the
effective document background composited over it, matching the color
used before normal painting.
Store that color on the view and use it when AppKit, Qt, and Gtk need
to fill areas exposed while an older bitmap is still on screen during a
window resize.
The layout loop added for container queries intentionally caps repeated
style/layout passes, since each layout can enqueue more size-query style
work. That cap should still apply, but absence of post-layout style work
does not by itself mean the document is clean.
Only leave the loop when there is no pending post-layout style work and
layout is up to date. Otherwise spend another capped pass flushing the
layout-only invalidation instead of falling through to the final
layout_is_up_to_date() verification.
Add crash coverage for a clientHeight read after nested container query
invalidations, matching the synchronous layout flush path.
When an embedded document was render-blocked, the parent display list
recording skipped drawing that iframe's compositor surface. Once the
child document later unblocked, only the child surface was repainted, so
the parent could keep replaying a display list that had no surface draw
command until another invalidation, such as resize, forced a recording.
Invalidate the containing iframe when a document's render-blocking set
becomes empty so the parent display list records the child surface. Add
a deterministic reftest that gates a render-blocking stylesheet on a
test-server signal, forcing the parent to paint the blocked iframe
before unblocking it.
Preserve leading BOMs when parsing already-decoded HTML strings, since
those strings do not go through the encoded byte decoder path.
Decoded markup from JS strings can also contain WTF-8 for lone surrogate
code units. Keep the common scalar UTF-8 path to a single validation and
copy, but replace surrogates before handing bytes to the Rust tokenizer.
Add text coverage for DOMParser and innerHTML string parsing, including
leading BOMs, text and attributes, lone high and low surrogates, and a
valid surrogate pair.
Do not build content blocker cosmetic style sheets for decoded SVG
image documents. These documents are resource documents created for
image painting rather than ordinary navigable documents, and other
engines keep extension cosmetic CSS out of them.
Add text coverage that verifies a generic cosmetic rule still applies
to the embedding HTML document while a matching element inside an SVG
loaded through <img> keeps rendering.
Refresh content blocker styles when connected elements gain or change
class or id tokens that can affect generic cosmetic selectors. Cache the
class and id tokens covered by each document's content blocker
stylesheet, then ask the adblock engine whether newly-added tokens can
unlock selectors before invalidating user style for the whole page.
This keeps dynamic cosmetic hiding correct without refreshing blocker
CSS for unrelated class and id mutations.
Add text coverage for irrelevant class/id mutations that should preserve
author style invalidation without refreshing blocker CSS. Also prove
matching tokens still hide elements.
Replace PseudoClassInvalidator's subtree scan with targeted
invalidation for elements whose pseudo-class state changes. This scopes
state changes to affected selectors instead of rechecking a whole
common-ancestor subtree.
Use the ancestor chain that matches each pseudo-class. Hover walks the
shadow-including chain. :focus-within walks the flat-tree chain, so
slotted content invalidates its assigned slot and relevant shadow-tree
descendants. Focus, FocusVisible, and Target invalidate just the state
node.
Route each affected element through Element::invalidate_style with the
pseudo-class property. This uses the same invalidation-plan machinery as
Disabled, Checked, and other pseudo-class state changes.
Interaction state pseudo classes are not tracked in :has() metadata, so
schedule :has() ancestor invalidation explicitly when the state flips.
The callers no longer need cross-scope branching. The chain walk handles
shadow boundaries, and property invalidation already visits every
observer style scope.