Commit graph

40 commits

Author SHA1 Message Date
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
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
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
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
f8640d813a LibGfx+LibWeb: Make DecodedImageFrame a value type
DecodedImageFrame only wraps a ref-counted Bitmap and color-space
metadata. The frame object itself does not provide shared mutable
state or lifetime ownership beyond those members, so ref-counting it
adds an unnecessary layer of indirection.
2026-05-07 16:08:13 +02:00
Aliaksandr Kalenik
76c79ee522 LibGfx: Remove ImmutableBitmap
DecodedImageFrame now owns decoded bitmap pixels directly, so the
separate ImmutableBitmap wrapper no longer carries useful semantics.
Remove the class and pass decoded image frames or bitmaps at the
boundaries where pixels are actually required.

The Skia image cache now keys off DecodedImageFrame, matching the
display-list commands that paint decoded images. Video frames stay
owned by LibMedia, with the explicit YUV-to-bitmap conversion living
at HTMLVideoElement's decoded-frame entry point for canvas and WebGL
callers.
2026-05-05 14:39:17 -05:00
Aliaksandr Kalenik
40f2abb7fe LibGfx+LibWeb: Add DecodedImageFrame
Decoded image data should not continue to traffic in ImmutableBitmap now
that the bitmap wrapper is being retired. Introduce DecodedImageFrame as
the paintable decoded-image unit and store a Bitmap plus ColorSpace in
it directly.

Thread the new frame type through decoded image data, display-list
image commands, filters, canvas drawImage, patterns, WebGL texture
upload, and CSS/SVG image consumers. ImmutableBitmap remains only at
the legacy boundaries that still need it, such as HTML video snapshots
and callers that explicitly ask for a bitmap snapshot.

This keeps color-space ownership with the decoded frame while making
the expensive or legacy ImmutableBitmap path explicit at the few call
sites that still need it.
2026-05-05 14:39:17 -05:00
Shannon Booth
fd44da6829 LibWeb/Bindings: Emit one bindings header and cpp per IDL
Previously, the LibWeb bindings generator would output multiple per
interface files like Prototype/Constructor/Namespace/GlobalMixin
depending on the contents of that IDL file.

This complicates the build system as it means that it does not know
what files will be generated without knowledge of the contents of that
IDL file.

Instead, for each IDL file only generate a single Bindings/<IDLFile>.h
and Bindings/<IDLFile>.cpp.
2026-04-21 07:36:13 +02:00
Aliaksandr Kalenik
eae94a8a46 LibWeb: Route repaint requests through paintables, not Document
Rename Document::set_needs_display() to set_needs_repaint() and make it
private. External callers must now go through Node/Paintable which
route the request to the document internally.

Fix one existing misuse in AnimationEffect that was calling
document-level set_needs_display() instead of routing through the
target element's paintable.

This is preparation for per-paintable display list command caching:
repaint requests must go through specific paintables so their cached
command lists can be invalidated.
2026-03-04 19:35:45 +01:00
Andreas Kling
a146225331 LibWeb: Use unsafe layout/paintable accessors where appropriate
Add unsafe_layout_node(), unsafe_paintable(), and unsafe_paintable_box()
accessors that skip layout-staleness verification. These are for use in
contexts where accessing layout/paintable data is legitimate despite
layout not being up to date: tree construction, style recalculation,
painting, animation interpolation, DOM mutation, and invalidation
propagation.

Also add wrapper APIs on Node to centralize common patterns:
- set_needs_display() wraps if (unsafe_paintable()) ...set_needs_display
- set_needs_paint_only_properties_update() wraps similar
- set_needs_layout_update() wraps if (unsafe_layout_node()) ...

And add Document::layout_is_up_to_date() which checks whether layout
tree update flags are all clear.
2026-02-26 21:09:08 +01:00
Aliaksandr Kalenik
1f0f427a3c LibWeb: Remove Document.h include from XMLDocumentBuilder.h
...and SVGDecodedImageData.h

These headers only use Document via forward-declarable references and
smart pointers. Add explicit Document.h includes to .cpp files that
were relying on the transitive include.
2026-02-08 18:51:13 +01:00
Luke Wilde
babfd70ca7 LibGC: Enforce that a Cell type must declare the allocator to use
This ensures that we are explicitly declaring the allocator to use when
allocating a cell(-inheriting) type, instead of silently falling back
to size-based allocation.

Since this is done in allocate_cell, this will only be detected for
types that are actively being allocated. However, since that means
they're _not_ being allocated, that means it's safe to not declare
an allocator to use for those. For example, the base TypedArray<T>,
which is never directly allocated and only the defined specializations
are ever allocated.
2026-01-20 12:00:11 +01:00
ayeteadoe
eb4ff07d83 LibWeb: Define SVGImageElement destructor for Core::Timer forward decl
This fixes a "error: member access into incomplete type 'Core::Timer'"
build error on clang-cl Windows.
2025-11-06 07:10:23 -05:00
Andreas Kling
cebd4cc10d LibWeb: Add ImageProvider virtual to access the DecodedImageData
...and also the current frame index, in case of animations.
2025-11-05 09:11:49 +01:00
Tim Ledbetter
7dade36d96 LibWeb: Ensure SVG image element respects viewBox
Previously, any active viewBox was ignored by SVG image elements.
2025-09-01 13:28:35 +01:00
Erik Kurzinger
42339be999 LibWeb: Use intrinsic size for current_image_bitmap
Currently, ImageProvider::current_image_bitmap takes a Gfx::IntSize
argument which determines the size of the returned bitmap. The default
value of this argument is 0x0 which causes the function to return
nullptr. This behavior is evidently unintuitive enough that it has lead
to incorrect usage in multiple places. For example, the 2D canvas
drawImage method will never actually draw anything because it calls
current_image_bitmap with no arguments. And the naturalWidth and
naturalHeight of an image will always return 0 (even after the image has
loaded) for the same reason.

To correct this and hopefully avoid similar issues in the future,
ImageProvider::current_image_bitmap will be renamed to
current_image_bitmap_sized, and the default value for the size argument
will be removed. For consistency, a similar change will be made to
SVGImageElement::default_image_bitmap.

The existing current_image_bitmap function will no longer take a size
argument. Instead it will always return a bitmap of the image's
intrinsic size. This seems to be what most existing callers had already
assumed was the function's behavior.
2025-08-30 15:49:11 +02:00
Jelle Raaijmakers
676f5837b3 LibWeb: Implement SVGLength's read-only property
An SVGLength can be read-only, e.g. all animVal values cannot be
modified. Implement this for all instantiations of SVGLength.

While we're here, add `fake_animated_length_fixme()` so we can easily
find all sites where we need to improve our animated length game.
2025-08-27 11:50:27 +02:00
Andreas Kling
bd7599ccfc LibCore: Remove macro-generated EventReceiver::try_create(...) factories
We can just use the infallible factory everywhere instead.
2025-08-11 16:55:25 +02:00
Idan Horowitz
bb6855c849 LibWeb: Add a getter for the default image bitmap of SVGImageElement
The default image bitmap is the first frame for animated bitmaps, and
the only frame for non-animated bitmaps.
2025-08-03 21:47:48 +02:00
Andreas Kling
77abe2a84d LibWeb: Allow ImageProvider subclasses to visit additional GC edges
More prep work for CSS content:image.
2025-07-28 22:46:27 +02:00
Tim Ledbetter
4dd538e708 LibWeb/SVG: Prefer href to xlink:href attribute on images 2025-07-12 13:13:47 +02:00
Tim Ledbetter
93a35895ae LibWeb/SVG: Don't crash when changing SVG image href
Previously, a segfault could occur if `m_resource_request` was replaced
before its callbacks were invoked.
2025-07-12 13:13:47 +02:00
Tim Ledbetter
ff3d3840ac LibWeb: Replace usages of Document::parse_url()
The spec has been updated to use `encoding_parse_url()` and
`encoding_parse_and_serialize_url()` instead.
2025-06-24 19:55:43 +02:00
Andreas Kling
3c15fec303 LibWeb: Move "needs layout update" flag from DOM to layout tree
This is in preparation for allowing anonymous boxes to retain their
intrinsic size cache across layouts.
2025-04-21 00:31:08 +02:00
Andreas Kling
a6dfc74e93 LibWeb: Only set prototype once for object with IDL interface
Before this change, we were going through the chain of base classes for
each IDL interface object and having them set the prototype to their
prototype.

Instead of doing that, reorder things so that we set the right prototype
immediately in Foo::initialize(), and then don't bother in all the base
class overrides.

This knocks off a ~1% profile item on Speedometer 3.
2025-04-20 18:43:11 +02:00
Aliaksandr Kalenik
a4463c45b9 LibWeb: Bring back cache of intrinsic sizes across layout runs
12c6ac78e2 with fixed mistake when cache
slot is copied instead of being referenced:
```cpp
auto cache =
    box.cached_intrinsic_sizes().min_content_height.ensure(width);
```
while it should've been:
```cpp
auto& cache =
    box.cached_intrinsic_sizes().min_content_height.ensure(width);
```
2025-03-08 14:52:33 +01:00
Andreas Kling
73a4b176cf Revert "LibWeb: Cache intrinsic sizes across layout runs"
This reverts commit 12c6ac78e2.

Very large performance regression when viewing GitHub repository pages.
2025-03-08 12:08:51 +01:00
Aliaksandr Kalenik
12c6ac78e2 LibWeb: Cache intrinsic sizes across layout runs
This change moves intrinsic sizes cache from
LayoutState, which is local to current layout run,
to layout nodes, so it could be reused between
layout runs. This optimization is possible because
we can guarantee that these measurements will
remain unchanged unless the style of the element
or any of its descendants changes.

For now, invalidation is implemented simply by
resetting cache on whole ancestors chain once we
figured that element needs layout update.
The case when layout is invalidated by DOM's
structural changes is covered by layout tree
invalidation that drops intrinsic sizes cache
along with layout nodes.

I measured improvement on couple websites:
- Mail list on GMail 28ms -> 6ms
- GitHub large code page 47ms -> 36ms
- Discord chat history 15ms -> 8ms
(Time does not include `commit()`)
2025-03-08 11:45:36 +01:00
Andreas Kling
2abbf99a95 LibWeb: Add opt-in tracing of set_needs_layout() calls with reason 2025-03-08 03:37:38 +01:00
InvalidUsernameException
be47f95180 LibWeb: Reduce number of recompiled files for display list headers
This reduces the number of `.cpp` files that need to be recompiled when
one of the below header files changes as follows:

Painting/Command.h: 1030 -> 61
Painting/DisplayList.h: 1030 -> 60
Painting/DisplayListRecorder.h: 557 -> 59
2025-02-23 10:14:39 -05:00
Shannon Booth
22a7cd9700 LibWeb: Port Document encoding_parse_url and parse_url to Optional<URL>
This ports two more APIs away from URL::is_valid.
2025-01-27 00:03:07 +00:00
Tim Ledbetter
400aefb784 LibWeb: Resolve SVGImageElement source URL correctly
This fixes an issue where an SVGImageElement, whose source was a
relative URL would not load inside an iframe.
2025-01-19 15:02:38 +01:00
Andreas Kling
74469a0c1f LibWeb: Make CSS::ComputedProperties GC-allocated 2024-12-22 10:12:49 +01:00
Andreas Kling
c1cad8fa0e LibWeb: Rename CSS::StyleProperties => CSS::ComputedProperties
Now that StyleProperties is only used to hold computed properties, let's
name it ComputedProperties.
2024-12-22 10:12:49 +01:00
Shannon Booth
f87041bf3a LibGC+Everywhere: Factor out a LibGC from LibJS
Resulting in a massive rename across almost everywhere! Alongside the
namespace change, we now have the following names:

 * JS::NonnullGCPtr -> GC::Ref
 * JS::GCPtr -> GC::Ptr
 * JS::HeapFunction -> GC::Function
 * JS::CellImpl -> GC::Cell
 * JS::Handle -> GC::Root
2024-11-15 14:49:20 +01:00
Timothy Flynn
d4f8b598cb LibWeb: Consolidate the attribute change handlers
We currently have 2 virtual methods to inform DOM::Element subclasses
when an attribute has changed, one of which is spec-compliant. This
patch removes the non-compliant variant.
2024-11-14 15:39:02 +01:00
Shannon Booth
1e54003cb1 LibJS+LibWeb: Rename Heap::allocate_without_realm to Heap::allocate
Now that the heap has no knowledge about a JavaScript realm and is
purely for managing the memory of the heap, it does not make sense
to name this function to say that it is a non-realm variant.
2024-11-13 16:51:44 -05:00
Timothy Flynn
93712b24bf Everywhere: Hoist the Libraries folder to the top-level 2024-11-10 12:50:45 +01:00
Renamed from Userland/Libraries/LibWeb/SVG/SVGImageElement.cpp (Browse further)