Commit graph

1575 commits

Author SHA1 Message Date
Aliaksandr Kalenik
c78dd59cc3 LibWeb: Move async scrolling recording to paintables
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.
2026-05-25 00:45:24 +02:00
Andreas Kling
8be96396d9 LibWebView: Use page background color while resizing
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.
2026-05-24 23:41:41 +02:00
Andreas Kling
8d491d72f0 LibWeb: Keep flushing layout dirtied during layout
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.
2026-05-24 19:55:12 +02:00
Shannon Booth
013afac638 LibWeb: Move PseudoElement visit_edges implementations out of line
Otherwise Element.h needs to be included in PseudoElement.h to compile
standalone.
2026-05-24 16:37:28 +02:00
Andreas Kling
412a66ad5e LibWeb: Repaint iframe containers after render unblocking
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.
2026-05-24 12:24:50 +02:00
Andreas Kling
ca97f68cb7 LibWeb: Normalize decoded HTML string parsing
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.
2026-05-24 10:14:17 +02:00
Andreas Kling
762aea0568 LibWeb: Skip cosmetic blocking in SVG images
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.
2026-05-24 09:58:10 +02:00
Andreas Kling
6ea2a4eff5 LibWeb: Refresh blocker CSS after relevant class/id changes
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.
2026-05-24 08:16:46 +02:00
Tim Ledbetter
f607af9ff3 LibWeb: Use XML encoding rules for XML document loading
Previously, the XML document loading paths used the HTML encoding
sniffing algorithm to determine character encoding. After the chardetng
integration, this algorithm could return a non-UTF-8 encoding for XML
documents that did not specify a charset, causing non-ASCII UTF-8
content to be decoded incorrectly.

We now use BOM sniffing and default to UTF-8, per the XML specification.
2026-05-24 00:51:14 +02:00
Andreas Kling
fa579754bf LibWeb: Use invalidation plans for pseudo-class changes
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.
2026-05-23 23:37:36 +02:00
Andreas Kling
44a6da5165 LibWeb: Avoid cold rule cache builds for :has() checks
Cache lightweight selector insights on each stylesheet so cold style
scopes can answer whether :has() invalidation is relevant without
building a complete rule cache. This avoids forcing rule caches for
shadow roots whose active sheets do not contain :has() selectors.

Imported sheets contribute to their parent sheet's effective rules, so
an imported sheet load or CSSOM change also clears ancestor selector
insight caches.

Add test-only counters and regression coverage for cold shadow roots
with and without :has() selectors, plus delayed imported :has() rules.
2026-05-23 22:03:46 +02:00
Andreas Kling
5cc6cb106d LibWeb: Cache content blocker styles per document
Build the user stylesheet only for document style scopes, since user
rules are already considered relevant across shadow boundaries during
rule matching. This avoids regenerating and reparsing the same cosmetic
content blocker stylesheet for every shadow root in the document.

Keep the generated cosmetic stylesheet cached on the Document and clear
it whenever user style is invalidated, so content blocker changes still
produce fresh CSS for the next style update.
2026-05-23 22:03:46 +02:00
Tim Ledbetter
988dce3e7a LibWeb: Drop :local-link rule-cache gate from base URL change handler
The base URL change handler checked whether any style scope contained
a `:local-link` rule before walking the document's links, this forced
a rule cache rebuild, which is generally slower than the link walk we
were trying to avoid.

On Speedometer2 the duplicated rule-cache and invalidation-set
construction accounted for roughly 4% of total samples. Removing the
gate lets the URL-unchanged early-exit handle the no-op case and runs
the link walk only when the URL actually changes, which the profile
showed to be inexpensive on its own.
2026-05-23 19:43:42 +01:00
Shannon Booth
637fd51595 LibWeb: Unify WebIDL C++ type generation
Represent WebIDL C++ types with a single CppType model that tracks
nullability, optional presence, and contained storage.

GC-like values now use GC::Ref/GC::Ptr directly, while containers choose
"plain", "Root", or "Conservative" container types depending on what
they contain. For example, sequence<Element> becomes a RootVector of
GC::Ref values, while sequence<SomeDictionary> becomes a
ConservativeVector only when the dictionary contains GC-like values.
This moves the generated bindings away from wrapping GC values in
GC::Root by default.

This has broad fallout as the types passed to interfaces for GC
objects changes almost fully across the board.
2026-05-23 18:26:12 +02:00
Andreas Kling
17902b02ab LibWeb: Stop idle animated CSS image timers
Keep animated ImageStyleValue frame advancement owned by the
style value. The current frame and loop state live there, so a
separate document scheduler would duplicate ownership of that state.

Start the ImageStyleValue timer only while it has layout clients.
Stop it when the last client unregisters, or when a finite animation
completes. Expose a document-scoped active timer count through
internals for focused regression tests.

Clear image observers when layout nodes detach. Use current-node
cleanup for per-DOM-node clearing, and explicit subtree cleanup for
tree replacement, full tree teardown, and synthetic pseudo-elements.
This keeps large document clearing linear.

Unregister generated-content image providers during layout detach
instead of waiting for GC to finalize the provider.

Cover hidden animated background images, generated content images,
layout node replacement, full layout tree teardown, and document
scoping for the internals counter.
2026-05-23 11:36:53 +02:00
Andreas Kling
15714bade3 LibWeb: Avoid running scripts adopted during fetch
Follow the script execution model by checking the preparation-time
document before waiting for scripts to run in the node document. An
async script can be adopted into a freshly-created document while its
fetch is still pending, and that script must return without executing
when the fetch completes.

Also unblock render-blocking elements when removal disconnects them
from their browsing context, matching the render-blocking mechanism.
Add a crash test for an adopted async script whose fetch completes
after adoption.
2026-05-23 11:36:45 +02:00
Zaggy1024
a0b3b21011 LibWeb: Only fire document observers' fully active callbacks on change
Previously, the fully active callback could fire when the document was
never actually deactivated, which could cause a verification failure in
HTMLMediaElement due to its assumption that the callbacks only fire
when a document's fully active state genuinely changed.
2026-05-23 09:14:13 +02:00
Andreas Kling
7b6be1201d LibWeb: Resolve element style without a full document flush
Add a targeted update_style_for_element() mode that resolves one
flat-tree inheritance chain. Normal callers still get computed style in
display:none subtrees. Focusability stops when that chain resolves to
display:none.

The targeted path falls back to normal document style traversal for
document invalidation, full rebuilds, and selector work. That keeps
global dirty state consumed atomically. Local recomputation preserves
layout, display-list, slot, visual-context, and stacking-context
invalidation. It marks children dirty when a recomputed element can
affect descendant style.

Use StopAtDisplayNone for focusable-area rendering checks so incidental
is_focusable() queries do not repeatedly force unrelated document style
work. Rebaseline style invalidation counters for the reduced
getComputedStyle() recomputation scope.
2026-05-22 21:25:58 +02:00
Andreas Kling
b3e4372404 LibWeb: Share style invalidation side effects
Factor the element-level and document-level side effects from style
recomputation into helpers used by normal document style traversal. This
keeps existing behavior unchanged while letting the later targeted style
update path reuse the same invalidation bookkeeping.
2026-05-22 21:25:58 +02:00
Shannon Booth
2ae54d77e0 LibWeb: Reuse initial about:blank window for first same-origin load
Follow the spec reuse of the initial same-origin about:blank window for
a browsing context's first real navigation. This fixes a crash in
promise-job-entry-different-function-realm.html by preserving the
correct iframe realm.

However, reusing the initial about:blank Window means
create-and-initialize can associate that Window with the pending
Document before session history activation has made the Document active.
Treating a browsing context's active document as its active Window's
associated Document therefore exposes the pending Document too early.

To fix this, add an explicit active document slot to BrowsingContext and
update it when a Document is made active.

A similar fix was attempted in 7fc7263a4d,
but that version still queued navigation tasks through the reused active
Window, tagging them with the pending Document before it was active.
This caused parser-created iframe loads to hang in encoding WPTs. This
commit instead queues those navigation-internal tasks against the
navigable's currently active Document.

The newly added iframe-initial-load-chunked-body.html mimics the same
type of failure seen by the WPT regressions mentioned above.
2026-05-22 18:17:58 +02:00
Callum Law
4fd1635b17 LibWeb: Use correct event type for cancelled CSS transition
Previously we incorrectly dispatched a "animationstart" event instead of
a "transitioncancel" one
2026-05-22 11:59:19 +01:00
Callum Law
b0a818a476 LibWeb: Use correct scheduled time for Transition and Animation events
The spec doesn't say what time to use here but in other places where we
schedule animation events it says to use "the result of applying the
procedure to convert timeline time to origin-relative time to the
current time of the timeline with which animation is associated", so we
do that here as well.

This time is stable per animation frame per timeline so we now correctly
fall back to composite ordering of animation/transition events where we
previously wouldn't (since the scheduled time was so precise that it was
always unique), which causes the imported test to no longer be flakey.
2026-05-22 11:59:19 +01:00
Callum Law
b3a80f0415 LibWeb: Dispatch events for CSS animations without timelines 2026-05-22 11:59:19 +01:00
Callum Law
2a65e7c80c LibWeb: Use owning element as AnimationEvent target
The spec says we should always use the owning element even if the
effect's target element has changed
2026-05-22 11:59:19 +01:00
Callum Law
869ac61147 LibWeb: Apply animations without timelines when updating animated style
Previously we would only apply animations which were associated with a
timeline.
2026-05-22 11:59:19 +01:00
Callum Law
a1632cb1c1 LibWeb: Respect composite order in update_animated_style_if_needed 2026-05-22 11:59:19 +01:00
Sam Atkins
16f7333586 LibWeb: Ensure XML error documents always finish loading
In some cases, the XML error document would never be marked as ready for
post-load tasks, even though a load event got fired. This caused
intermittent timeouts on our iframe-load-event-for-bad-xml.html test.

Make XML error document conversion finish loading when the parser has
not already queued post-load tasks. This lets bad XML iframe loads fire
load instead of leaving the parent parser waiting indefinitely.

Also install the test's iframe load handler before assigning src to
avoid a potential race condition there.
2026-05-22 09:59:19 +01:00
sideshowbarker
c1f38f92fe LibWeb: Keep repaints pending inside visibility:hidden iframes
Problem: An iframe whose content changes while it (or an ancestor)
has visibility:hidden isn’t painted once it becomes visible again.
The stale previous frame stays on screen until an unrelated
repaint (e.g., window resize) happens to occur.

Cause: set_needs_repaint() returns early for any document inside an
iframe with a visibility:hidden ancestor — discarding the request
entirely. The navigable’s needs_repaint flag is never set. Nothing
sets it again when the iframe becomes visible — so the rendering
loop keeps skipping it — and its display list stays stale.

Fix: Stop discarding the request in set_needs_repaint(). Instead,
skip painting hidden navigables in the rendering loop — while
leaving needs_repaint set. Once an ancestor iframe becomes
visible, the still-set flag makes the rendering loop paint the
navigable on the next frame.

Fixes https://github.com/LadybirdBrowser/ladybird/issues/9305
2026-05-22 09:58:10 +01:00
Andreas Kling
e548c97b6d LibWeb: Limit non-inherited inherit tracking to direct children
Track explicit inherit of non-inherited properties only on the direct
parent shadow root. A deeper descendant with margin-left: inherit still
inherits from its own parent, so a host margin change does not require
marking every ancestor as possibly affected.

Extend the shadow-root inherited style test to cover both the direct
child case that must still update and the deeper descendant case that
must not trigger broad inherited-style recomputation.
2026-05-22 09:38:59 +02:00
Andreas Kling
ddce686ed0 LibWeb: Propagate inherited style across slot boundaries
Make style updates reach a fixed point when slot invalidation dirties
assigned nodes after their traversal point. During inherited-style
cascades, only the topmost changed element scans for descendant slots.

Animation inherited-style updates now include the target slot and walk
shadow-including descendants, so host animations propagate inherited
values through shadow trees and assigned slottables. Animated inherited
longhands also carry the same inherited-style invalidation signal as
regular style changes.

Mark custom elements dirty when their :defined state flips, so upgraded
elements do not keep stale :not(:defined) computed style. Add coverage
for slotted menu invalidation, descendant-slot scan counts, target slot
animations, host shadow-tree animations, and explicit inherit from an
animated non-inherited longhand.
2026-05-22 09:38:59 +02:00
Andreas Kling
d0b47e32c9 LibWeb: Propagate inherited style into shadow roots
Track when style recomputation may require inherited-style work in a
shadow tree, and use that signal when crossing from a shadow host into
its shadow root. Shadow descendants can explicitly inherit normally
non-inherited host properties, so any host style change may need
inherited-style recomputation there.

Use the CSS property definition to tell whether changed longhands need
to propagate to shadow descendants. Do the same after recomputing
inheritance-dependent values, such as host font-size: 2em after a parent
font-size change.

The shadow DOM tests cover inline and class-driven inherited host style
changes, explicit inherit for non-inherited host properties, relative
units on hosts, and nested slotted inheritance updates.
2026-05-22 09:38:59 +02:00
Andreas Kling
3f1a6926f4 LibWeb: Set about base URL before document URL
Install the inherited about base URL before setting the document URL so
fallback-base URL consumers see the correct source during URL-dependent
style and content blocker work.
2026-05-21 21:16:56 +02:00
Callum Law
9db3c77c6e LibWeb: Apply "element-reference" pseudo inline styles correctly
Previously we applied them ad-hoc when computing the style for the
referenced element but we now apply it as part of the cascade. This
fixes a couple bugs:
 - Computing the style for the pseudo-element (rather than the
   referenced element itself) as we do in the case we don't have a
   layout node in `get_direct_property` now includes the inline style.
 - Inline style is applied according to the cascade (i.e. it can be
   overriden by non-inline `!important` styles).
 - Properties go through the computation process.
2026-05-21 14:26:22 +01:00
Callum Law
199e82fc73 LibWeb: Forward non-synthetic pseudo-elt accesses to relevant element
This adds a new class `ElementReferencePseudoElement` which forwards
any accesses to the referenced element for "element-reference" pseudo
elements.

This allows us to, for instance:
 - Access the underlying `ComputedProperties` in getComputedStyle, which
   includes inline style.
 - Store and access `CustomPropertyData`
 - Access the layout node for "resolved value" computation
 - Apply animations from `update_animated_style_if_needed`

We register these with the originating element when calling
`set_associated_shadow_host_pseudo_element`, this requires us to append
the element to the tree beforehand since we need to be able to get the
shadow host.
2026-05-21 14:26:22 +01:00
Callum Law
507380d8c5 LibWeb: Rename get_pseudo_element_node to pseudo_element_layout_node
This matches the naming convention of the newly introduced
`pseudo_element_unsafe_layout_node` and the top-level
`Element::layout_node`
2026-05-21 14:26:22 +01:00
Callum Law
0f92554427 LibWeb: Introduce Element::pseudo_element_unsafe_layout_node
This is used whereever we use `unsafe_layout_node`. There's no
difference in behavior for `SyntheticPseudoElement` but once we
implement element-reference pseudo-elements it will call
`unsafe_layout_node` on the referenced element rather than `layout_node`
2026-05-21 14:26:22 +01:00
Callum Law
977c054e77 LibWeb: Rename use_pseudo_element
`associated_shadow_host_pseudo_element` is clearer on what this is.
2026-05-21 14:26:22 +01:00
Callum Law
79f6cc1d7a LibWeb: Make DOM::PseudoElement abstract
And add a new `SyntheticPseudoElement` sub-class.

This will allow us to implement `ElementReferencePseudoElement` in the
next commit.
2026-05-21 14:26:22 +01:00
Callum Law
28afdb327a LibWeb: Limit some pseudo-element functionality to synthetic only
Most of this functionality was already implicitly disallowed for
element-reference pseudo-elements by the fact that we weren't creating
entries in `m_pseudo_element_data` for them, but we need to explicitly
limit it in preparation of creating those entries.

Due to the above this is mostly non-functional apart from a regression
where we no longer support custom properties on element-reference
pseudo-elements. Previously when setting custom properties for an
element-reference pseudo-element we would call `ensure_pseudo_element()`
which created a synthetic pseudo-element entry distinct from the
referenced element which only stored custom property data - this was
clearly wrong and will be implemented properly in a future commit.
2026-05-21 14:26:22 +01:00
Callum Law
5e302c87b4 LibWeb: Remove unused Element::has_pseudo_element() 2026-05-21 14:26:22 +01:00
Callum Law
468c696ec7 LibWeb: Combine pseudo element clearing methods
These two methods were the same just with slightly different names and
badges so let's combine them.
2026-05-21 14:26:22 +01:00
Aliaksandr Kalenik
cded122a2d LibIPC: Support DistinctNumeric serialization
IPC currently needs each distinct numeric wrapper to provide its own
serialization specialization. That makes compositor resource ids and
pixel-unit wrappers grow one-off glue as soon as they cross an endpoint.

Teach LibIPC to serialize AK::DistinctNumeric wrappers through their
underlying value type and remove the redundant Web-side specializations
for UniqueNodeID and DevicePixels. Existing compound pixel-unit
serializers now rely on generic wrapper support for their components.

This is preparatory work required to add IPC between the main and
compositor threads.
2026-05-21 11:45:06 +01:00
Tim Ledbetter
d760cec702 LibWeb: Stop retaining fragment-parsing documents after adoption
When adopting an element, we now set the node document of each of its
attributes to the new document. Without this, `Attr` objects retained
document pointers back to a temporary document created during fragment
parsing, these documents were then kept alive after the parsed nodes
were moved into the real document.
2026-05-21 08:56:41 +02:00
Andreas Kling
c92ea86c30 LibWeb: Invalidate shadow focus-within styles
Match :focus-within by walking flat-tree parents so shadow hosts and
slotted content follow the Selectors definition instead of DOM ancestry.

When focus state changes, also run pseudo-class invalidation in shadow
style scopes that can observe the focused node. This lets shadow-root
rules such as :host(:focus-within)::before and ::slotted(:focus) update
without waiting for an unrelated DOM mutation to restyle the host.

Add focused coverage for host pseudo-elements, shadow descendants, and
slotted controls, and update the style-invalidation counter baseline for
the lower focus-family no-op restyle counts.
2026-05-21 08:56:05 +02:00
Andreas Kling
94302775bf LibWeb: Map document element focus to the viewport
Apply the focusing steps' get-the-focusable-area mapping before
rejecting a non-focusable target. This preserves documentElement.focus()
by mapping the non-focusable document element to the Document viewport.

Also map rendered navigable containers with content navigables to their
active document, while leaving hidden containers unfocused. Preserve
Window focus events for child document viewports reached through iframe
focus, while still suppressing the top-level viewport surrogate events.

Treat rendered object elements as focusable through their default
non-null tabindex, even when they show fallback or image content instead
of a child navigable.

Keep the spec focus-chain common-tail handling intact for viewport
focus. The Document object is only our surrogate for the viewport, so
designate viewport focus from the new focus target without dispatching
Window focus/focusin events for that top-level surrogate.

Pass that viewport surrogate as the fallback target for fragment
scrolling and NavigateEvent focus reset, so unfocusable body or fragment
targets still clear stale element focus.

Cover documentElement.focus() in both the activeElement and focus-chain
tests, including a tabindex document element that remains focused as an
element. Cover object focus with and without a child navigable, hidden
object focus attempts, iframe focus events, hidden iframe focus, and
blurring a focused iframe after it becomes hidden. Also cover viewport
fallback for intercepted navigation focus reset and fragment scrolling
to an unfocusable target.
2026-05-21 08:56:05 +02:00
Andreas Kling
99b1780d57 LibWeb: Reject non-rendered elements as focus targets
Do not let elements inside display:none subtrees become focus targets.
The HTML focusable-area model only allows elements to be focusable when
they are rendered, delegate rendering to their children, or are relevant
canvas fallback content.

Preserve blur for the current focused area after script hides it, since
the unfocusing steps operate on the old focus target. Check display:none
through the flat-tree style parent chain, so slotted controls inside
hidden slot subtrees cannot become focused.

Cover hidden ancestors with materialized computed style, display:none
controls, display:contents, hidden focused controls, and slotted cases
inside hidden and visible shadow-tree subtrees.
2026-05-21 08:56:05 +02:00
Tim Ledbetter
11ce247b1a LibWeb: Repaint when a range is dissociated from a selection
Previously, dissociating a range from a selectionleft stale selection
state on paintables and never requested a repaint, so the old highlight
remained visible until something else dirtied the viewport. We now
reset the per-paintable selection state and trigger a repaint on
dissociation.
2026-05-20 21:32:00 +02:00
Shannon Booth
de6aec04e8 LibGC: Default-construct ConservativeVector from the global heap 2026-05-20 20:37:55 +02:00
Shannon Booth
387cd6e2e2 LibGC: Default-construct RootVector from the global heap
Similar to GC::Root<T>, make GC::RootVector<T> constructible without
explicitly passing a Heap.

This is implemented by having RootVectorBase use GC::Heap::the() for
heap-free construction.
2026-05-20 20:37:55 +02:00
Andreas Kling
1f358ae8f7 LibWeb: Fix nested SVG partial relayout transform lookup
Partial SVG relayout builds a throwaway layout state rooted at the
nearest dirty SVG viewport. SVG graphics layout can still walk past
intermediate SVG viewports when looking for ancestor transforms, so
pre-populate every SVG graphics ancestor instead of stopping at the
first ancestor SVG viewport.

Fixes an issue where the typing indicator would crash Discord when
someone starts typing a message. :^)
2026-05-20 14:55:18 +02:00