Commit graph

95 commits

Author SHA1 Message Date
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
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
Andreas Kling
1e3653364b LibWeb: Pause animated images while hidden
Stop HTMLImageElement animation timers when their document becomes
hidden, and resume them only if visibility was what paused them. This
keeps inactive tabs from advancing animated image frames and requesting
more decoded frames from ImageDecoder.

Also avoid starting newly loaded animated images while their document is
hidden, so images loaded in background tabs wait until the page becomes
visible before animating.
2026-05-27 18:44:42 +02:00
Shannon Booth
2a2b0bf91a LibWeb: Use the node document realm for image requests
An HTMLImageElement can be adopted across documents while an image
update is still pending. In that case the element's wrapper realm can
still be the old iframe realm, but the image request must be created and
fetched using the element's current node document.

Using the old iframe realm associates the request with the removed
iframe's document-owned image request cache, so the adopted image can
fail to complete in the parent document. Use document().realm() when
creating/updating/fetching image requests so adopted images continue
loading in their new document.

Add a regression for an image created in an iframe, then adopted into
the parent after the iframe is removed.

Chromium and Webkit do not fire a load event for the test, but Firefox
does. All load the image, where we previously did not.

See: https://github.com/whatwg/html/issues/8492
2026-05-27 02:40:53 +01:00
Andreas Kling
56da8f01d7 LibWeb: Reject stale image decode promises
When an image source changes before a queued decode() job can attach
fetch callbacks, the current request can still be unavailable and have
no shared resource request. Treat that as the spec's current request
changed case instead of asserting while adding callbacks.

Add text coverage for changing an image source immediately after
calling decode(), matching the WPT image-decode-path-changes crash.
2026-05-23 11:36:45 +02:00
Andreas Kling
56e1f1ca22 LibWeb: Avoid relayout for definite-size image updates
When image data becomes available, intrinsic dimensions can change, but
that does not require relayout for image elements whose computed width,
height, and min/max constraints are independent of intrinsic sizing. In
that case, invalidate paint and intrinsic-size caches instead of marking
layout dirty.

Keep the conservative relayout path for auto, percentage,
calc-percentage, and intrinsic sizing constraints, since decoded image
dimensions can affect used size in indefinite contexts. Query image
availability while painting so an existing paintable can switch from alt
text to image content without relayout.

Add text coverage that waits for image load events and checks auto,
percentage, calc-percentage, intrinsic keyword, min/max percentage, and
fixed-size cases without relying on timeouts.
2026-05-22 15:09:29 +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
InvalidUsernameException
46e91c7ba6 LibWeb: Invalidate img-element when loaded from list of available images
When loading an image, there are two success paths: Either the image is
loaded through fetch, or it is part of the list of available images
already and doesn't need to be downloaded anymore.

Only the fetch code path was invalidating style and layout, meaning that
an image loaded from the list of available images was not visible until
something else caused an invalidation.

To fix this, perform the same invalidation in both code paths.

This fixes that most of the images on https://bleepingcomputer.com/ were
not loading until hovered. They are using
[`bLazy.JS`](https://github.com/dinbror/blazy), which loads the files on
a disconnected `img` element and then swaps out `src` or `srcset` on the
actual `img` element once the load has completed.

The `requestAnimationFrame`-dance in the testcase is there to ensure
that the test fails reliably without this change applied.
2026-05-18 12:54:49 +02:00
Andreas Kling
fe31d205a5 LibWeb: Bound per-document decoded image caches
Prune decoded image resources retained by active documents once they
grow beyond a recent working set. Route-heavy applications can load
many unique images through one Document, and the shared resource and
available-image caches would otherwise keep decoded image data alive
after the DOM stopped using those images.

Track cache touches and evict least recently used decoded images from
both caches. This keeps active documents from accumulating unbounded
decoded image resources while preserving a small hot cache for repeat
loads.
2026-05-17 00:29:18 +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
568b7ce7ea LibWeb: Make Paintable tree ref-counted
The Paintable tree and its supplemental painting data structures were
GC allocated because that was the easiest way to manage it and avoid
leaks introduced by ref cycles. This included the Paintable subclasses
themselves plus StackingContext, ChromeWidget, Scrollbar, ResizeHandle,
and scroll-frame state.

We are now trying to reduce GC allocation churn on layout and painting
updates, so keeping this short-lived rendering tree outside the JS heap
is a better fit. Move Paintable to RefCountedTreeNode, make painting
helpers ref-counted or weakly reference Paintables, and update the
layout and event-handler call sites to use RefPtr/WeakPtr ownership.
2026-05-07 15:03:44 +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
Tim Ledbetter
d1fc4b4234 LibWeb: Route presentational hints through the CSS cascade
Previously, presentational hints bypassed the regular cascade pipeline
and wrote directly into `CascadedProperties` under
`CascadeOrigin::Author`. That meant `var()` substitution and the
invalid-at-computed-value-time fallback had to be duplicated in a
separate per-element pass, which in practice missed the IACVT step and
could leave a `GuaranteedInvalidStyleValue` in the cascaded
properties. This caused a crash in downstream code that assumed the
value had been resolved.

This introduces an `AuthorPresentationalHint` cascade origin and feeds
them through the cascade as normal declarations. This means that
`var()` resolution now happens in only one place.
2026-04-30 19:50:28 +01:00
Andreas Kling
30f37d691c LibWeb: Share supported image MIME type list
Extract the file-local is_supported_image_type() helper from
HTMLImageElement into a small standalone translation unit so other
parts of the engine can ask the same question. The next commit reuses
it for the image-set() type() filter.

The list is still hard-coded; deriving it from the registered image
decoders remains a FIXME.
2026-04-25 14:54:10 +02:00
Aliaksandr Kalenik
1193409f64 LibWeb: Wait for CompletelyAvailable state before resolving img.decode()
HTMLImageElement's update-the-image-data step 16 queues its state
transition and load event dispatch via a 1 ms BatchingDispatcher, so
the current request does not become CompletelyAvailable synchronously
when the fetch finishes. decode()'s on_finish callback, however, was
queuing its resolve task directly on the event loop, bypassing the
batch. That race meant decode() could resolve while the image request
was still in Unavailable state, so any .then() handler inspecting
img.width / img.height (or anything derived from the bitmap) would see
zeros.

Google Maps hits this on its .9.png road shield icons: after awaiting
img.decode() it reads a.width / a.height and calls
ctx.getImageData(0, 0, 0, 0), which throws IndexSizeError and aborts
the tile rendering pipeline.

Route decode()'s on_finish through the same BatchingDispatcher so both
are processed in the same batch, with the decode resolution queued
after step 16's element task.
2026-04-24 19:27:26 +02: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
Jelle Raaijmakers
1d904d2295 LibWeb: Reduce noise from <img>'s decoding attribute
Our implementation of image decoding and its effect on
presenting/rendering the DOM is already effectively "async". The value
"auto" is implementation-defined, but should likely default to async
behavior as well. That leaves us with `decoding="sync"`, for which we
should still show a FIXME.
2026-03-12 16:58:00 +00:00
Aliaksandr Kalenik
3012c0fe72 LibWeb: Store scroll frames by value in contiguous storage
Replace per-frame heap-allocated RefCounted ScrollFrame objects with a
single contiguous Vector<ScrollFrame> inside ScrollState. All frames for
a viewport are now stored in one allocation, using type-safe
ScrollFrameIndex instead of RefPtr pointers.

This reduces allocation churn, improves cache locality, and moves
parent-chain traversal (cumulative offset, nearest scrolling ancestor)
into ScrollState — similar to how visual context nodes were recently
consolidated into AccumulatedVisualContextTree.
2026-03-12 12:06:40 +01:00
Aliaksandr Kalenik
8f7bb7dd2e LibWeb: Skip layout update for disconnected elements querying metrics
Introduce Document::update_layout_if_needed_for_node() which only calls
update_layout() when the node is connected. Use it at all call sites
that query layout metrics (offsets, client dimensions, image size, SVG
bounding box, etc.) so disconnected elements no longer trigger an
unnecessary layout.
2026-03-05 14:17:20 +01: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
Aliaksandr Kalenik
b49c243abd LibWeb: Fix stale image request callbacks corrupting newer requests
When img.src is changed rapidly (e.g., YouTube Music sets a GIF
placeholder then swaps to a real URL via IntersectionObserver), the
failure callback from the stale first request could corrupt the newer
request by calling abort_the_image_request on the now-reassigned
m_current_request.

Fix this by using the existing m_update_the_image_data_count generation
counter to detect stale fetch callbacks.

This fixes thumbnail loading on YouTube Music.
2026-03-03 14:12:06 +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
Jonathan Gamble
34742681de LibWeb: Avoid spin_until in HTMLImageElement::decode_image
Improve performance of overlapping decodes. Reject on src changes
during the operation.
2026-02-23 10:11:04 +01:00
Callum Law
b8f2989ccb LibWeb: Reduce recompilation from editing CascadedProperties.h
This reduces the recompilation of editing `Properties.json` from ~1429
to ~158
2026-02-19 11:27:06 +00:00
Rocco Corsi
7eb0bb7c70 LibWeb: Reset animated frame index when loading new img element src
When an img element is changed from animated image to static image, the
animation briefly continues into the new image, even if the new image
has only a single frame (static image).

Could also impact when going from animated image to another animated
image, but the new image has less frames versus the previously animated
image.

In some cases a newly loaded static image would continue to be animated,
so that is also stopped.

fixes: #7879
fixes: #7945
2026-02-19 10:51:29 +01:00
Andreas Kling
97986f9739 LibWeb: Stream animated image frames on demand
Add AnimatedDecodedImageData which implements DecodedImageData with
an 8-slot buffer pool instead of storing all frames in memory.
Frames are requested on demand from the ImageDecoder service as
the animation progresses.

For a 344-frame animated image at 1920x1080, this reduces
WebContent memory from ~1.3 GB to ~66 MB.

The streaming class owns frame progression and synchronizes
multiple callers (HTMLImageElement and ImageStyleValue) through
notify_frame_advanced() returning the authoritative frame index.
When a frame isn't in the pool, the last displayed frame is shown
as a fallback (brief freeze rather than blank).

Rename the old AnimatedBitmapDecodedImageData (which now only
handles static/single-frame images) to BitmapDecodedImageData.
2026-02-13 18:34:24 +01:00
Chase Knowlden
b8f31179b2 LibWeb: Use dimension image source for images
Fixes tiny images on Wikipedia
2026-02-13 10:42:38 +00:00
Andreas Kling
bacd946721 LibWeb: Bail out of image callbacks when document becomes inactive
HTMLImageElement's "update the image data" algorithm checks
is_fully_active() at the start, but its async continuations
(microtasks, element tasks, batching dispatcher callbacks) skip
this check. When an iframe is removed or navigated, these
callbacks fire on an inactive document, causing crashes.

Fix this with two changes:

1) Add is_fully_active() bail-out checks at all async callback
   entry points in HTMLImageElement. Each bail-out also clears
   the DocumentLoadEventDelayer to prevent blocking the parent
   document's load event forever.

2) Create the DocumentObserver eagerly in initialize() (like
   HTMLMediaElement) with a document_became_inactive callback
   that clears the load event delayer and stops the animation
   timer. Fire document_became_inactive from Document::destroy()
   in addition to did_stop_being_active_document_in_navigable(),
   since iframe removal takes a different path than navigation.
   A guard flag prevents duplicate firing.
2026-02-10 21:19:35 +01:00
Psychpsyo
b8b0522cdc LibWeb: Apply base presentational hints across the board 2026-02-04 20:21:36 +01:00
Tim Ledbetter
79a427e1ef LibWeb: Implement HTMLImageElement x() and y() getters
These attributes get the image's top left border edge  relative to the
root element's origin.

These methods ignore any transforms.
2026-01-11 00:33:08 +01:00
InvalidUsernameException
28ba610f32 Everywhere: Avoid large rebuilds when editing (Immutable)Bitmap headers
This reduces the number of recompiled files as follow:
- Bitmap.h: 1309 -> 101
- ImmutableBitmap.h: 1218 -> 75
2025-11-28 18:32:48 +01:00
Tim Ledbetter
fdd3975104 LibWeb: Don't run update_the_image_data() algorithm if already started
This ensures that events are not fired if the image source is updated
while it is being fetched.
2025-11-15 12:39:37 +01:00
Psychpsyo
100f37995f Everywhere: Clean up AD-HOC and FIXME comments without colons 2025-11-13 15:56:04 +01:00
Luke Wilde
82bd3d3891 LibWeb: Avoid invoking Trusted Types where avoidable
Prevents observably calling Trusted Types, which can run arbitrary JS,
cause crashes due to use of MUST and allow arbitrary JS to modify
internal elements.
2025-11-06 11:43:06 -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
Callum Law
05c336ea4e LibWeb: Use document's viewport when resolving lengths in media queries
Previously we would always use the window's viewport which was incorrect
if we were within an iframe.

This is likely applicable to all uses of
`Length::ResolutionContext::for_window`.
2025-10-07 10:32:59 +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
Tim Ledbetter
cb1a1a5cb5 LibWeb: Replace is<T>() with as_if<T>() where possible 2025-08-25 18:45:00 +02:00
Timothy Flynn
70db474cf0 LibJS+LibWeb: Port interned bytecode strings to UTF-16
This was almost a no-op, except we intern JS exception messages. So the
bulk of this patch is porting exception messages to UTF-16.
2025-08-14 10:27:08 +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
Sam Atkins
4e92ab52e3 LibWeb/CSS: Rename CSSKeywordValue -> KeywordStyleValue
The typed-om CSSKeywordValue will need to be a separate class.
2025-08-08 15:19:03 +01:00
Idan Horowitz
cc0496284b LibWeb: Add a getter for the default image bitmap of HTMLImageElement
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
Glenn Skrzypczak
6e6507c8c5 LibWeb/HTML: Sanitize email input with multiple attribute
This implements the missing part of the value sanitization algorithm
for email inputs with the multiple attribute.
2025-07-22 23:02:33 +01: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
Sam Atkins
423cdd447d LibWeb+LibGfx: Apply editorial punctuation/whitespace/markup fixes
Corresponds to d426109ea1
and fd08f81d06
2025-06-25 03:12:19 +12:00
Shannon Booth
0bdcaf02d3 LibWeb/HTML: Only update the image data on fully loaded document
Documents created by DOMParser and fragment documents do not
have an origin set on the document by the spec. These documents
also happen to never become fully active.

By properly implementing the steps for the <img> element to only
update the image data for documents which are fully active, this
fixes a crash for img elements in these types of documents.

Unfortunately, this is not a full fix for the microtask queue case.
This is because it seems possible for node document for an <img>
element to be changed during the microtask queue for that document.
It is not clear to me how this can be fixed in a nice way.
2025-06-24 09:56:14 +02:00
Shannon Booth
bc85a9bace LibWeb/HTML: Don't return any errors for update_the_img_data
There should not be any exceptions to propagate, so let's update
the return type accordingly.
2025-06-24 09:56:14 +02:00