Nullptr crash accessing Document in GenericEventQueue::dispatchOneEvent()
authorcdumez@apple.com <cdumez@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 30 Jul 2018 18:17:12 +0000 (18:17 +0000)
committercdumez@apple.com <cdumez@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 30 Jul 2018 18:17:12 +0000 (18:17 +0000)
https://bugs.webkit.org/show_bug.cgi?id=187284

Unreviewed, roll r233496 and r233571 now that all known issues have been fixed via
dependency bugs.

Source/WebCore:

Test: media/track/video-track-addition-and-frame-removal.html

* Modules/encryptedmedia/legacy/WebKitMediaKeySession.cpp:
(WebCore::WebKitMediaKeySession::suspend):
(WebCore::WebKitMediaKeySession::resume):
(WebCore::WebKitMediaKeySession::stop):
* Modules/encryptedmedia/legacy/WebKitMediaKeySession.h:
* Modules/mediasource/MediaSource.cpp:
(WebCore::MediaSource::removeSourceBuffer):
(WebCore::MediaSource::suspend):
(WebCore::MediaSource::resume):
* Modules/mediasource/MediaSource.h:
* Modules/mediasource/SourceBuffer.cpp:
(WebCore::SourceBuffer::suspend):
(WebCore::SourceBuffer::resume):
(WebCore::SourceBuffer::stop):
* Modules/mediasource/SourceBuffer.h:
* Modules/mediasource/SourceBufferList.cpp:
(WebCore::SourceBufferList::SourceBufferList):
(WebCore::SourceBufferList::canSuspendForDocumentSuspension const):
(WebCore::SourceBufferList::suspend):
(WebCore::SourceBufferList::resume):
(WebCore::SourceBufferList::stop):
(WebCore::SourceBufferList::activeDOMObjectName const):
* Modules/mediasource/SourceBufferList.h:
* Modules/mediasource/SourceBufferList.idl:
* dom/Document.h:
(WebCore::Document::hasBrowsingContext const):
* dom/GenericEventQueue.cpp:
(WebCore::GenericEventQueue::dispatchOneEvent):
* html/HTMLMediaElement.cpp:
(WebCore::HTMLMediaElement::prepareForLoad):
(WebCore::HTMLMediaElement::selectMediaResource):
(WebCore::HTMLMediaElement::prepareToPlay):
(WebCore::HTMLMediaElement::playInternal):
(WebCore::HTMLMediaElement::pauseInternal):
(WebCore::HTMLMediaElement::sourceWasAdded):
* html/track/AudioTrackList.cpp:
(AudioTrackList::activeDOMObjectName const):
* html/track/AudioTrackList.h:
* html/track/AudioTrackList.idl:
* html/track/TextTrackList.cpp:
(TextTrackList::activeDOMObjectName const):
* html/track/TextTrackList.h:
* html/track/TextTrackList.idl:
* html/track/TrackListBase.cpp:
(WebCore::TrackListBase::TrackListBase):
(WebCore::TrackListBase::canSuspendForDocumentSuspension const):
(WebCore::TrackListBase::suspend):
(WebCore::TrackListBase::resume):
(WebCore::TrackListBase::stop):
* html/track/TrackListBase.h:
* html/track/VideoTrackList.cpp:
(VideoTrackList::activeDOMObjectName const):
* html/track/VideoTrackList.h:
* html/track/VideoTrackList.idl:

LayoutTests:

* media/track/video-track-addition-and-frame-removal-expected.txt: Added.
* media/track/video-track-addition-and-frame-removal.html: Added.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@234374 268f45cc-cd09-0410-ab3c-d52691b4dbfc

27 files changed:
LayoutTests/ChangeLog
LayoutTests/media/track/video-track-addition-and-frame-removal-expected.txt [new file with mode: 0644]
LayoutTests/media/track/video-track-addition-and-frame-removal.html [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/Modules/encryptedmedia/legacy/WebKitMediaKeySession.cpp
Source/WebCore/Modules/encryptedmedia/legacy/WebKitMediaKeySession.h
Source/WebCore/Modules/mediasource/MediaSource.cpp
Source/WebCore/Modules/mediasource/MediaSource.h
Source/WebCore/Modules/mediasource/SourceBuffer.cpp
Source/WebCore/Modules/mediasource/SourceBuffer.h
Source/WebCore/Modules/mediasource/SourceBufferList.cpp
Source/WebCore/Modules/mediasource/SourceBufferList.h
Source/WebCore/Modules/mediasource/SourceBufferList.idl
Source/WebCore/dom/Document.h
Source/WebCore/dom/GenericEventQueue.cpp
Source/WebCore/html/HTMLMediaElement.cpp
Source/WebCore/html/track/AudioTrackList.cpp
Source/WebCore/html/track/AudioTrackList.h
Source/WebCore/html/track/AudioTrackList.idl
Source/WebCore/html/track/TextTrackList.cpp
Source/WebCore/html/track/TextTrackList.h
Source/WebCore/html/track/TextTrackList.idl
Source/WebCore/html/track/TrackListBase.cpp
Source/WebCore/html/track/TrackListBase.h
Source/WebCore/html/track/VideoTrackList.cpp
Source/WebCore/html/track/VideoTrackList.h
Source/WebCore/html/track/VideoTrackList.idl

index 1ecdd91..0567628 100644 (file)
@@ -1,3 +1,14 @@
+2018-07-30  Chris Dumez  <cdumez@apple.com>
+
+        Nullptr crash accessing Document in GenericEventQueue::dispatchOneEvent()
+        https://bugs.webkit.org/show_bug.cgi?id=187284
+
+        Unreviewed, roll r233496 and r233571 now that all known issues have been fixed via
+        dependency bugs.
+
+        * media/track/video-track-addition-and-frame-removal-expected.txt: Added.
+        * media/track/video-track-addition-and-frame-removal.html: Added.
+
 2018-07-30  Per Arne Vollan  <pvollan@apple.com>
 
         [Win] Layout test http/tests/security/canvas-remote-read-remote-video-hls.html is failing.
diff --git a/LayoutTests/media/track/video-track-addition-and-frame-removal-expected.txt b/LayoutTests/media/track/video-track-addition-and-frame-removal-expected.txt
new file mode 100644 (file)
index 0000000..f662545
--- /dev/null
@@ -0,0 +1,4 @@
+This tests removing a frame immediately after inserting a new track element.
+WebKit should not hit any assertions.
+
+
diff --git a/LayoutTests/media/track/video-track-addition-and-frame-removal.html b/LayoutTests/media/track/video-track-addition-and-frame-removal.html
new file mode 100644 (file)
index 0000000..ef35557
--- /dev/null
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html>
+<body>
+<p>This tests removing a frame immediately after inserting a new track element.<br>
+WebKit should not hit any assertions.</p>
+<script src=../../resources/gc.js></script>
+<script>
+
+if (window.testRunner)
+    testRunner.waitUntilDone();
+
+function startTest()
+{
+    const doc = frame.contentDocument;
+    const trackElement = doc.createElement('track');
+    doc.querySelector('video').appendChild(trackElement);
+
+    frame.remove();
+    gc();
+    setTimeout(() => {
+        if (window.testRunner)
+            testRunner.notifyDone();        
+    }, 100);
+}
+
+</script>
+<iframe id="frame" src="./video-track-add-remove.html"></iframe>
+</body>
+</html>
index 19d1329..59e07b4 100644 (file)
@@ -1,5 +1,70 @@
 2018-07-30  Chris Dumez  <cdumez@apple.com>
 
+        Nullptr crash accessing Document in GenericEventQueue::dispatchOneEvent()
+        https://bugs.webkit.org/show_bug.cgi?id=187284
+
+        Unreviewed, roll r233496 and r233571 now that all known issues have been fixed via
+        dependency bugs.
+
+        Test: media/track/video-track-addition-and-frame-removal.html
+
+        * Modules/encryptedmedia/legacy/WebKitMediaKeySession.cpp:
+        (WebCore::WebKitMediaKeySession::suspend):
+        (WebCore::WebKitMediaKeySession::resume):
+        (WebCore::WebKitMediaKeySession::stop):
+        * Modules/encryptedmedia/legacy/WebKitMediaKeySession.h:
+        * Modules/mediasource/MediaSource.cpp:
+        (WebCore::MediaSource::removeSourceBuffer):
+        (WebCore::MediaSource::suspend):
+        (WebCore::MediaSource::resume):
+        * Modules/mediasource/MediaSource.h:
+        * Modules/mediasource/SourceBuffer.cpp:
+        (WebCore::SourceBuffer::suspend):
+        (WebCore::SourceBuffer::resume):
+        (WebCore::SourceBuffer::stop):
+        * Modules/mediasource/SourceBuffer.h:
+        * Modules/mediasource/SourceBufferList.cpp:
+        (WebCore::SourceBufferList::SourceBufferList):
+        (WebCore::SourceBufferList::canSuspendForDocumentSuspension const):
+        (WebCore::SourceBufferList::suspend):
+        (WebCore::SourceBufferList::resume):
+        (WebCore::SourceBufferList::stop):
+        (WebCore::SourceBufferList::activeDOMObjectName const):
+        * Modules/mediasource/SourceBufferList.h:
+        * Modules/mediasource/SourceBufferList.idl:
+        * dom/Document.h:
+        (WebCore::Document::hasBrowsingContext const):
+        * dom/GenericEventQueue.cpp:
+        (WebCore::GenericEventQueue::dispatchOneEvent):
+        * html/HTMLMediaElement.cpp:
+        (WebCore::HTMLMediaElement::prepareForLoad):
+        (WebCore::HTMLMediaElement::selectMediaResource):
+        (WebCore::HTMLMediaElement::prepareToPlay):
+        (WebCore::HTMLMediaElement::playInternal):
+        (WebCore::HTMLMediaElement::pauseInternal):
+        (WebCore::HTMLMediaElement::sourceWasAdded):
+        * html/track/AudioTrackList.cpp:
+        (AudioTrackList::activeDOMObjectName const):
+        * html/track/AudioTrackList.h:
+        * html/track/AudioTrackList.idl:
+        * html/track/TextTrackList.cpp:
+        (TextTrackList::activeDOMObjectName const):
+        * html/track/TextTrackList.h:
+        * html/track/TextTrackList.idl:
+        * html/track/TrackListBase.cpp:
+        (WebCore::TrackListBase::TrackListBase):
+        (WebCore::TrackListBase::canSuspendForDocumentSuspension const):
+        (WebCore::TrackListBase::suspend):
+        (WebCore::TrackListBase::resume):
+        (WebCore::TrackListBase::stop):
+        * html/track/TrackListBase.h:
+        * html/track/VideoTrackList.cpp:
+        (VideoTrackList::activeDOMObjectName const):
+        * html/track/VideoTrackList.h:
+        * html/track/VideoTrackList.idl:
+
+2018-07-30  Chris Dumez  <cdumez@apple.com>
+
         Unreviewed internal build fix after r234347.
 
         * html/StepRange.cpp:
index 73f2d26..f0bf1e0 100644 (file)
@@ -239,8 +239,19 @@ bool WebKitMediaKeySession::hasPendingActivity() const
     return (m_keys && m_session) || m_asyncEventQueue.hasPendingEvents();
 }
 
+void WebKitMediaKeySession::suspend(ReasonForSuspension)
+{
+    ASSERT_NOT_REACHED();
+}
+
+void WebKitMediaKeySession::resume()
+{
+    ASSERT_NOT_REACHED();
+}
+
 void WebKitMediaKeySession::stop()
 {
+    m_asyncEventQueue.close();
     close();
 }
 
index 1a22d32..1d0dc7c 100644 (file)
@@ -76,6 +76,8 @@ private:
     void refEventTarget() final { ref(); }
     void derefEventTarget() final { deref(); }
 
+    void suspend(ReasonForSuspension) final;
+    void resume() final;
     void stop() final;
     bool canSuspendForDocumentSuspension() const final;
     const char* activeDOMObjectName() const final;
index e85c91b..9aee75d 100644 (file)
@@ -692,124 +692,127 @@ ExceptionOr<void> MediaSource::removeSourceBuffer(SourceBuffer& buffer)
     // 3. If the sourceBuffer.updating attribute equals true, then run the following steps: ...
     buffer.abortIfUpdating();
 
-    // 4. Let SourceBuffer audioTracks list equal the AudioTrackList object returned by sourceBuffer.audioTracks.
-    auto& audioTracks = buffer.audioTracks();
-
-    // 5. If the SourceBuffer audioTracks list is not empty, then run the following steps:
-    if (audioTracks.length()) {
-        // 5.1 Let HTMLMediaElement audioTracks list equal the AudioTrackList object returned by the audioTracks
-        // attribute on the HTMLMediaElement.
-        // 5.2 Let the removed enabled audio track flag equal false.
-        bool removedEnabledAudioTrack = false;
-
-        // 5.3 For each AudioTrack object in the SourceBuffer audioTracks list, run the following steps:
-        while (audioTracks.length()) {
-            auto& track = *audioTracks.lastItem();
-
-            // 5.3.1 Set the sourceBuffer attribute on the AudioTrack object to null.
-            track.setSourceBuffer(nullptr);
-
-            // 5.3.2 If the enabled attribute on the AudioTrack object is true, then set the removed enabled
-            // audio track flag to true.
-            if (track.enabled())
-                removedEnabledAudioTrack = true;
-
-            // 5.3.3 Remove the AudioTrack object from the HTMLMediaElement audioTracks list.
-            // 5.3.4 Queue a task to fire a trusted event named removetrack, that does not bubble and is not
-            // cancelable, and that uses the TrackEvent interface, at the HTMLMediaElement audioTracks list.
-            if (mediaElement())
-                mediaElement()->removeAudioTrack(track);
-
-            // 5.3.5 Remove the AudioTrack object from the SourceBuffer audioTracks list.
-            // 5.3.6 Queue a task to fire a trusted event named removetrack, that does not bubble and is not
-            // cancelable, and that uses the TrackEvent interface, at the SourceBuffer audioTracks list.
-            audioTracks.remove(track);
+    ASSERT(scriptExecutionContext());
+    if (!scriptExecutionContext()->activeDOMObjectsAreStopped()) {
+        // 4. Let SourceBuffer audioTracks list equal the AudioTrackList object returned by sourceBuffer.audioTracks.
+        auto& audioTracks = buffer.audioTracks();
+
+        // 5. If the SourceBuffer audioTracks list is not empty, then run the following steps:
+        if (audioTracks.length()) {
+            // 5.1 Let HTMLMediaElement audioTracks list equal the AudioTrackList object returned by the audioTracks
+            // attribute on the HTMLMediaElement.
+            // 5.2 Let the removed enabled audio track flag equal false.
+            bool removedEnabledAudioTrack = false;
+
+            // 5.3 For each AudioTrack object in the SourceBuffer audioTracks list, run the following steps:
+            while (audioTracks.length()) {
+                auto& track = *audioTracks.lastItem();
+
+                // 5.3.1 Set the sourceBuffer attribute on the AudioTrack object to null.
+                track.setSourceBuffer(nullptr);
+
+                // 5.3.2 If the enabled attribute on the AudioTrack object is true, then set the removed enabled
+                // audio track flag to true.
+                if (track.enabled())
+                    removedEnabledAudioTrack = true;
+
+                // 5.3.3 Remove the AudioTrack object from the HTMLMediaElement audioTracks list.
+                // 5.3.4 Queue a task to fire a trusted event named removetrack, that does not bubble and is not
+                // cancelable, and that uses the TrackEvent interface, at the HTMLMediaElement audioTracks list.
+                if (mediaElement())
+                    mediaElement()->removeAudioTrack(track);
+
+                // 5.3.5 Remove the AudioTrack object from the SourceBuffer audioTracks list.
+                // 5.3.6 Queue a task to fire a trusted event named removetrack, that does not bubble and is not
+                // cancelable, and that uses the TrackEvent interface, at the SourceBuffer audioTracks list.
+                audioTracks.remove(track);
+            }
+
+            // 5.4 If the removed enabled audio track flag equals true, then queue a task to fire a simple event
+            // named change at the HTMLMediaElement audioTracks list.
+            if (removedEnabledAudioTrack)
+                mediaElement()->ensureAudioTracks().scheduleChangeEvent();
         }
 
-        // 5.4 If the removed enabled audio track flag equals true, then queue a task to fire a simple event
-        // named change at the HTMLMediaElement audioTracks list.
-        if (removedEnabledAudioTrack)
-            mediaElement()->ensureAudioTracks().scheduleChangeEvent();
-    }
-
-    // 6. Let SourceBuffer videoTracks list equal the VideoTrackList object returned by sourceBuffer.videoTracks.
-    auto& videoTracks = buffer.videoTracks();
-
-    // 7. If the SourceBuffer videoTracks list is not empty, then run the following steps:
-    if (videoTracks.length()) {
-        // 7.1 Let HTMLMediaElement videoTracks list equal the VideoTrackList object returned by the videoTracks
-        // attribute on the HTMLMediaElement.
-        // 7.2 Let the removed selected video track flag equal false.
-        bool removedSelectedVideoTrack = false;
-
-        // 7.3 For each VideoTrack object in the SourceBuffer videoTracks list, run the following steps:
-        while (videoTracks.length()) {
-            auto& track = *videoTracks.lastItem();
-
-            // 7.3.1 Set the sourceBuffer attribute on the VideoTrack object to null.
-            track.setSourceBuffer(nullptr);
-
-            // 7.3.2 If the selected attribute on the VideoTrack object is true, then set the removed selected
-            // video track flag to true.
-            if (track.selected())
-                removedSelectedVideoTrack = true;
-
-            // 7.3.3 Remove the VideoTrack object from the HTMLMediaElement videoTracks list.
-            // 7.3.4 Queue a task to fire a trusted event named removetrack, that does not bubble and is not
-            // cancelable, and that uses the TrackEvent interface, at the HTMLMediaElement videoTracks list.
-            if (mediaElement())
-                mediaElement()->removeVideoTrack(track);
-
-            // 7.3.5 Remove the VideoTrack object from the SourceBuffer videoTracks list.
-            // 7.3.6 Queue a task to fire a trusted event named removetrack, that does not bubble and is not
-            // cancelable, and that uses the TrackEvent interface, at the SourceBuffer videoTracks list.
-            videoTracks.remove(track);
+        // 6. Let SourceBuffer videoTracks list equal the VideoTrackList object returned by sourceBuffer.videoTracks.
+        auto& videoTracks = buffer.videoTracks();
+
+        // 7. If the SourceBuffer videoTracks list is not empty, then run the following steps:
+        if (videoTracks.length()) {
+            // 7.1 Let HTMLMediaElement videoTracks list equal the VideoTrackList object returned by the videoTracks
+            // attribute on the HTMLMediaElement.
+            // 7.2 Let the removed selected video track flag equal false.
+            bool removedSelectedVideoTrack = false;
+
+            // 7.3 For each VideoTrack object in the SourceBuffer videoTracks list, run the following steps:
+            while (videoTracks.length()) {
+                auto& track = *videoTracks.lastItem();
+
+                // 7.3.1 Set the sourceBuffer attribute on the VideoTrack object to null.
+                track.setSourceBuffer(nullptr);
+
+                // 7.3.2 If the selected attribute on the VideoTrack object is true, then set the removed selected
+                // video track flag to true.
+                if (track.selected())
+                    removedSelectedVideoTrack = true;
+
+                // 7.3.3 Remove the VideoTrack object from the HTMLMediaElement videoTracks list.
+                // 7.3.4 Queue a task to fire a trusted event named removetrack, that does not bubble and is not
+                // cancelable, and that uses the TrackEvent interface, at the HTMLMediaElement videoTracks list.
+                if (mediaElement())
+                    mediaElement()->removeVideoTrack(track);
+
+                // 7.3.5 Remove the VideoTrack object from the SourceBuffer videoTracks list.
+                // 7.3.6 Queue a task to fire a trusted event named removetrack, that does not bubble and is not
+                // cancelable, and that uses the TrackEvent interface, at the SourceBuffer videoTracks list.
+                videoTracks.remove(track);
+            }
+
+            // 7.4 If the removed selected video track flag equals true, then queue a task to fire a simple event
+            // named change at the HTMLMediaElement videoTracks list.
+            if (removedSelectedVideoTrack)
+                mediaElement()->ensureVideoTracks().scheduleChangeEvent();
         }
 
-        // 7.4 If the removed selected video track flag equals true, then queue a task to fire a simple event
-        // named change at the HTMLMediaElement videoTracks list.
-        if (removedSelectedVideoTrack)
-            mediaElement()->ensureVideoTracks().scheduleChangeEvent();
-    }
-
-    // 8. Let SourceBuffer textTracks list equal the TextTrackList object returned by sourceBuffer.textTracks.
-    auto& textTracks = buffer.textTracks();
-
-    // 9. If the SourceBuffer textTracks list is not empty, then run the following steps:
-    if (textTracks.length()) {
-        // 9.1 Let HTMLMediaElement textTracks list equal the TextTrackList object returned by the textTracks
-        // attribute on the HTMLMediaElement.
-        // 9.2 Let the removed enabled text track flag equal false.
-        bool removedEnabledTextTrack = false;
-
-        // 9.3 For each TextTrack object in the SourceBuffer textTracks list, run the following steps:
-        while (textTracks.length()) {
-            auto& track = *textTracks.lastItem();
-
-            // 9.3.1 Set the sourceBuffer attribute on the TextTrack object to null.
-            track.setSourceBuffer(nullptr);
-
-            // 9.3.2 If the mode attribute on the TextTrack object is set to "showing" or "hidden", then
-            // set the removed enabled text track flag to true.
-            if (track.mode() == TextTrack::Mode::Showing || track.mode() == TextTrack::Mode::Hidden)
-                removedEnabledTextTrack = true;
-
-            // 9.3.3 Remove the TextTrack object from the HTMLMediaElement textTracks list.
-            // 9.3.4 Queue a task to fire a trusted event named removetrack, that does not bubble and is not
-            // cancelable, and that uses the TrackEvent interface, at the HTMLMediaElement textTracks list.
-            if (mediaElement())
-                mediaElement()->removeTextTrack(track);
-
-            // 9.3.5 Remove the TextTrack object from the SourceBuffer textTracks list.
-            // 9.3.6 Queue a task to fire a trusted event named removetrack, that does not bubble and is not
-            // cancelable, and that uses the TrackEvent interface, at the SourceBuffer textTracks list.
-            textTracks.remove(track);
+        // 8. Let SourceBuffer textTracks list equal the TextTrackList object returned by sourceBuffer.textTracks.
+        auto& textTracks = buffer.textTracks();
+
+        // 9. If the SourceBuffer textTracks list is not empty, then run the following steps:
+        if (textTracks.length()) {
+            // 9.1 Let HTMLMediaElement textTracks list equal the TextTrackList object returned by the textTracks
+            // attribute on the HTMLMediaElement.
+            // 9.2 Let the removed enabled text track flag equal false.
+            bool removedEnabledTextTrack = false;
+
+            // 9.3 For each TextTrack object in the SourceBuffer textTracks list, run the following steps:
+            while (textTracks.length()) {
+                auto& track = *textTracks.lastItem();
+
+                // 9.3.1 Set the sourceBuffer attribute on the TextTrack object to null.
+                track.setSourceBuffer(nullptr);
+
+                // 9.3.2 If the mode attribute on the TextTrack object is set to "showing" or "hidden", then
+                // set the removed enabled text track flag to true.
+                if (track.mode() == TextTrack::Mode::Showing || track.mode() == TextTrack::Mode::Hidden)
+                    removedEnabledTextTrack = true;
+
+                // 9.3.3 Remove the TextTrack object from the HTMLMediaElement textTracks list.
+                // 9.3.4 Queue a task to fire a trusted event named removetrack, that does not bubble and is not
+                // cancelable, and that uses the TrackEvent interface, at the HTMLMediaElement textTracks list.
+                if (mediaElement())
+                    mediaElement()->removeTextTrack(track);
+
+                // 9.3.5 Remove the TextTrack object from the SourceBuffer textTracks list.
+                // 9.3.6 Queue a task to fire a trusted event named removetrack, that does not bubble and is not
+                // cancelable, and that uses the TrackEvent interface, at the SourceBuffer textTracks list.
+                textTracks.remove(track);
+            }
+
+            // 9.4 If the removed enabled text track flag equals true, then queue a task to fire a simple event
+            // named change at the HTMLMediaElement textTracks list.
+            if (removedEnabledTextTrack)
+                mediaElement()->ensureTextTracks().scheduleChangeEvent();
         }
-
-        // 9.4 If the removed enabled text track flag equals true, then queue a task to fire a simple event
-        // named change at the HTMLMediaElement textTracks list.
-        if (removedEnabledTextTrack)
-            mediaElement()->ensureTextTracks().scheduleChangeEvent();
     }
 
     // 10. If sourceBuffer is in activeSourceBuffers, then remove sourceBuffer from activeSourceBuffers ...
@@ -932,6 +935,25 @@ bool MediaSource::hasPendingActivity() const
         || ActiveDOMObject::hasPendingActivity();
 }
 
+void MediaSource::suspend(ReasonForSuspension reason)
+{
+    switch (reason) {
+    case ReasonForSuspension::PageCache:
+    case ReasonForSuspension::PageWillBeSuspended:
+        m_asyncEventQueue.suspend();
+        break;
+    case ReasonForSuspension::JavaScriptDebuggerPaused:
+    case ReasonForSuspension::WillDeferLoading:
+        // Do nothing, we don't pause media playback in these cases.
+        break;
+    }
+}
+
+void MediaSource::resume()
+{
+    m_asyncEventQueue.resume();
+}
+
 void MediaSource::stop()
 {
     m_asyncEventQueue.close();
index 2934657..bd5cd7c 100644 (file)
@@ -107,6 +107,8 @@ public:
 private:
     explicit MediaSource(ScriptExecutionContext&);
 
+    void suspend(ReasonForSuspension) final;
+    void resume() final;
     void stop() final;
     bool canSuspendForDocumentSuspension() const final;
     const char* activeDOMObjectName() const final;
index 1fa3974..a5266ee 100644 (file)
@@ -458,8 +458,28 @@ bool SourceBuffer::hasPendingActivity() const
     return m_source || m_asyncEventQueue.hasPendingEvents();
 }
 
+void SourceBuffer::suspend(ReasonForSuspension reason)
+{
+    switch (reason) {
+    case ReasonForSuspension::PageCache:
+    case ReasonForSuspension::PageWillBeSuspended:
+        m_asyncEventQueue.suspend();
+        break;
+    case ReasonForSuspension::JavaScriptDebuggerPaused:
+    case ReasonForSuspension::WillDeferLoading:
+        // Do nothing, we don't pause media playback in these cases.
+        break;
+    }
+}
+
+void SourceBuffer::resume()
+{
+    m_asyncEventQueue.resume();
+}
+
 void SourceBuffer::stop()
 {
+    m_asyncEventQueue.close();
     m_appendBufferTimer.stop();
     m_removeTimer.stop();
 }
index c1a3dc5..9a30799 100644 (file)
@@ -123,6 +123,8 @@ private:
     void refEventTarget() final { ref(); }
     void derefEventTarget() final { deref(); }
 
+    void suspend(ReasonForSuspension) final;
+    void resume() final;
     void stop() final;
     const char* activeDOMObjectName() const final;
     bool canSuspendForDocumentSuspension() const final;
index 4e8f520..ec178ee 100644 (file)
 namespace WebCore {
 
 SourceBufferList::SourceBufferList(ScriptExecutionContext* context)
-    : ContextDestructionObserver(context)
+    : ActiveDOMObject(context)
     , m_asyncEventQueue(*this)
 {
+    suspendIfNeeded();
 }
 
 SourceBufferList::~SourceBufferList()
@@ -97,6 +98,39 @@ void SourceBufferList::scheduleEvent(const AtomicString& eventName)
     m_asyncEventQueue.enqueueEvent(WTFMove(event));
 }
 
+bool SourceBufferList::canSuspendForDocumentSuspension() const
+{
+    return !m_asyncEventQueue.hasPendingEvents();
+}
+
+void SourceBufferList::suspend(ReasonForSuspension reason)
+{
+    switch (reason) {
+    case ReasonForSuspension::PageCache:
+    case ReasonForSuspension::PageWillBeSuspended:
+        m_asyncEventQueue.suspend();
+        break;
+    case ReasonForSuspension::JavaScriptDebuggerPaused:
+    case ReasonForSuspension::WillDeferLoading:
+        // Do nothing, we don't pause media playback in these cases.
+        break;
+    }
+}
+
+void SourceBufferList::resume()
+{
+    m_asyncEventQueue.resume();
+}
+
+void SourceBufferList::stop()
+{
+    m_asyncEventQueue.close();
+}
+
+const char* SourceBufferList::activeDOMObjectName() const
+{
+    return "SourceBufferList";
+}
 
 } // namespace WebCore
 
index 4186b4f..073198d 100644 (file)
@@ -32,7 +32,7 @@
 
 #if ENABLE(MEDIA_SOURCE)
 
-#include "ContextDestructionObserver.h"
+#include "ActiveDOMObject.h"
 #include "EventTarget.h"
 #include "GenericEventQueue.h"
 #include <wtf/RefCounted.h>
@@ -42,7 +42,7 @@ namespace WebCore {
 
 class SourceBuffer;
 
-class SourceBufferList final : public RefCounted<SourceBufferList>, public EventTargetWithInlineData, public ContextDestructionObserver {
+class SourceBufferList final : public RefCounted<SourceBufferList>, public EventTargetWithInlineData, public ActiveDOMObject {
 public:
     static Ref<SourceBufferList> create(ScriptExecutionContext* context)
     {
@@ -77,6 +77,12 @@ private:
     void refEventTarget() override { ref(); }
     void derefEventTarget() override { deref(); }
 
+    bool canSuspendForDocumentSuspension() const final;
+    void suspend(ReasonForSuspension) final;
+    void resume() final;
+    void stop() final;
+    const char* activeDOMObjectName() const final;
+
     GenericEventQueue m_asyncEventQueue;
 
     Vector<RefPtr<SourceBuffer>> m_list;
index 6c2d564..99c185a 100644 (file)
@@ -29,6 +29,7 @@
  */
  
 [
+    ActiveDOMObject,
     Conditional=MEDIA_SOURCE,
     GenerateIsReachable=Impl,
 ] interface SourceBufferList : EventTarget {
index a8d3722..83b0889 100644 (file)
@@ -813,6 +813,8 @@ public:
     // In DOM Level 2, the Document's DOMWindow is called the defaultView.
     WEBCORE_EXPORT WindowProxy* windowProxy() const;
 
+    bool hasBrowsingContext() const { return !!frame(); }
+
     Document& contextDocument() const;
     void setContextDocument(Document& document) { m_contextDocument = makeWeakPtr(document); }
 
index 5a32483..8cfa8f0 100644 (file)
 #include "config.h"
 #include "GenericEventQueue.h"
 
+#include "Document.h"
 #include "Event.h"
 #include "EventTarget.h"
+#include "Node.h"
 #include "ScriptExecutionContext.h"
 #include "Timer.h"
 #include <wtf/MainThread.h>
@@ -65,6 +67,9 @@ void GenericEventQueue::dispatchOneEvent()
     Ref<EventTarget> protect(m_owner);
     RefPtr<Event> event = m_pendingEvents.takeFirst();
     EventTarget& target = event->target() ? *event->target() : m_owner;
+    ASSERT_WITH_MESSAGE(!target.scriptExecutionContext()->activeDOMObjectsAreStopped(),
+        "An attempt to dispatch an event on a stopped target by EventTargetInterface=%d (nodeName=%s target=%p owner=%p)",
+        m_owner.eventTargetInterface(), m_owner.isNode() ? static_cast<Node&>(m_owner).nodeName().ascii().data() : "", &target, &m_owner);
     target.dispatchEvent(*event);
 }
 
index e2f9a37..8fbcb82 100644 (file)
@@ -1299,6 +1299,9 @@ void HTMLMediaElement::prepareForLoad()
     m_loadState = WaitingForSource;
     m_currentSourceNode = nullptr;
 
+    if (!document().hasBrowsingContext())
+        return;
+
     createMediaPlayer();
 
     // 2 - Let pending tasks be a list of all tasks from the media element's media element event task source in one of the task queues.
@@ -1416,7 +1419,6 @@ void HTMLMediaElement::selectMediaResource()
     // put into the background.
     m_mediaSession->removeBehaviorRestriction(MediaElementSession::RequirePageConsentToLoadMedia);
 
-
     m_resourceSelectionTaskQueue.enqueueTask([this]  {
         // 5. If the media element’s blocked-on-parser flag is false, then populate the list of pending text tracks.
 #if ENABLE(VIDEO_TRACK)
@@ -2893,7 +2895,7 @@ bool HTMLMediaElement::supportsScanning() const
 void HTMLMediaElement::prepareToPlay()
 {
     INFO_LOG(LOGIDENTIFIER);
-    if (m_havePreparedToPlay)
+    if (m_havePreparedToPlay || !document().hasBrowsingContext())
         return;
     m_havePreparedToPlay = true;
     if (m_player)
@@ -3488,6 +3490,11 @@ void HTMLMediaElement::playInternal()
         return;
     }
 
+    if (!document().hasBrowsingContext()) {
+        INFO_LOG(LOGIDENTIFIER, "  returning because there is no browsing context");
+        return;
+    }
+
     if (!m_mediaSession->clientWillBeginPlayback()) {
         ALWAYS_LOG(LOGIDENTIFIER, "  returning because of interruption");
         return;
@@ -3579,6 +3586,11 @@ void HTMLMediaElement::pauseInternal()
         return;
     }
 
+    if (!document().hasBrowsingContext()) {
+        INFO_LOG(LOGIDENTIFIER, "  returning because there is no browsing context");
+        return;
+    }
+
     if (!m_mediaSession->clientWillPausePlayback()) {
         ALWAYS_LOG(LOGIDENTIFIER, "  returning because of interruption");
         return;
@@ -4641,6 +4653,11 @@ void HTMLMediaElement::sourceWasAdded(HTMLSourceElement& source)
         INFO_LOG(LOGIDENTIFIER, "'src' is ", url);
     }
 
+    if (!document().hasBrowsingContext()) {
+        INFO_LOG(LOGIDENTIFIER, "<source> inserted inside a document without a browsing context is not loaded");
+        return;
+    }
+
     // We should only consider a <source> element when there is not src attribute at all.
     if (hasAttributeWithoutSynchronization(srcAttr))
         return;
index 3b07f05..f8b5d0a 100644 (file)
@@ -81,4 +81,9 @@ EventTargetInterface AudioTrackList::eventTargetInterface() const
     return AudioTrackListEventTargetInterfaceType;
 }
 
+const char* AudioTrackList::activeDOMObjectName() const
+{
+    return "AudioTrackList";
+}
+
 #endif
index 0c637dd..c490020 100644 (file)
@@ -52,6 +52,7 @@ public:
 
 private:
     AudioTrackList(HTMLMediaElement*, ScriptExecutionContext*);
+    const char* activeDOMObjectName() const final;
 };
 
 } // namespace WebCore
index cde73f4..7781c09 100644 (file)
@@ -24,6 +24,7 @@
  */
 
 [
+    ActiveDOMObject,
     Conditional=VIDEO_TRACK,
     GenerateIsReachable=ImplElementRoot,
     JSCustomMarkFunction,
index aa964f2..9b691f5 100644 (file)
@@ -265,4 +265,9 @@ EventTargetInterface TextTrackList::eventTargetInterface() const
     return TextTrackListEventTargetInterfaceType;
 }
 
+const char* TextTrackList::activeDOMObjectName() const
+{
+    return "TextTrackList";
+}
+
 #endif
index c42f1e1..5204291 100644 (file)
@@ -62,6 +62,7 @@ private:
     TextTrackList(HTMLMediaElement*, ScriptExecutionContext*);
 
     void invalidateTrackIndexesAfterTrack(TextTrack&);
+    const char* activeDOMObjectName() const final;
 
     Vector<RefPtr<TrackBase>> m_addTrackTracks;
     Vector<RefPtr<TrackBase>> m_elementTracks;
index fedd73b..35820a5 100644 (file)
@@ -24,6 +24,7 @@
  */
 
 [
+    ActiveDOMObject,
     Conditional=VIDEO_TRACK,
     GenerateIsReachable=ImplElementRoot,
     JSCustomMarkFunction,
index 2e96942..147700f 100644 (file)
 #include "ScriptExecutionContext.h"
 #include "TrackEvent.h"
 
-using namespace WebCore;
+namespace WebCore {
 
 TrackListBase::TrackListBase(HTMLMediaElement* element, ScriptExecutionContext* context)
-    : ContextDestructionObserver(context)
+    : ActiveDOMObject(context)
     , m_element(element)
     , m_asyncEventQueue(*this)
 {
-    ASSERT(is<Document>(context));
+    ASSERT(!context || is<Document>(context));
+    suspendIfNeeded();
 }
 
 TrackListBase::~TrackListBase()
@@ -174,4 +175,35 @@ bool TrackListBase::isAnyTrackEnabled() const
     return false;
 }
 
+bool TrackListBase::canSuspendForDocumentSuspension() const
+{
+    return !m_asyncEventQueue.hasPendingEvents();
+}
+
+void TrackListBase::suspend(ReasonForSuspension reason)
+{
+    switch (reason) {
+    case ReasonForSuspension::PageCache:
+    case ReasonForSuspension::PageWillBeSuspended:
+        m_asyncEventQueue.suspend();
+        break;
+    case ReasonForSuspension::JavaScriptDebuggerPaused:
+    case ReasonForSuspension::WillDeferLoading:
+        // Do nothing, we don't pause media playback in these cases.
+        break;
+    }
+}
+
+void TrackListBase::resume()
+{
+    m_asyncEventQueue.resume();
+}
+
+void TrackListBase::stop()
+{
+    m_asyncEventQueue.close();
+}
+
+} // namespace WebCore
+
 #endif
index f3d644d..fdae3b0 100644 (file)
@@ -27,7 +27,7 @@
 
 #if ENABLE(VIDEO_TRACK)
 
-#include "ContextDestructionObserver.h"
+#include "ActiveDOMObject.h"
 #include "EventTarget.h"
 #include "GenericEventQueue.h"
 #include <wtf/RefCounted.h>
@@ -39,7 +39,7 @@ class HTMLMediaElement;
 class Element;
 class TrackBase;
 
-class TrackListBase : public RefCounted<TrackListBase>, public EventTargetWithInlineData, public ContextDestructionObserver {
+class TrackListBase : public RefCounted<TrackListBase>, public EventTargetWithInlineData, public ActiveDOMObject {
 public:
     virtual ~TrackListBase();
 
@@ -74,6 +74,11 @@ protected:
 private:
     void scheduleTrackEvent(const AtomicString& eventName, Ref<TrackBase>&&);
 
+    bool canSuspendForDocumentSuspension() const final;
+    void suspend(ReasonForSuspension) final;
+    void resume() final;
+    void stop() final;
+
     // EventTarget
     void refEventTarget() final { ref(); }
     void derefEventTarget() final { deref(); }
index 3c5b5f1..a1549ab 100644 (file)
@@ -94,4 +94,9 @@ EventTargetInterface VideoTrackList::eventTargetInterface() const
     return VideoTrackListEventTargetInterfaceType;
 }
 
+const char* VideoTrackList::activeDOMObjectName() const
+{
+    return "VideoTrackList";
+}
+
 #endif
index 6b5be25..cc250fc 100644 (file)
@@ -53,6 +53,7 @@ public:
 
 private:
     VideoTrackList(HTMLMediaElement*, ScriptExecutionContext*);
+    const char* activeDOMObjectName() const final;
 };
 
 } // namespace WebCore
index 9c43fcd..18f40ed 100644 (file)
@@ -24,6 +24,7 @@
  */
 
 [
+    ActiveDOMObject,
     Conditional=VIDEO_TRACK,
     GenerateIsReachable=ImplElementRoot,
     JSCustomMarkFunction,