Skip to content

Commit 4471e8c

Browse files
Zaggy1024gmta
authored andcommitted
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: whatwg/html#11775
1 parent 3be6b95 commit 4471e8c

File tree

2 files changed

+37
-4
lines changed

2 files changed

+37
-4
lines changed

Libraries/LibWeb/HTML/HTMLMediaElement.cpp

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,8 @@ void HTMLMediaElement::set_current_playback_position(double playback_position)
314314
// which these steps should be invoked, which is when we've reached the end of the media playback.
315315
if (m_current_playback_position == m_duration)
316316
reached_end_of_media_playback();
317+
318+
upon_has_ended_playback_possibly_changed();
317319
}
318320

319321
// https://html.spec.whatwg.org/multipage/media.html#dom-media-duration
@@ -332,8 +334,10 @@ bool HTMLMediaElement::ended() const
332334
{
333335
// The ended attribute must return true if, the last time the event loop reached step 1, the media element had ended
334336
// playback and the direction of playback was forwards, and false otherwise.
335-
// FIXME: Add a hook into EventLoop::process() to be notified when step 1 is reached.
336-
return has_ended_playback() && direction_of_playback() == PlaybackDirection::Forwards;
337+
// NOTE: We queue a task to set this at event loop step 1 whenever something happens that may affect the resulting value.
338+
// Currently, that is when the ready state changes, when the current playback position changes, or the duration
339+
// changes.
340+
return m_ended;
337341
}
338342

339343
// https://html.spec.whatwg.org/multipage/media.html#durationChange
@@ -354,6 +358,8 @@ void HTMLMediaElement::set_duration(double duration)
354358

355359
m_duration = duration;
356360

361+
upon_has_ended_playback_possibly_changed();
362+
357363
if (auto* paintable = this->paintable())
358364
paintable->set_needs_display();
359365
}
@@ -1456,6 +1462,7 @@ void HTMLMediaElement::set_ready_state(ReadyState ready_state)
14561462
{
14571463
ScopeGuard guard { [&] {
14581464
m_ready_state = ready_state;
1465+
upon_has_ended_playback_possibly_changed();
14591466
set_needs_style_update(true);
14601467
} };
14611468

@@ -1863,6 +1870,11 @@ void HTMLMediaElement::set_paused(bool paused)
18631870
set_needs_style_update(true);
18641871
}
18651872

1873+
void HTMLMediaElement::set_ended(bool ended)
1874+
{
1875+
m_ended = ended;
1876+
}
1877+
18661878
// https://html.spec.whatwg.org/multipage/media.html#dom-media-defaultplaybackrate
18671879
void HTMLMediaElement::set_default_playback_rate(double new_value)
18681880
{
@@ -1984,7 +1996,11 @@ bool HTMLMediaElement::has_ended_playback() const
19841996
direction_of_playback() == PlaybackDirection::Forwards &&
19851997

19861998
// The media element does not have a loop attribute specified.
1987-
!has_attribute(HTML::AttributeNames::loop)) {
1999+
// AD-HOC: Use the value of the loop attribute from the last time we reached end of playback.
2000+
// Without this change, the ended attribute changes when enabling the loop attribute after
2001+
// playback has ended, and playback will not restart when playing the element.
2002+
// See https://github.com/whatwg/html/issues/11775
2003+
!m_loop_was_specified_when_reaching_end_of_media_resource) {
19882004
return true;
19892005
}
19902006

@@ -2001,11 +2017,21 @@ bool HTMLMediaElement::has_ended_playback() const
20012017
return false;
20022018
}
20032019

2020+
void HTMLMediaElement::upon_has_ended_playback_possibly_changed()
2021+
{
2022+
run_when_event_loop_reaches_step_1(GC::Function<void()>::create(heap(), [&] {
2023+
// The ended attribute must return true if, the last time the event loop reached step 1, the media element had ended
2024+
// playback and the direction of playback was forwards, and false otherwise.
2025+
set_ended(has_ended_playback() && direction_of_playback() == PlaybackDirection::Forwards);
2026+
}));
2027+
}
2028+
20042029
// https://html.spec.whatwg.org/multipage/media.html#reaches-the-end
20052030
void HTMLMediaElement::reached_end_of_media_playback()
20062031
{
20072032
// 1. If the media element has a loop attribute specified,
2008-
if (has_attribute(HTML::AttributeNames::loop)) {
2033+
m_loop_was_specified_when_reaching_end_of_media_resource = has_attribute(HTML::AttributeNames::loop);
2034+
if (m_loop_was_specified_when_reaching_end_of_media_resource) {
20092035
// then seek to the earliest possible position of the media resource and return.
20102036
seek_element(0);
20112037
// FIXME: Tell PlaybackManager that we're looping to allow data providers to decode frames ahead when looping

Libraries/LibWeb/HTML/HTMLMediaElement.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,7 @@ class HTMLMediaElement : public HTMLElement {
211211
void set_show_poster(bool);
212212
void set_paused(bool);
213213
void set_duration(double);
214+
void set_ended(bool);
214215

215216
void volume_or_muted_attribute_changed();
216217
void update_volume();
@@ -224,6 +225,7 @@ class HTMLMediaElement : public HTMLElement {
224225
PlaybackDirection direction_of_playback() const;
225226

226227
bool has_ended_playback() const;
228+
void upon_has_ended_playback_possibly_changed();
227229
void reached_end_of_media_playback();
228230

229231
void dispatch_time_update_event();
@@ -291,6 +293,9 @@ class HTMLMediaElement : public HTMLElement {
291293
// https://html.spec.whatwg.org/multipage/media.html#dom-media-paused
292294
bool m_paused { true };
293295

296+
// https://html.spec.whatwg.org/multipage/media.html#dom-media-ended
297+
bool m_ended { false };
298+
294299
// https://html.spec.whatwg.org/multipage/media.html#dom-media-defaultplaybackrate
295300
double m_default_playback_rate { 1.0 };
296301

@@ -334,6 +339,8 @@ class HTMLMediaElement : public HTMLElement {
334339
GC::Ptr<VideoTrack> m_selected_video_track;
335340
RefPtr<Media::DisplayingVideoSink> m_selected_video_track_sink;
336341

342+
bool m_loop_was_specified_when_reaching_end_of_media_resource { false };
343+
337344
// Cached state for layout.
338345
Optional<MediaComponent> m_mouse_tracking_component;
339346
Optional<MediaComponent> m_hovered_component;

0 commit comments

Comments
 (0)