Commit graph

374 commits

Author SHA1 Message Date
Andreas Kling
2bf1f41805 LibWeb: Make computed properties immutable
Move CSS animation values into a mutable overlay on computed properties
and make base computed style data immutable after construction. Base
style mutation now goes through a builder that is consumed on publish,
so installed styles no longer expose mutation APIs.

Build new base style data for inherited style updates instead of cloning
and mutating installed computed properties. Element-specific computed
style adjustments now run before publication, while animation and
transition updates continue to mutate only the animated overlay.
2026-06-20 23:43:17 +02:00
sideshowbarker
4bbc5e0950 LibWeb: Fix crash from SVG resource boxes outliving removed element
Problem: Crash when removing a <mask>, <clipPath>, or <pattern> element
that’s referenced via url(#id), and then GC’ing it.

Cause: <mask>, <clipPath>, and <pattern> are laid out as resource boxes
attached to the referencing element's layout subtree, rather than their
own. So those survive cleanup of their DOM ancestor. Once a removed
<mask>/<clipPath>/<pattern> is collected, its resource box’s weak DOM
node pointer is null. And dereferencing that trips an assert — both
while painting and while tearing down the layout tree.

Fix: When a <mask>, <clipPath>, or <pattern> is removed, request a full
layout-tree update while the element’s still alive. That drops the stale
resource boxes (whose url(#id) references no longer resolve) before the
node gets collected.

Fixes https://github.com/LadybirdBrowser/ladybird/issues/10127
2026-06-18 13:35:32 +02:00
Callum Law
8ebdaeab69 LibWeb: Transfer animation ownership to AnimatedBitmapDecodedImageData
Previously animation ownership was a messy split between
`AnimatedBitmapDecodedImageData` and the consumers (i.e.
`ImageStyleValueResource`, `HTMLImageElement`, and `SVGImageElement`)
with `AnimatedBitmapDecodedImageData` owning the frames and a current
frame index, and the consumers owning the rest of the state (e.g. loop
count, timers to drive the animation forward, their own current index).

This had a couple of main issues:
 - While `AnimatedDecodedImageData` partially synchronized animations by
   dropping unexpected advancement notifications, this didn't apply to
   other animation state which meant, for instance, that a later started
   consumer could drive the animation of an earlier one past the max
   loop count (albeit without invalidating the earlier consumer).
 - Multiple consumers didn't share frame timings, meaning animations
   could be up to a full frame out of sync visually.
 - Animations were paused depending on whether there were any consumers,
   this is different to the behavior in other browsers (where they
   continue regardless of whether there are any consumers).
 - It was an overgeneralization of how animations need to work - only
   `AnimatedBitmapDecodedImageData` works with an indexed frame model,
   with animated SVGs (although not yet implemented) relying on their
   internal event loop to be driven forward.

Given the above the new approach implemented in this commit is:
 - The API for `DecodedImageData` is animation system agnostic, only
   exposing `default_frame`, `current_frame`, and `restart_animation`
   methods not reliant on providing a specific frame index.
 - `AnimatedBitmapDecodedImageData` owns its own timer, loop count,
   etc. The animation starts when the first consumer registers and ends
   when the document is hidden or becomes inactive (or completes in the
   case of finite animations).
 - Consumers are invalidated by `AnimatedBitmapDecodedImageData` when
   required.

Tests have been added for:
 - Animations being paused when the document becomes inactive and
   restarted when it becomes active again.
 - Frame timings being synchronized across consumers.
 - Restarts triggered by `HTMLImageElement` applying to all consumers.
 - Processing ending once a non-infinite animation plays to completion.

The tests to ensure animations are cancelled when consumers are removed
(e.g. `animated-background-image-timer-stops-when-hidden.html`) have
been updated to assert the inverse since animation state is now per
resource not per consumer.
2026-06-18 10:44:25 +02:00
Callum Law
a7881ca3eb LibWeb: Register DecodedImageData consumers as clients
In a future commit, ownership of animation will be transferred from
these clients to `AnimatedDecodedImageData` and we will need a way to
invalidate them for new frames.

This also revealed some `ImageProvider`s which don't yet support
animated images (e.g. `<input type="file">`, `<object>`, etc) but that
is left as a FIXME for now.
2026-06-18 10:44:25 +02:00
Callum Law
74e04ed258 LibWeb: Add DecodedImageData::default_frame helper
The index based `frame(size_t, ...)` getter is going to be removed in a
future commit.
2026-06-18 10:44:25 +02:00
Callum Law
d98c5d1b03 LibWeb: Inline image scaling mode computation
This allows us to remove the `frame_rect` accessor.

This also fixes a bug where we computed the scaling mode based on the
clipping rect rather than the draw rect for `ImagePaintable`
2026-06-18 10:44:25 +02:00
Aliaksandr Kalenik
23885e7b4d LibWeb+WebContent+WebWorker: Drop display list player type selection
WebContent no longer chooses between CPU and GPU display list players,
and the remaining callers always use Skia raster playback. Remove the
PageClient virtual and now-single-value enum, then play SVG image and
cursor display lists directly.
2026-06-18 10:25:44 +02:00
sideshowbarker
a38407d457 LibWeb: Unregister a “use” element from its document when finalized
Problem: Discarding a document that contains an SVG “use” element could
abort the process with a !is_in_list() verification failure in the
IntrusiveListNode destructor. That surfaced intermittently in our style-
invalidation stress tests, depending on GC sweep order.

Cause: A “use” element connected to a document registers itself in the
document’s list of “use” elements and unregisters during its removal
steps. A GC’ed “use” element is swept without running those removal
steps — so it stays linked. When it’s destroyed before its document,
its list node is still linked — and the destructor aborts.

Fix: Override finalize() to unregister the “use” element before
destruction. The collector finalizes every dying cell before destroying
any of them. So, the node is always unlinked in time — the same approach
DocumentObserver and NavigationObserver already use.
2026-06-18 09:39:47 +02:00
Tim Ledbetter
7bb200a663 LibWeb: Add fast_is to SVGTextContentElement 2026-06-18 01:27:28 +02:00
Tim Ledbetter
9d359d9d7c LibWeb: Add fast_is to SVGClipPathElement 2026-06-18 01:27:28 +02:00
Tim Ledbetter
7218794aaa LibWeb: Add fast_is to SVGPatternElement 2026-06-18 01:27:28 +02:00
Tim Ledbetter
d86936f09c LibWeb: Add fast_is to SVGGradientElement 2026-06-18 01:27:28 +02:00
Callum Law
a40b1183ad LibWeb: Hoist overloaded ImageProvider methods to base class
All of these overloads did the same thing so lets just define them in
the base class
2026-06-16 09:26:15 +02:00
Callum Law
60c6cc2f0f LibWeb: Remove unused ImageProvider methods 2026-06-16 09:26:15 +02:00
Callum Law
34df191e54 LibWeb: Remove unused SVGDecodedImageData::surface()
The only caller was removed in 395a126
2026-06-16 09:26:15 +02:00
Andreas Kling
c419bb526b LibWeb: Track connected SVG use elements in a per-document list
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.
2026-06-11 15:28:59 +02:00
Sam Atkins
e7aad5a9d3 LibWeb: Connect iframe referrerpolicy to ancestorOrigins
Corresponds to:
e161310ae7

This unfortunately isn't testable as we don't implement enough of
ancestorOrigins to be able to observe it.
2026-06-11 14:25:27 +01:00
Zaggy1024
57a78f2b59 LibWeb: Transfer load delayers when elements are adopted
Otherwise, the load event will block the original document until GC
runs.

Without this, media-load-task-after-adoption.html would wait for the
idle timeout to trigger a garbage collection, which could sometimes
cause the test to time out entirely.
2026-06-11 00:34:12 +02:00
Callum Law
3c4076597e LibWeb: Avoid Vector allocation in parse_coordinate{_pair_}_sequence
Previously we would often return a `Vector` just for the caller to
iterate over it a single time and disregard it. We instead now skip the
`Vector` allocation and second iteration by executing a callback as we
parse each value in the sequence.
2026-06-08 12:39:39 +01:00
Callum Law
5fbb8d82e7 LibWeb: Move SVG::parse_elliptical_arc_argument inline
`SVG::parse_elliptical_arc` was the only user and moving it inline
allows us to avoid a `Vector` allocation.
2026-06-08 12:39:39 +01:00
Callum Law
b33b9a6747 LibWeb: Parse SVG coordinate pair as Gfx::FloatPoint
We ended up converting this to a `Gfx::FloatPoint` in all but one place
any way so this just skips some extra handling and `Vector` allocations.

Also makes `parse_coordinate_pair_{double|triplet}` return `Vectors`
store data inline to avoid a heap alloc.
2026-06-08 12:39:39 +01:00
Callum Law
1026991820 LibWeb: Specialize SVG::PathInstruction structs
Previously we had a single `PathInstruction` struct which stored it's
data in a `Vector<float>`. We now define `PathInstruction` as a
`Variant<>` of structs storing data inline (note that the remaining
`Vector` properties will be replaced with `Gfx::FloatPoint` in a future
commit) - this avoids a `Vector` allocation and avoids magic indices
when accessing data.
2026-06-08 12:39:39 +01:00
Callum Law
8ab9eaf44e LibWeb: Simplify SVG::PathInstruction dumping
This updates dumping to reflect the fact that each instruction only ever
contains a single set of arguments, and that `ClosePath` does not have a
concept of absoluteness (i.e. 'Z' and 'z' are equivalent).
2026-06-08 12:39:39 +01:00
Callum Law
337748e3d8 LibWeb: Avoid string value copy in SVGPathElement::attribute_changed 2026-06-08 12:39:39 +01:00
Callum Law
743922b868 LibWeb: Avoid copying instructions in AttributeParser::parse_path_data
We throw away the parser immediately afterwards so we can just `move()`
instead of copying
2026-06-08 12:39:39 +01:00
Andreas Kling
e23c657144 LibWeb: Avoid SVG getBBox paintable downcasts
SVG foreignObject elements expose SVGGraphicsElement methods, including
getBBox(), but their layout boxes create SVGForeignObjectPaintable,
rather than SVGGraphicsPaintable. Only SVGGraphicsPaintable stores the
computed SVG transform data, so use it only when that paintable kind is
present.

Add reduced crash coverage for calling getBBox() on a rendered
foreignObject element.
2026-06-08 01:04:08 +02:00
Andreas Kling
7b3d33f062 LibWeb: Guard SVG animated length conversion
SVG animated length accessors read computed geometry properties, but
those can contain CSS sizing keywords such as fit-content. Those values
are not length-percentage values and must not be converted as one.

Only convert length, percentage, and calculated values. Fall back to the
existing zero length for other computed values, matching the auto path.
Add a crash test for reading height.baseVal on an SVG filter primitive
with fit-content height.
2026-06-08 01:04:08 +02:00
Andreas Kling
9340d2d1a3 LibWeb: Make layout nodes refcounted
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.
2026-06-07 20:52:49 +02:00
Andreas Kling
7c06c3fd14 LibWeb: Make computed style data refcounted
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.
2026-06-06 23:29:48 +02:00
Aliaksandr Kalenik
852c7a10f3 LibWeb: Let image paint callers own clipping
DecodedImageData::paint() used to take both a destination and a
clip rectangle even though most callers passed the same value. SVG
image painting used that API to wrap every nested SVG display list in
save/add-clip/restore, which put an unbounded command in front of
the bounded nested-list command and made offscreen SVG image content
harder to cull.

Move clipping to ImagePaintable, where the object-fit destination can
be compared with the replaced element box. CSS image and marker
painting continue to draw into their destination rect, while repeated
background images keep their explicit tile clip. The scaled decoded
image display-list command now stores only its destination rect and
uses that as its bounds; playback still clips decoded images to that
rect so bitmap rendering stays unchanged.
2026-06-06 13:23:21 +01:00
Andreas Kling
164ed80244 Meta: Enable exit-time destructor warnings for libraries
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.
2026-06-04 19:20:49 +02:00
Sam Atkins
89bdc5f889 LibWeb/SVG: Apply parser-created style timing
Let SVGStyleElement delegate style updates through StyleElementBase so
parser-created inline SVG `<style>` elements update when they are popped
from the parser stack, just like HTML style elements.

This lets SVG style imports participate in script-blocking stylesheet
checks while keeping dynamic text, type, and media changes from
re-blocking parser scripts.
2026-06-04 16:39:54 +01:00
Sam Atkins
c1953f93c7 LibWeb: Move style element hooks to StyleElementBase
Move HTMLStyleElement's dynamic update handling, media/type attribute
handling, and script-blocking predicate skeleton into StyleElementBase
so style elements can share that plumbing.
2026-06-04 16:39:54 +01:00
Tim Ledbetter
ad7105b570 LibGfx+LibWeb: Implement SVGTextPathElement.startOffset 2026-06-04 16:01:56 +02:00
Tim Ledbetter
260064deef LibWeb: Fall back to xlink:href when resolving SVGTextPathElement path 2026-06-04 16:01:56 +02:00
François Guerraz
c984ce2429 LibWeb/SVG: Fix crash parsing a coordinate sequence that ends mid-token
AttributeParser::parse_coordinate_sequence() appended the result of
parse_coordinate() to the sequence even when parsing had failed on a
non-first iteration: its error branch was missing the `break` that the
sibling parse_coordinate_pair_sequence() has. Calling release_value() on
the errored ErrorOr then trips a VERIFY and aborts the process.

A path such as `<path d="H1,,">` reaches this: after the first
coordinate the trailing comma leaves match_comma_whitespace() true, the
following parse_coordinate() fails, and the loop falls through to
release the error.

Break out of the loop on a non-first error, matching the sibling
function.
2026-06-04 13:36:16 +01:00
Andreas Kling
7b159143df LibWeb: Avoid redundant SVG use recloning
Skip the document-complete clone when a resolved SVG use element already
has a current shadow tree. Href processing creates that root for refs.
Replacing it during load can leave stale mask contents.

Still clone at document-complete for forward refs and changed refs.
Add coverage for SVG use in masks, pre-load subtree mutations, and
unchanged instance roots.
2026-05-31 15:49:58 +02:00
Aliaksandr Kalenik
d725c36129 LibWeb: Store cached display list commands without resources
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.
2026-05-31 00:21:25 +01:00
Andreas Kling
44205cd086 LibWeb: Allow positioned SVG roots in foreignObject
Do not force nested SVG roots back to position: static when they
are in HTML content inside a foreignObject. These SVG elements take
part in CSS box layout, so absolute positioning must be preserved.

Add a ref test that covers a nested masked foreignObject shape like
the Discord server icons, where ignoring the absolute offset clips the
icon four pixels too far inward.
2026-05-30 21:36:47 +02:00
Tim Ledbetter
b0f926d16b LibWeb: Resolve SVG gradient references within shadow trees
Previously, the `linked_gradient()` lookup only searched the document
for gradient IDs, so gradients inheriting stops via href inside a
shadow DOM would fail to find the correct target and be rendered black.
We now check any containing shadow root first, before checking the
document for linked gradient IDs.
2026-05-28 10:39:13 +02:00
Aliaksandr Kalenik
b36e6c9b97 Compositor+LibWeb: Pass AVC trees separately from display lists
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.
2026-05-27 18:29:42 +01:00
Aliaksandr Kalenik
6577d3f5b1 Compositor+LibWeb: Make display list flushing explicit
DisplayListPlayer::execute() used to flush the active painting surface
as part of replay. That made replay and submission inseparable, so
callers could not add extra painting after replay without either
accepting a stale submission boundary or flushing again.

Expose flush(PaintingSurface&) on the player and make execute() only
replay commands. Existing callers now issue an explicit flush at the
same point where the implicit flush used to happen, keeping behavior
unchanged while making the submission boundary visible to compositor
code.
2026-05-26 18:25:59 +01:00
Callum Law
883199cb39 LibWeb: Make TransformationSV::to_matrix infallible
Parameter values are absolutized at style computation time (or are
confirmed to be resolvable in the case of reification or DOMMatrix) so
there is no reason this function should fail.
2026-05-21 17:07:58 +01:00
Shannon Booth
78a4438cd8 LibGC: Default-construct RootHashTable from the global heap 2026-05-20 20:37:55 +02:00
Luke Wilde
4248d4baf2 LibWeb/SVG: Root seen-element hash tables in gradient/pattern recursion 2026-05-19 19:24:08 +02:00
Tim Ledbetter
33aff52db8 LibWeb: Cache recorded display lists for SVG images by output size
Recording a display list for an SVG image triggered a full layout
update on the SVG's internal document and re-recorded the commands on
every paint. Pages with many small SVG icons therefore could accumulate
tens of thousands of redundant layout updates per page load.

We now cache these display lists per output size to avoid this
redundant work.

The cache currently uses the same 10 entry cap as the cache for
rendered surfaces.
2026-05-19 11:34:26 +02:00
Andreas Kling
b849af70b8 AK+LibWeb: Reject impossible Variant visit overloads
Make Variant::visit reject typed visitor overloads that cannot be
called for any variant alternative. This catches stale visitors after a
variant payload type changes instead of falling through to a generic
overload.

Update fetch body consumers that still expected ByteBuffer after the
body payload moved to Core::ImmutableBytes.
2026-05-18 01:21:34 +02:00
Aliaksandr Kalenik
8906011a6b LibWeb: Synchronize display list resources via transactions
Display lists used to own the resource storage needed to replay their
command bytes. That kept the compositor tied to in-process object
ownership: sending a display list update also meant sharing the same
resource container with the recording side.

Move resource storage out of DisplayList and make display list updates
carry a transaction of resources to add and remove. Navigable now tracks
the resources referenced by the current display list, sends only the
delta to the compositor, and trims its recording-side storage to the
active set. The compositor applies those transactions to its own storage
before replacing the cached display list.

This still carries in-process resource objects, but it puts the
ownership boundary in the right place. Command bytes and resource
lifetime are now synchronized explicitly, which is the shape needed
before the compositor can receive serializable resource updates across a
process boundary.
2026-05-16 19:35:24 +02:00
Aliaksandr Kalenik
ec1cdea7db LibWeb: Inline paint styles in display list commands
Paint styles were stored as display list resources, which kept path
painting tied to in-process C++ objects. Move the gradient and pattern
payload into FillPath and StrokePath instead, with gradient stops
serialized as parallel color and position spans.

This is prep work for making display lists serializable across the IPC
boundary without needing to marshal SVG paint style objects separately.
2026-05-10 19:49:29 +02:00
Shannon Booth
5adfd1c43a LibWeb/Bindings: Generate struct definitions from IDL dictionaries
Previously we were inconsistent by generating code for enum definitions
but not generating code for dictionaries. With future changes to the
IDL generator to expose helpers to convert to and from IDL values
this produced circular depdendencies. To solve this problem, also
generate the dictionary definitions in bindings headers.
2026-05-09 10:49:49 +02:00