Commit graph

87 commits

Author SHA1 Message Date
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
Zaggy1024
9e4c87ab85 LibWeb: Ensure that media elements can seek to the duration
Due to the round trip of Duration -> double -> Duration, seeking to the
end of some media can sometimes result in the seek being resolved close
to the end but not quite there. This is a little bit of a hack to make
that work, but may be necessary depending on how the spec changes with
regard to the value returned by currentTime after a seek begins.
2025-10-27 17:28:49 -07:00
Zaggy1024
cb1719aa81 LibWeb: Don't loop the media element when paused
We should be able to seek to the end of the media without looping if
we're paused.

See: https://github.com/whatwg/html/issues/11774
2025-10-27 17:28:49 -07:00
Zaggy1024
4471e8c0ec LibWeb: Consider playback ended when loop is set after ending playback
This allows playback to restart when playing is requested after the end
of playback was reached while loop was disabled, regardless of whether
loop is then subsequently enabled.

This matches other browsers' implementations, but differs from the spec
in how the ended attribute is handled.

See: https://github.com/whatwg/html/issues/11775
2025-10-27 17:28:49 -07:00
Zaggy1024
3be6b957f8 LibWeb: Add the concept of direction of playback in HTMLMediaElement 2025-10-27 17:28:49 -07:00
Zaggy1024
45727d7a58 LibWeb: Fire the media element ended event inside a task
The spec changed in this regard, and this change ensures that once the
ended attribute is updated only during event loop step 1, ended event
handlers will see the ended attribute set to true.
2025-10-27 17:28:49 -07:00
Zaggy1024
e81fece123 LibWeb: Abort media element resource selection upon loading a source
This fixes a crash when playing video on The Cutting Room Floor.

Without aborting the resource selection algorithm, two resource
selection algorithms could be running at once, resulting in the
element requesting removal of a track from the PlaybackManager
immediately after it had been replaced with a different instance.
PlaybackManager asserts that removal of a track is valid, so this was
causing a WebContent crash.
2025-10-27 17:28:49 -07:00
Zaggy1024
122f97c68d LibWeb: Update media volume when creating a playback manager 2025-10-27 17:28:49 -07:00
Zaggy1024
49f088275e LibWeb: Implement HTMLMediaElement looping 2025-10-27 17:28:49 -07:00
Zaggy1024
16b296c090 LibWeb: Update HTMLMediaElements' playback positions when invisible 2025-10-27 17:28:49 -07:00
Zaggy1024
e9495d0ba0 LibWeb: Implement media element seeking through PlaybackManager
Including the behavior to conditionally seek forward when fast seeking!
2025-10-27 17:28:49 -07:00
Zaggy1024
8d9a493b1b LibMedia+LibWeb: Implement media volume/muting 2025-10-27 17:28:49 -07:00