Commit graph

98 commits

Author SHA1 Message Date
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
Zaggy1024
4eb310cd3f LibWeb: Skip range requests for media if the server won't accept them
Currently, this just respects the reported value from Accept-Ranges,
but we could also just try sending a range request and see if the
server rejects it, then fall back to a normal request after. For now,
this is fine, and we can make it use a fallback later if needed.
2026-01-29 05:22:27 -06:00
Zaggy1024
e8dcf5fad2 LibWeb: Keep a seek's target position when pausing a media element
The spec's steps for pausing an HTMLMediaElements prescribe setting the
official playback position to the current playback position, but the
seeking steps are not synchronous, so there's no guarantee that the
current playback position reflects the seek. Therefore, we need to skip
that step if we're in the middle of a seek.

This is included in a pull request to the HTML spec:
https://github.com/whatwg/html/pull/11792
2026-01-29 05:22:27 -06:00
Zaggy1024
7ede4e8b03 LibWeb: Verify and use the response's Content-Range in HTMLMediaElement
Using the response's header over our own request's byte range is likely
to be a little more reliable for piecing the data together in the
stream, in case the server decides to give us a slightly different
range than we requested. The media element spec requires that the
Content-Range parses correctly anyway, so we should make use of the
values we get out of it.
2026-01-29 05:22:27 -06:00
Zaggy1024
1b06792e8f LibMedia+LibWeb: Use range requests to fulfill media data
This makes media playback able to start without having to wait for data
to sequentially download, especially when seeking the media to a
timestamp residing in data that hasn't loaded yet.

Initially, the HTMLMediaElement will request the file without range a
range request. Then, if the IncrementallyPopulatedStream finds that it
needs data that is not yet available, it will decide whether to wait
for that data to be received through the current request, or start a
new request that is closer to the required data.

In this commit, it assumes that the server will support range requests.
2026-01-29 05:22:27 -06:00
Zaggy1024
58f7c906e2 LibWeb: Rename HTMLMediaElement::setup_playback_manager to set_up 2026-01-29 05:22:27 -06:00
Aliaksandr Kalenik
551ea7d6fc LibWeb: Use incrementally_read() for media fetching 2025-12-16 02:42:48 -06:00
Aliaksandr Kalenik
c5d8cb5c47 LibMedia: Change demuxers to use IncrementallyPopulatedStream as input
Refactor the FFmpeg and Matroska demuxers to consume data through
`IncrementallyPopulatedStream::Cursor` instead of a pointer to fully
buffered.

This change establishes a new rule: each track must be initialized with
its own cursor. Data providers now explicitly create a per-track context
via `Demuxer::create_context_for_track(track, cursor)`, and own pointer
to that cursor. In the upcoming changes, holding the cursor in the
provider would allow to signal "cancel blocking reads" so an
in-flight seek can fail immediately when a newer seek request arrives.
2025-12-16 02:42:48 -06:00
Zaggy1024
d0d10b4200 LibWeb: Update the duration of an element based on media data 2025-12-10 16:02:40 -06:00
Aliaksandr Kalenik
9f60828a57 LibMedia+LibWeb: Create demuxer and extract tracks on a separate thread
Demuxer creation and track+duration extraction are moved to a separate
thread so that the media data byte buffer is no longer accessed from the
main thread. This will be important once the buffer is populated
incrementally, as having the main thread both populate and read from the
same buffer could easily lead to deadlocks. Aside from that, moving
demuxer creation off the main thread helps to be more responsive.

`VideoDataProvider` and `AudioDataProvider` now accept the main thread
event loop pointer as they are constructed from the thread responsible
for demuxer creation.
2025-12-09 17:36:18 -06:00
Psychpsyo
6951ef4ee3 Meta: Validate proper formatting for FIXMEs and AD-HOCs 2025-11-13 15:56:04 +01:00
Luke Wilde
167de08c81 LibWeb: Remove exception throwing from Fetch
These were only here to manage OOMs, but there's not really any way to
recover from small OOMs in Fetch especially with its async nature.
2025-11-07 04:08:30 +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
Zaggy1024
418f1575b0 LibWeb: Stop returning the value in HTMLMediaElement::set_current_time
This wasn't actually affecting the result in a script assigning a
variable to the result of an expression assigning to currentTime.
2025-10-29 06:22:48 +00:00
Zaggy1024
93fde59892 LibWeb: Make the value of assignment to media currentTime the rhs value
In cases where a script assigns `x = video.currentTime = y`, we are
expected to have a result of `x === y`, even if the video's duration
is less than y.

According to the spec, this happens because the official playback
position is set to `y` in this case, but since we are following
implementations in making `currentTime` immediately return the position
on the valid media timeline, we have to specifically return the
unchanged value from the setter.

See: https://github.com/whatwg/html/issues/11773
2025-10-27 17:28:49 -07:00