فهرست منبع

LibWeb: Implement the HTMLMediaElement paused attribute

Note that the default value of the attribute is true. We were previously
autoplaying videos as soon as they loaded - this will prevent that from
happening until the paused attribute is set to false.
Timothy Flynn 2 سال پیش
والد
کامیت
90921a4f16

+ 42 - 5
Userland/Libraries/LibWeb/HTML/HTMLMediaElement.cpp

@@ -179,9 +179,14 @@ WebIDL::ExceptionOr<void> HTMLMediaElement::load_element()
         if (m_ready_state != ReadyState::HaveNothing)
             set_ready_state(ReadyState::HaveNothing);
 
-        // FIXME: 6. If the paused attribute is false, then:
-        //            1. Set the paused attribute to true.
-        //            2. Take pending play promises and reject pending play promises with the result and an "AbortError" DOMException.
+        // 6. If the paused attribute is false, then:
+        if (!paused()) {
+            // 1. Set the paused attribute to true.
+            set_paused(true);
+
+            // FIXME 2. Take pending play promises and reject pending play promises with the result and an "AbortError" DOMException.
+        }
+
         // FIXME: 7. If seeking is true, set it to false.
         // FIXME: 8. Set the current playback position to 0.
         //            Set the official playback position to 0.
@@ -737,7 +742,10 @@ void HTMLMediaElement::set_ready_state(ReadyState ready_state)
             dispatch_event(DOM::Event::create(this->realm(), HTML::EventNames::canplay).release_value_but_fixme_should_propagate_errors());
         });
 
-        // FIXME: If the element's paused attribute is false, the user agent must notify about playing for the element.
+        // If the element's paused attribute is false, the user agent must notify about playing for the element.
+        if (!paused())
+            notify_about_playing();
+
         return;
     }
 
@@ -746,10 +754,12 @@ void HTMLMediaElement::set_ready_state(ReadyState ready_state)
         // If the previous ready state was HAVE_CURRENT_DATA or less, the user agent must queue a media element task given the media element to fire an event
         // named canplay at the element, and, if the element's paused attribute is false, notify about playing for the element.
         if (m_ready_state <= ReadyState::HaveCurrentData) {
-            // FIXME: Handle the paused attribute.
             queue_a_media_element_task([this] {
                 dispatch_event(DOM::Event::create(this->realm(), HTML::EventNames::canplay).release_value_but_fixme_should_propagate_errors());
             });
+
+            if (!paused())
+                notify_about_playing();
         }
 
         // The user agent must queue a media element task given the media element to fire an event named canplaythrough at the element.
@@ -774,4 +784,31 @@ void HTMLMediaElement::set_ready_state(ReadyState ready_state)
     }
 }
 
+// https://html.spec.whatwg.org/multipage/media.html#notify-about-playing
+void HTMLMediaElement::notify_about_playing()
+{
+    // FIXME: 1. Take pending play promises and let promises be the result.
+
+    // 2. Queue a media element task given the element and the following steps:
+    queue_a_media_element_task([this]() {
+        // 1. Fire an event named playing at the element.
+        dispatch_event(DOM::Event::create(realm(), HTML::EventNames::playing).release_value_but_fixme_should_propagate_errors());
+
+        // FIXME: 2. Resolve pending play promises with promises.
+    });
+
+    on_playing();
+}
+
+void HTMLMediaElement::set_paused(bool paused)
+{
+    if (m_paused == paused)
+        return;
+
+    m_paused = paused;
+
+    if (m_paused)
+        on_paused();
+}
+
 }

+ 12 - 0
Userland/Libraries/LibWeb/HTML/HTMLMediaElement.h

@@ -45,6 +45,7 @@ public:
 
     WebIDL::ExceptionOr<void> load();
     double duration() const;
+    bool paused() const { return m_paused; }
     void pause() const;
 
     JS::NonnullGCPtr<VideoTrackList> video_tracks() const { return *m_video_tracks; }
@@ -55,6 +56,11 @@ protected:
     virtual JS::ThrowCompletionOr<void> initialize(JS::Realm&) override;
     virtual void visit_edges(Cell::Visitor&) override;
 
+    // Override in subclasses to handle implementation-specific behavior when the element state changes
+    // to playing or paused, e.g. to start/stop play timers.
+    virtual void on_playing() { }
+    virtual void on_paused() { }
+
 private:
     struct EntireResource { };
     using ByteRange = Variant<EntireResource>; // FIXME: This will need to include "until end" and an actual byte range.
@@ -71,6 +77,9 @@ private:
     WebIDL::ExceptionOr<void> handle_media_source_failure();
     void forget_media_resource_specific_tracks();
     void set_ready_state(ReadyState);
+
+    void notify_about_playing();
+    void set_paused(bool);
     void set_duration(double);
 
     // https://html.spec.whatwg.org/multipage/media.html#media-element-event-task-source
@@ -86,6 +95,9 @@ private:
     // https://html.spec.whatwg.org/multipage/media.html#dom-media-duration
     double m_duration { NAN };
 
+    // https://html.spec.whatwg.org/multipage/media.html#dom-media-paused
+    bool m_paused { true };
+
     // https://html.spec.whatwg.org/multipage/media.html#dom-media-videotracks
     JS::GCPtr<VideoTrackList> m_video_tracks;
 

+ 1 - 0
Userland/Libraries/LibWeb/HTML/HTMLMediaElement.idl

@@ -31,6 +31,7 @@ interface HTMLMediaElement : HTMLElement {
 
     // playback state
     readonly attribute unrestricted double duration;
+    readonly attribute boolean paused;
     [Reflect, CEReactions] attribute boolean autoplay;
     [Reflect, CEReactions] attribute boolean loop;
     undefined pause();

+ 10 - 2
Userland/Libraries/LibWeb/HTML/HTMLVideoElement.cpp

@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 2020, the SerenityOS developers.
+ * Copyright (c) 2023, Tim Flynn <trflynn89@serenityos.org>
  *
  * SPDX-License-Identifier: BSD-2-Clause
  */
@@ -84,9 +85,10 @@ void HTMLVideoElement::set_video_track(JS::GCPtr<HTML::VideoTrack> video_track)
         m_video_timer->stop();
 
     m_video_track = video_track;
-    if (!m_video_track)
-        return;
+}
 
+void HTMLVideoElement::on_playing()
+{
     if (!m_video_timer) {
         m_video_timer = Platform::Timer::create_repeating(s_frame_delay_ms, [this]() {
             if (auto frame = m_video_track->next_frame())
@@ -101,4 +103,10 @@ void HTMLVideoElement::set_video_track(JS::GCPtr<HTML::VideoTrack> video_track)
     m_video_timer->start();
 }
 
+void HTMLVideoElement::on_paused()
+{
+    if (m_video_timer)
+        m_video_timer->stop();
+}
+
 }

+ 3 - 0
Userland/Libraries/LibWeb/HTML/HTMLVideoElement.h

@@ -38,6 +38,9 @@ private:
 
     virtual JS::GCPtr<Layout::Node> create_layout_node(NonnullRefPtr<CSS::StyleProperties>) override;
 
+    virtual void on_playing() override;
+    virtual void on_paused() override;
+
     JS::GCPtr<HTML::VideoTrack> m_video_track;
     RefPtr<Platform::Timer> m_video_timer;
     RefPtr<Gfx::Bitmap> m_current_frame;