Commit graph

112 commits

Author SHA1 Message Date
Zaggy1024
ac70eb601f LibWeb: Throttle media elements' time marches on ready state updates 2026-04-10 15:21:07 -05:00
Zaggy1024
c11dc9fae9 LibWeb: Prevent reentrancy in HTMLMediaElement::set_ready_state()
We could apparently update the ready state within time_marches_on()
called by set_ready_state(), which could result in triggering autoplay
twice for the same state transition.
2026-04-10 15:21:07 -05:00
Zaggy1024
cb852a7e19 LibWeb: Don't fast seek when seeking media to its current position
This ensures that we don't seek away from the end of the file if we're
already there and fastSeek() is called with a timestamp at or past the
end.
2026-04-10 04:08:28 -05:00
Zaggy1024
84e0b7d36f LibWeb: Increment the fetch generation when cancelling media fetch
The case of an unsupported format error wasn't covered for this,
meaning that it could crash if the fetch completed successfully after
the fetch was cancelled due to such an error.

A crash test is included for this issue, using an echo of a large
corrupted WebM file to ensure that the fetch completes after media
init.
2026-04-08 13:03:39 +02:00
Zaggy1024
6a46bcaf35 LibWeb: Remove an unused capture in the media data request callback 2026-04-08 13:03:39 +02:00
Zaggy1024
a19c40aac5 LibWeb: Move closing of incremental media streams to the fetch cancel
This should ensure that the close() happens in all cases where the
fetch completion may not occur otherwise.
2026-04-08 13:03:39 +02:00
Tim Ledbetter
648ececa62 LibWeb: Remove unused ran_media_element_task variable
Writing to this variable triggered a stack use after return ASAN error.
This variable is safe to remove since it was written to but never read.
2026-04-08 05:10:40 +02:00
Zaggy1024
1467127d35 LibMedia+LibWeb: Disable audio output in headless mode
Audio output on macOS was consuming Core Audio resources until the
PlaybackStream creation took well over the timeout for some tests.
This was observed in media-source-buffered.html, where it would time
out due to the long-running callback on the main thread to create the
PlaybackStream for AudioMixingSink.

However, the AudioUnit init should definitely not be blocking the main
thread, so I've added a FIXME there.
2026-04-01 02:54:22 -05:00
Zaggy1024
94be6c7611 LibWeb: Prevent a crash when triggering media load in the error handler
If we fire the error event synchronously within the on_error callback,
then we'll end up destroying the PlaybackManager inside its own
callback and crash. Instead, queue a task to execute the error steps.

This could happen with or without MSE, but I observed it occurring on
YouTube with MSE when we hit a decoding error, since they immediately
try another source when an error is reported.
2026-04-01 02:54:22 -05:00
Zaggy1024
51c3f7c41e LibWeb: Implement appending and demuxing WebM MSE segments
The segments are parsed for the SourceBufferProcessor by the
WebMByteStreamParser. It parses the initialization segment to update
its internal set of tracks, then SourceBufferProcessor/SourceBuffer set
them up for playback. When a media segment is received, it also parses
as much of it as is available, returning all the coded frames found so
far. SourceBufferProcessor then tells TrackBufferDemuxer to remove any
overlapping frames and insert the new ones.

TrackBufferDemuxer implements the Demuxer interface in terms of the
coded frame store maintained by the SourceBufferProcessor. It returns
the frames in decode order when requested by a data provider. When a
is needed, it finds the keyframe prior to the target timestamp, and
checks that there are no gaps in data up to the target timestamp. If
there are any gaps, it blocks until the gaps are gone.
2026-04-01 02:54:22 -05:00
Zaggy1024
d960c1eaf5 LibWeb: Implement assignment of MediaSource to HTMLMediaElement src
Also, support the srcObject attribute, and fire the sourceopen event
at the MediaSource.
2026-04-01 02:54:22 -05:00
Zaggy1024
6dcfe20f1e LibWeb: Rename HTMLMediaElement::FetchData -> RemoteFetchData
This struct is only used for remote resources, so let's make that
clearer before adding local resource support in the form of
MediaSources.
2026-04-01 02:54:22 -05:00
Zaggy1024
a41e1ad080 LibWeb: Include a comment for media src attribute and match spec
We're apparently not supposed to load anything when the attribute is
removed.
2026-04-01 02:54:22 -05:00
Zaggy1024
6034a93c83 LibWeb: Expose PlaybackManager's buffered range on the media element 2026-04-01 02:54:22 -05:00
Zaggy1024
f3832c0b36 LibWeb: Use MSE steps to update the media element's ready state
These steps are the best definition we have for how the ready state
should be set, and it seems to be reasonable to apply to plain file
playback as well.

Since our file demuxers are hardcoded to return the entire duration as
buffered, the ready state immediately progresses to HAVE_CURRENT_DATA.
This will probably change once we can check the demuxers for buffered
data.
2026-04-01 02:54:22 -05:00
Zaggy1024
29db875b7f LibWeb: Don't remove video displays on media element finalization
Removing a display risks triggering callbacks on the playback manager
that may cause a recursive GC. This wasn't having any effect since the
playback manager became an OwnPtr.
2026-04-01 02:54:22 -05:00
Zaggy1024
b4db8f11c5 LibMedia+LibWeb: Align Media::Track more to the web spec
...giving tracks a kind attribute, and renaming name to label.

Demuxers will need to determine the kind attribute, since the spec for
sourcing tracks requires us to select based on info we don't expose.
2026-04-01 02:54:22 -05:00
Zaggy1024
9664c11c15 LibMedia+LibWeb: Remove an unnecessary parameter from on_track_added
The TrackType parameter is redundant, since the actual Track object
already contains it.
2026-04-01 02:54:22 -05:00
Jelle Raaijmakers
a5000d07c0 LibWeb: Prevent running permanently unrunnable tasks in EventLoop
In `::spin_processing_tasks_with_source_until()`, we would first take a
set of tasks based on a filter, and then run them one by one. If there
was more than one task matched and put in that vector, they could
interfere with each other's runnability by making later tasks
permanently unrunnable.

The `::take_tasks_matching()` API is a footgun - remove it in favor of
an API that takes tasks one by one, performing the runnability check
just in time.
2026-03-26 18:48:27 +01:00
Zaggy1024
704269f664 LibWeb: Use PlaybackManager's states to progress media ready state
Since we know whether we're buffering from the PlaybackManager state,
let's use that to update the ready state. This ensures that when we set
the ready state to HAVE_CURRENT_DATA, we actually have a frame.

Setting the ready state to HAVE_ENOUGH_DATA should probably still be
further conditioned on the buffer size in IncrementallyPopulatedStream,
but this is still an improvement for now.
2026-03-21 23:11:47 -05:00
Shannon Booth
cc6536b527 LibWeb/HTML: Always provide ChildrenChangedMetadata to children changed 2026-03-19 09:46:54 +01:00
Lluc Simó
900c593b6e LibWeb: Update gen counter on cancel of media element fetching process
This workaround avoids a crash that occurs when various fetching
processes (in the media element) are quickly started and canceled.
Although a better solution would be to actually remove the body
callbacks when the fetch is stopped, this works for now.
2026-03-06 18:34:52 -06: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
Zaggy1024
f6ed54baf4 LibWeb: Keep the media element alive until fetches complete
d146adf made the fetch callbacks use the media element via weak
references. This caused the `error` event not to fire on media elements
that are detached from the document and go out of scope, if the GC got
to them before the fetch completed.

Instead of relying on weak references in the callbacks, we can stop the
ongoing fetch when the document becomes inactive to allow it to be GCed
after that point. By storing the FetchData on the media element, we're
able to resume the fetch where it left off if the document becomes
active again.

We could potentially figure out a way to make elements with no event
handlers and no parent stop their fetches in order to be GCed sooner,
but that is probably a bit fiddly, so may not be worth it for now.

Fixes a rare flake in WPT's `html/semantics/embedded-content/media-
elements/error-codes/error.html` test. A test to force the bug using
`Internals::gc()` has been added.
2026-03-01 23:13:22 -06:00
Timothy Flynn
24aacfea48 LibWeb: Add a button to the built-in media player to toggle fullscreen 2026-03-01 15:41:43 -06:00
Zaggy1024
02a2eb6da5 LibWeb: Keep delaying the load if an error event changes the media src 2026-02-26 22:02:47 -06:00
Zaggy1024
93537276e6 LibWeb: Don't synchronously set media element's seeking attribute false
This could cause the attribute to be false when the seeking event
fired.
2026-02-26 22:02:47 -06:00
Zaggy1024
d6530d070f LibWeb: Clear the playback manager when forgetting media tracks
This fixes a crash that would occasionally happen in media-load-during-
track-setup.html, where the media element would add new tracks to the
AudioTrackList or VideoTrackList after the src attribute changed and
caused the element to clear those lists.

In order to make this safe, the on_unsupported_format_error needs to
queue a task to invoke the failure callback which eventually clears
the playback manager, or ~PlaybackManager() will try to destroy the
enclosing lambda.
2026-02-26 22:02:47 -06:00
Zaggy1024
57e36ce77c LibWeb: Handle interrupted fetches in the media element 2026-02-26 22:02:47 -06:00
Zaggy1024
fa7f1792bc LibWeb: Don't try to restart the media fetching process after an error
All errors specify that the resource selection algorithm should
terminate, so let's not continue trying to fetch data.
2026-02-26 22:02:47 -06:00
Zaggy1024
b84473ff1d LibWeb: Implement setting media element's network state to Loading 2026-02-26 22:02:47 -06:00
Zaggy1024
a3a3e604c5 LibWeb: Implement media decode error handling in HTMLMediaElement
This wasn't actually hooked up like it should have been.
2026-02-26 22:02:47 -06:00
Zaggy1024
696a328253 LibWeb: Skip waiting for media source failure steps to execute
This wasn't actually doing anything except nesting things within a
spin_until() call.
2026-02-26 22:02:47 -06:00
Zaggy1024
32efd1ffa0 LibWeb: Remove ExceptionOr from the media element source selector
These are unused.
2026-02-26 22:02:47 -06:00
Zaggy1024
a65046fad5 LibWeb: Remove ExceptionOr in the media element's resource selection
These are unused.
2026-02-26 22:02:47 -06:00
Zaggy1024
4bfa0d408e LibWeb: Clang-tidy up HTMLMediaElement a bit 2026-02-26 22:02:47 -06:00
Zaggy1024
7ee96285e2 LibWeb: Await a stable state in media element failed_with_elements()
We found an HTML spec issue that implies that media element steps that
mutate the DOM should run in tasks rather than awaiting a stable state.
Awaiting a stable state implies running in a microtask, which is
apparently not supposed to be used to mutate.
2026-02-26 22:02:47 -06: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
Jelle Raaijmakers
f9e837a832 LibGC+LibWeb: Add and use GC::weak_callback() 2026-02-26 08:03:50 -05:00
Zaggy1024
21019c2fa9 LibWeb: Use UA shadow DOM for media elements' controls
Instead of using a custom paintable to draw the controls for video and
audio elements, we build them out of plain old HTML elements within a
shadow root.

This required a few hacks in the previous commits in order to allow a
replaced element to host children within a shadow root, but it's
fairly self-contained.

A big benefit is that we can drive all the UI updates off of plain old
DOM events (except the play button overlay on videos, which uses the
video element representation), so we can test our media and input event
handling more thoroughly. :^)

The control bar visibility is now more similar to how other browsers
handle it. It will show upon hovering over the element, but if the
cursor is kept still for more than a second, it will hide again. While
dragging, the controls remain visible, and will then hide after the
mouse button is released.

The icons have been redesigned from scratch, and the mute icon now
visualizes the volume level along with indicating the mute state.
2026-02-23 07:27:31 +01:00
Zaggy1024
ab0a358a98 LibWeb: Move video/audio adjust_computed_style to HTMLMediaElement
These are the same code, so we may as well move them up the chain. This
becomes useful in a later commit, where it will be used to rewrite
inline-flow to inline-block for layout of shadow DOM.
2026-02-23 07:27:31 +01:00
Zaggy1024
d9eafc8edc LibWeb: Use one method to cancel media elements' fetches
This should help avoid the footgun of forgetting to check for null on
m_fetch_controller. We had missed this check when firing off an error
due to an unsupported format in the PlaybackManager, so we could call
stop_fetch() on a null pointer if the download had completed already.
2026-02-20 19:11:31 -06:00
Zaggy1024
ebda8fcf11 LibWeb: Clarify the capture safety in HTMLMediaElement::fetch_resource
These tasks' captures aren't clearly safe as written, since raw
references don't make it apparent that we're capturing a GC-aware
reference. Conservative scanning made this safe, but let's make it a
bit clearer.
2026-02-20 12:46:17 -06:00
Aliaksandr Kalenik
8a31ecdf39 LibWeb: Use ExternalContentSource for video painting
Publish new video frames to an ExternalContentSource, and switch
VideoPaintable from draw_scaled_immutable_bitmap to
draw_external_content.

Because DrawExternalContent reads the latest bitmap at replay time,
frame-only updates (no timeline or control change) now call
set_needs_display(InvalidateDisplayList::No) — skipping display list
rebuilds entirely. This addresses problem 2 from the previous commit.
2026-02-20 18:41:33 +01:00
Zaggy1024
ad92622cf4 LibWeb: Make HTMLMediaElement's FetchController reference weak
This allows the FetchController to be reclaimed when the fetch
completes.
2026-02-18 13:13:32 -06:00
Zaggy1024
b25562ead7 LibWeb: Break a reference loop on HTMLMediaElement::FetchData
The stream's data request callback can't hold a strong reference to
FetchData, as that will create a reference loop:

FetchData -> IncrementallyPopulatedStream -> (lambda) -> FetchData

To prevent a use-after-free on the FetchData& capture, we clear the
data request callback in ~FetchData().
2026-02-18 13:13:32 -06:00
Zaggy1024
a283489799 LibWeb: Close the media element's stream when it is destroyed
This prevents the PlaybackManager's init thread from hanging waiting
for data that will never come after the media element gets GCed.
2026-02-18 13:13:32 -06:00
Zaggy1024
af45418fbf Everywhere: Rename IncrementallyPopulatedStream::reached_end_of_body
This needs to be called even if we haven't reached the end of the body,
so let's call it close() instead.
2026-02-18 13:13:32 -06:00
Zaggy1024
15d0bc86fc LibWeb: Pass HTMLMediaElement::FetchData& to restart_fetch_at_offset
No behavior change.
2026-02-18 13:13:32 -06:00
Zaggy1024
d146adfd66 LibWeb: Don't keep HTMLMediaElement alive in its fetch callbacks
This isn't strictly necessary to allow the element to be collected, but
it doesn't make sense for a fetch to keep the media element alive.
2026-02-18 13:13:32 -06:00