Title: [233496] trunk
Revision
233496
Author
[email protected]
Date
2018-07-03 20:31:29 -0700 (Tue, 03 Jul 2018)

Log Message

Nullptr crash accessing Document in GenericEventQueue::dispatchOneEvent()
https://bugs.webkit.org/show_bug.cgi?id=187284

Reviewed by Eric Carlson.

Source/WebCore:

The null pointer crash was caused by some GenericEventQueue dispatching an event in a stopped document,
which does not have a valid script execution context because some uses of GenericEventQueue in media code
was not closing the queue upon stopping of all active DOM objects.

Fixed all uses of GenericEventQueue which did not suspend or stop the queue with active DOM objects.
Made SourceBufferList and TrackListBase (along with AudioTrackList, TextTrackList, and VideoTrackList)
inherit from ActiveDOMObject instead of ContextDestructionObserver to do this.

Also fixed a bug that media elements inside a template element (and other cases where the document doesn't
have a browsing context) were scheduling events since this would hit the newly added debug assertion in
GenericEventQueue::dispatchOneEvent.

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

* Modules/encryptedmedia/legacy/WebKitMediaKeySession.cpp:
(WebCore::WebKitMediaKeySession::suspend): Assert that we never try to suspend when the document has
this object alive since canSuspendForDocumentSuspension always returns false.
(WebCore::WebKitMediaKeySession::resume): Ditto.
(WebCore::WebKitMediaKeySession::stop): Stop the event queue to avoid the crash.
* Modules/encryptedmedia/legacy/WebKitMediaKeySession.h:
* Modules/mediasource/MediaSource.cpp:
(WebCore::MediaSource::removeSourceBuffer): Don't do any work to update tracks when the active DOM
objects are stopped since this MediaSource and the related media objects are about to be destructed.
(WebCore::MediaSource::suspend): Assert that m_asyncEventQueue is empty as canSuspendForDocumentSuspension
returns false whenever the queue is not empty.
(WebCore::MediaSource::resume): Ditto.
* Modules/mediasource/MediaSource.h:
* Modules/mediasource/SourceBuffer.cpp:
(WebCore::SourceBuffer::suspend): Ditto.
(WebCore::SourceBuffer::resume): Ditto.
(WebCore::SourceBuffer::stop): Stop the event queue to avoid the crash.
* Modules/mediasource/SourceBuffer.h:
* Modules/mediasource/SourceBufferList.cpp:
(WebCore::SourceBufferList): Made this an active DOM object.
(WebCore::SourceBufferList::SourceBufferList):
(WebCore::SourceBufferList::canSuspendForDocumentSuspension const): Added. Return false when there are
pending events to match other media code.
(WebCore::SourceBufferList::suspend): Added. Assert that the event queue is empty here.
(WebCore::SourceBufferList::resume): Ditto.
(WebCore::SourceBufferList::stop): Added. Stop the event queue to avoid the crash.
(WebCore::SourceBufferList::activeDOMObjectName const): Added.
* Modules/mediasource/SourceBufferList.h:
(WebCore::SourceBufferList): Made this an active DOM object.
* Modules/mediasource/SourceBufferList.idl:
* dom/Document.h:
(WebCore::Document::hasBrowsingContext const): Added.
* dom/GenericEventQueue.cpp:
(WebCore::GenericEventQueue::dispatchOneEvent): Added an assertion to catch when an event is dispatched
inside a stopped document, which is never correct and causes this crash down the line.
* html/HTMLMediaElement.cpp:
(WebCore::HTMLMediaElement::playInternal): Exit early when the document doesn't have a browsing context;
e.g. when the media element is inside a template element.
(WebCore::HTMLMediaElement::pauseInternal): Ditto.
(WebCore::HTMLMediaElement::sourceWasAdded): Ditto.
* html/track/AudioTrackList.cpp:
(AudioTrackList::activeDOMObjectName const): Added.
* html/track/AudioTrackList.h:
* html/track/AudioTrackList.idl:
* html/track/TextTrackList.cpp::
(TextTrackList::activeDOMObjectName const): Added.
* html/track/TextTrackList.h:
* html/track/TextTrackList.idl:
* html/track/TrackListBase.cpp:
(WebCore::TrackListBase): Made this an active DOM object.
(WebCore::TrackListBase::TrackListBase): 
(WebCore::TrackListBase::canSuspendForDocumentSuspension const): Added. Return false when there are pending events
to match other media code.
(WebCore::TrackListBase::suspend): Added. Assert that the event queue is empty here.
(WebCore::TrackListBase::resume): Ditto.
(WebCore::TrackListBase::stop): Added. Stop the event queue to avoid the crash.
* html/track/TrackListBase.h:
* html/track/VideoTrackList.cpp:
(VideoTrackList::activeDOMObjectName const): Added.
* html/track/VideoTrackList.h:
* html/track/VideoTrackList.idl:

LayoutTests:

Added a regression test which reliably hits the newly added debug assertion.

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

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (233495 => 233496)


--- trunk/LayoutTests/ChangeLog	2018-07-04 03:18:55 UTC (rev 233495)
+++ trunk/LayoutTests/ChangeLog	2018-07-04 03:31:29 UTC (rev 233496)
@@ -1,3 +1,15 @@
+2018-07-03  Ryosuke Niwa  <[email protected]>
+
+        Nullptr crash accessing Document in GenericEventQueue::dispatchOneEvent()
+        https://bugs.webkit.org/show_bug.cgi?id=187284
+
+        Reviewed by Eric Carlson.
+
+        Added a regression test which reliably hits the newly added debug assertion.
+
+        * media/track/video-track-addition-and-frame-removal-expected.txt: Added.
+        * media/track/video-track-addition-and-frame-removal.html: Added.
+
 2018-07-03  Fujii Hironori  <[email protected]>
 
         [cairo] Doesn't paint box-shadow with zero blur-radius

Added: trunk/LayoutTests/media/track/video-track-addition-and-frame-removal-expected.txt (0 => 233496)


--- trunk/LayoutTests/media/track/video-track-addition-and-frame-removal-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/media/track/video-track-addition-and-frame-removal-expected.txt	2018-07-04 03:31:29 UTC (rev 233496)
@@ -0,0 +1,4 @@
+This tests removing a frame immediately after inserting a new track element.
+WebKit should not hit any assertions.
+
+

Added: trunk/LayoutTests/media/track/video-track-addition-and-frame-removal.html (0 => 233496)


--- trunk/LayoutTests/media/track/video-track-addition-and-frame-removal.html	                        (rev 0)
+++ trunk/LayoutTests/media/track/video-track-addition-and-frame-removal.html	2018-07-04 03:31:29 UTC (rev 233496)
@@ -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=""
+<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=""
+</body>
+</html>

Modified: trunk/Source/WebCore/ChangeLog (233495 => 233496)


--- trunk/Source/WebCore/ChangeLog	2018-07-04 03:18:55 UTC (rev 233495)
+++ trunk/Source/WebCore/ChangeLog	2018-07-04 03:31:29 UTC (rev 233496)
@@ -1,3 +1,86 @@
+2018-07-03  Ryosuke Niwa  <[email protected]>
+
+        Nullptr crash accessing Document in GenericEventQueue::dispatchOneEvent()
+        https://bugs.webkit.org/show_bug.cgi?id=187284
+
+        Reviewed by Eric Carlson.
+
+        The null pointer crash was caused by some GenericEventQueue dispatching an event in a stopped document,
+        which does not have a valid script execution context because some uses of GenericEventQueue in media code
+        was not closing the queue upon stopping of all active DOM objects.
+
+        Fixed all uses of GenericEventQueue which did not suspend or stop the queue with active DOM objects.
+        Made SourceBufferList and TrackListBase (along with AudioTrackList, TextTrackList, and VideoTrackList)
+        inherit from ActiveDOMObject instead of ContextDestructionObserver to do this.
+
+        Also fixed a bug that media elements inside a template element (and other cases where the document doesn't
+        have a browsing context) were scheduling events since this would hit the newly added debug assertion in
+        GenericEventQueue::dispatchOneEvent.
+
+        Test: media/track/video-track-addition-and-frame-removal.html
+
+        * Modules/encryptedmedia/legacy/WebKitMediaKeySession.cpp:
+        (WebCore::WebKitMediaKeySession::suspend): Assert that we never try to suspend when the document has
+        this object alive since canSuspendForDocumentSuspension always returns false.
+        (WebCore::WebKitMediaKeySession::resume): Ditto.
+        (WebCore::WebKitMediaKeySession::stop): Stop the event queue to avoid the crash.
+        * Modules/encryptedmedia/legacy/WebKitMediaKeySession.h:
+        * Modules/mediasource/MediaSource.cpp:
+        (WebCore::MediaSource::removeSourceBuffer): Don't do any work to update tracks when the active DOM
+        objects are stopped since this MediaSource and the related media objects are about to be destructed.
+        (WebCore::MediaSource::suspend): Assert that m_asyncEventQueue is empty as canSuspendForDocumentSuspension
+        returns false whenever the queue is not empty.
+        (WebCore::MediaSource::resume): Ditto.
+        * Modules/mediasource/MediaSource.h:
+        * Modules/mediasource/SourceBuffer.cpp:
+        (WebCore::SourceBuffer::suspend): Ditto.
+        (WebCore::SourceBuffer::resume): Ditto.
+        (WebCore::SourceBuffer::stop): Stop the event queue to avoid the crash.
+        * Modules/mediasource/SourceBuffer.h:
+        * Modules/mediasource/SourceBufferList.cpp:
+        (WebCore::SourceBufferList): Made this an active DOM object.
+        (WebCore::SourceBufferList::SourceBufferList):
+        (WebCore::SourceBufferList::canSuspendForDocumentSuspension const): Added. Return false when there are
+        pending events to match other media code.
+        (WebCore::SourceBufferList::suspend): Added. Assert that the event queue is empty here.
+        (WebCore::SourceBufferList::resume): Ditto.
+        (WebCore::SourceBufferList::stop): Added. Stop the event queue to avoid the crash.
+        (WebCore::SourceBufferList::activeDOMObjectName const): Added.
+        * Modules/mediasource/SourceBufferList.h:
+        (WebCore::SourceBufferList): Made this an active DOM object.
+        * Modules/mediasource/SourceBufferList.idl:
+        * dom/Document.h:
+        (WebCore::Document::hasBrowsingContext const): Added.
+        * dom/GenericEventQueue.cpp:
+        (WebCore::GenericEventQueue::dispatchOneEvent): Added an assertion to catch when an event is dispatched
+        inside a stopped document, which is never correct and causes this crash down the line.
+        * html/HTMLMediaElement.cpp:
+        (WebCore::HTMLMediaElement::playInternal): Exit early when the document doesn't have a browsing context;
+        e.g. when the media element is inside a template element.
+        (WebCore::HTMLMediaElement::pauseInternal): Ditto.
+        (WebCore::HTMLMediaElement::sourceWasAdded): Ditto.
+        * html/track/AudioTrackList.cpp:
+        (AudioTrackList::activeDOMObjectName const): Added.
+        * html/track/AudioTrackList.h:
+        * html/track/AudioTrackList.idl:
+        * html/track/TextTrackList.cpp::
+        (TextTrackList::activeDOMObjectName const): Added.
+        * html/track/TextTrackList.h:
+        * html/track/TextTrackList.idl:
+        * html/track/TrackListBase.cpp:
+        (WebCore::TrackListBase): Made this an active DOM object.
+        (WebCore::TrackListBase::TrackListBase): 
+        (WebCore::TrackListBase::canSuspendForDocumentSuspension const): Added. Return false when there are pending events
+        to match other media code.
+        (WebCore::TrackListBase::suspend): Added. Assert that the event queue is empty here.
+        (WebCore::TrackListBase::resume): Ditto.
+        (WebCore::TrackListBase::stop): Added. Stop the event queue to avoid the crash.
+        * html/track/TrackListBase.h:
+        * html/track/VideoTrackList.cpp:
+        (VideoTrackList::activeDOMObjectName const): Added.
+        * html/track/VideoTrackList.h:
+        * html/track/VideoTrackList.idl:
+
 2018-07-03  Fujii Hironori  <[email protected]>
 
         [cairo] Doesn't paint box-shadow with zero blur-radius

Modified: trunk/Source/WebCore/Modules/encryptedmedia/legacy/WebKitMediaKeySession.cpp (233495 => 233496)


--- trunk/Source/WebCore/Modules/encryptedmedia/legacy/WebKitMediaKeySession.cpp	2018-07-04 03:18:55 UTC (rev 233495)
+++ trunk/Source/WebCore/Modules/encryptedmedia/legacy/WebKitMediaKeySession.cpp	2018-07-04 03:31:29 UTC (rev 233496)
@@ -239,8 +239,19 @@
     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();
 }
 

Modified: trunk/Source/WebCore/Modules/encryptedmedia/legacy/WebKitMediaKeySession.h (233495 => 233496)


--- trunk/Source/WebCore/Modules/encryptedmedia/legacy/WebKitMediaKeySession.h	2018-07-04 03:18:55 UTC (rev 233495)
+++ trunk/Source/WebCore/Modules/encryptedmedia/legacy/WebKitMediaKeySession.h	2018-07-04 03:31:29 UTC (rev 233496)
@@ -76,6 +76,8 @@
     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;

Modified: trunk/Source/WebCore/Modules/mediasource/MediaSource.cpp (233495 => 233496)


--- trunk/Source/WebCore/Modules/mediasource/MediaSource.cpp	2018-07-04 03:18:55 UTC (rev 233495)
+++ trunk/Source/WebCore/Modules/mediasource/MediaSource.cpp	2018-07-04 03:31:29 UTC (rev 233496)
@@ -692,124 +692,127 @@
     // 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();
+    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. 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 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.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.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.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.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()->audioTracks().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()->audioTracks().scheduleChangeEvent();
-    }
+        // 6. Let SourceBuffer videoTracks list equal the VideoTrackList object returned by sourceBuffer.videoTracks.
+        auto& videoTracks = buffer.videoTracks();
 
-    // 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. 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 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.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.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.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.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()->videoTracks().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()->videoTracks().scheduleChangeEvent();
-    }
+        // 8. Let SourceBuffer textTracks list equal the TextTrackList object returned by sourceBuffer.textTracks.
+        auto& textTracks = buffer.textTracks();
 
-    // 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. 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 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.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.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.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.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()->textTracks().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()->textTracks().scheduleChangeEvent();
     }
 
     // 10. If sourceBuffer is in activeSourceBuffers, then remove sourceBuffer from activeSourceBuffers ...
@@ -932,6 +935,16 @@
         || ActiveDOMObject::hasPendingActivity();
 }
 
+void MediaSource::suspend(ReasonForSuspension)
+{
+    ASSERT(!m_asyncEventQueue.hasPendingEvents());
+}
+
+void MediaSource::resume()
+{
+    ASSERT(!m_asyncEventQueue.hasPendingEvents());
+}
+
 void MediaSource::stop()
 {
     m_asyncEventQueue.close();

Modified: trunk/Source/WebCore/Modules/mediasource/MediaSource.h (233495 => 233496)


--- trunk/Source/WebCore/Modules/mediasource/MediaSource.h	2018-07-04 03:18:55 UTC (rev 233495)
+++ trunk/Source/WebCore/Modules/mediasource/MediaSource.h	2018-07-04 03:31:29 UTC (rev 233496)
@@ -107,6 +107,8 @@
 private:
     explicit MediaSource(ScriptExecutionContext&);
 
+    void suspend(ReasonForSuspension) final;
+    void resume() final;
     void stop() final;
     bool canSuspendForDocumentSuspension() const final;
     const char* activeDOMObjectName() const final;

Modified: trunk/Source/WebCore/Modules/mediasource/SourceBuffer.cpp (233495 => 233496)


--- trunk/Source/WebCore/Modules/mediasource/SourceBuffer.cpp	2018-07-04 03:18:55 UTC (rev 233495)
+++ trunk/Source/WebCore/Modules/mediasource/SourceBuffer.cpp	2018-07-04 03:31:29 UTC (rev 233496)
@@ -458,8 +458,19 @@
     return m_source || m_asyncEventQueue.hasPendingEvents();
 }
 
+void SourceBuffer::suspend(ReasonForSuspension)
+{
+    ASSERT(!hasPendingActivity());
+}
+
+void SourceBuffer::resume()
+{
+    ASSERT(!hasPendingActivity());
+}
+
 void SourceBuffer::stop()
 {
+    m_asyncEventQueue.close();
     m_appendBufferTimer.stop();
     m_removeTimer.stop();
 }

Modified: trunk/Source/WebCore/Modules/mediasource/SourceBuffer.h (233495 => 233496)


--- trunk/Source/WebCore/Modules/mediasource/SourceBuffer.h	2018-07-04 03:18:55 UTC (rev 233495)
+++ trunk/Source/WebCore/Modules/mediasource/SourceBuffer.h	2018-07-04 03:31:29 UTC (rev 233496)
@@ -123,6 +123,8 @@
     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;

Modified: trunk/Source/WebCore/Modules/mediasource/SourceBufferList.cpp (233495 => 233496)


--- trunk/Source/WebCore/Modules/mediasource/SourceBufferList.cpp	2018-07-04 03:18:55 UTC (rev 233495)
+++ trunk/Source/WebCore/Modules/mediasource/SourceBufferList.cpp	2018-07-04 03:31:29 UTC (rev 233496)
@@ -40,9 +40,10 @@
 namespace WebCore {
 
 SourceBufferList::SourceBufferList(ScriptExecutionContext* context)
-    : ContextDestructionObserver(context)
+    : ActiveDOMObject(context)
     , m_asyncEventQueue(*this)
 {
+    suspendIfNeeded();
 }
 
 SourceBufferList::~SourceBufferList()
@@ -97,7 +98,31 @@
     m_asyncEventQueue.enqueueEvent(WTFMove(event));
 }
 
+bool SourceBufferList::canSuspendForDocumentSuspension() const
+{
+    return !m_asyncEventQueue.hasPendingEvents();
+}
 
+void SourceBufferList::suspend(ReasonForSuspension)
+{
+    ASSERT(!m_asyncEventQueue.hasPendingEvents());
+}
+
+void SourceBufferList::resume()
+{
+    ASSERT(!m_asyncEventQueue.hasPendingEvents());
+}
+
+void SourceBufferList::stop()
+{
+    m_asyncEventQueue.close();
+}
+
+const char* SourceBufferList::activeDOMObjectName() const
+{
+    return "SourceBufferList";
+}
+
 } // namespace WebCore
 
 #endif

Modified: trunk/Source/WebCore/Modules/mediasource/SourceBufferList.h (233495 => 233496)


--- trunk/Source/WebCore/Modules/mediasource/SourceBufferList.h	2018-07-04 03:18:55 UTC (rev 233495)
+++ trunk/Source/WebCore/Modules/mediasource/SourceBufferList.h	2018-07-04 03:31:29 UTC (rev 233496)
@@ -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 @@
 
 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 @@
     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;

Modified: trunk/Source/WebCore/Modules/mediasource/SourceBufferList.idl (233495 => 233496)


--- trunk/Source/WebCore/Modules/mediasource/SourceBufferList.idl	2018-07-04 03:18:55 UTC (rev 233495)
+++ trunk/Source/WebCore/Modules/mediasource/SourceBufferList.idl	2018-07-04 03:31:29 UTC (rev 233496)
@@ -29,6 +29,7 @@
  */
  
 [
+    ActiveDOMObject,
     Conditional=MEDIA_SOURCE,
     GenerateIsReachable=Impl,
 ] interface SourceBufferList : EventTarget {

Modified: trunk/Source/WebCore/dom/Document.h (233495 => 233496)


--- trunk/Source/WebCore/dom/Document.h	2018-07-04 03:18:55 UTC (rev 233495)
+++ trunk/Source/WebCore/dom/Document.h	2018-07-04 03:31:29 UTC (rev 233496)
@@ -803,6 +803,8 @@
     // 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); }
 

Modified: trunk/Source/WebCore/dom/GenericEventQueue.cpp (233495 => 233496)


--- trunk/Source/WebCore/dom/GenericEventQueue.cpp	2018-07-04 03:18:55 UTC (rev 233495)
+++ trunk/Source/WebCore/dom/GenericEventQueue.cpp	2018-07-04 03:31:29 UTC (rev 233496)
@@ -26,8 +26,10 @@
 #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 @@
     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);
 }
 

Modified: trunk/Source/WebCore/html/HTMLMediaElement.cpp (233495 => 233496)


--- trunk/Source/WebCore/html/HTMLMediaElement.cpp	2018-07-04 03:18:55 UTC (rev 233495)
+++ trunk/Source/WebCore/html/HTMLMediaElement.cpp	2018-07-04 03:31:29 UTC (rev 233496)
@@ -1299,6 +1299,9 @@
     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 @@
     // 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 @@
 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 @@
         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 @@
         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 @@
         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;

Modified: trunk/Source/WebCore/html/track/AudioTrackList.cpp (233495 => 233496)


--- trunk/Source/WebCore/html/track/AudioTrackList.cpp	2018-07-04 03:18:55 UTC (rev 233495)
+++ trunk/Source/WebCore/html/track/AudioTrackList.cpp	2018-07-04 03:31:29 UTC (rev 233496)
@@ -81,4 +81,9 @@
     return AudioTrackListEventTargetInterfaceType;
 }
 
+const char* AudioTrackList::activeDOMObjectName() const
+{
+    return "AudioTrackList";
+}
+
 #endif

Modified: trunk/Source/WebCore/html/track/AudioTrackList.h (233495 => 233496)


--- trunk/Source/WebCore/html/track/AudioTrackList.h	2018-07-04 03:18:55 UTC (rev 233495)
+++ trunk/Source/WebCore/html/track/AudioTrackList.h	2018-07-04 03:31:29 UTC (rev 233496)
@@ -52,6 +52,7 @@
 
 private:
     AudioTrackList(HTMLMediaElement*, ScriptExecutionContext*);
+    const char* activeDOMObjectName() const final;
 };
 
 } // namespace WebCore

Modified: trunk/Source/WebCore/html/track/AudioTrackList.idl (233495 => 233496)


--- trunk/Source/WebCore/html/track/AudioTrackList.idl	2018-07-04 03:18:55 UTC (rev 233495)
+++ trunk/Source/WebCore/html/track/AudioTrackList.idl	2018-07-04 03:31:29 UTC (rev 233496)
@@ -24,6 +24,7 @@
  */
 
 [
+    ActiveDOMObject,
     Conditional=VIDEO_TRACK,
     GenerateIsReachable=ImplElementRoot,
     JSCustomMarkFunction,

Modified: trunk/Source/WebCore/html/track/TextTrackList.cpp (233495 => 233496)


--- trunk/Source/WebCore/html/track/TextTrackList.cpp	2018-07-04 03:18:55 UTC (rev 233495)
+++ trunk/Source/WebCore/html/track/TextTrackList.cpp	2018-07-04 03:31:29 UTC (rev 233496)
@@ -265,4 +265,9 @@
     return TextTrackListEventTargetInterfaceType;
 }
 
+const char* TextTrackList::activeDOMObjectName() const
+{
+    return "TextTrackList";
+}
+
 #endif

Modified: trunk/Source/WebCore/html/track/TextTrackList.h (233495 => 233496)


--- trunk/Source/WebCore/html/track/TextTrackList.h	2018-07-04 03:18:55 UTC (rev 233495)
+++ trunk/Source/WebCore/html/track/TextTrackList.h	2018-07-04 03:31:29 UTC (rev 233496)
@@ -62,6 +62,7 @@
     TextTrackList(HTMLMediaElement*, ScriptExecutionContext*);
 
     void invalidateTrackIndexesAfterTrack(TextTrack&);
+    const char* activeDOMObjectName() const final;
 
     Vector<RefPtr<TrackBase>> m_addTrackTracks;
     Vector<RefPtr<TrackBase>> m_elementTracks;

Modified: trunk/Source/WebCore/html/track/TextTrackList.idl (233495 => 233496)


--- trunk/Source/WebCore/html/track/TextTrackList.idl	2018-07-04 03:18:55 UTC (rev 233495)
+++ trunk/Source/WebCore/html/track/TextTrackList.idl	2018-07-04 03:31:29 UTC (rev 233496)
@@ -24,6 +24,7 @@
  */
 
 [
+    ActiveDOMObject,
     Conditional=VIDEO_TRACK,
     GenerateIsReachable=ImplElementRoot,
     JSCustomMarkFunction,

Modified: trunk/Source/WebCore/html/track/TrackListBase.cpp (233495 => 233496)


--- trunk/Source/WebCore/html/track/TrackListBase.cpp	2018-07-04 03:18:55 UTC (rev 233495)
+++ trunk/Source/WebCore/html/track/TrackListBase.cpp	2018-07-04 03:31:29 UTC (rev 233496)
@@ -34,14 +34,15 @@
 #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,26 @@
     return false;
 }
 
+bool TrackListBase::canSuspendForDocumentSuspension() const
+{
+    return !m_asyncEventQueue.hasPendingEvents();
+}
+
+void TrackListBase::suspend(ReasonForSuspension)
+{
+    ASSERT(!m_asyncEventQueue.hasPendingEvents());
+}
+
+void TrackListBase::resume()
+{
+    ASSERT(!m_asyncEventQueue.hasPendingEvents());
+}
+
+void TrackListBase::stop()
+{
+    m_asyncEventQueue.close();
+}
+
+} // namespace WebCore
+
 #endif

Modified: trunk/Source/WebCore/html/track/TrackListBase.h (233495 => 233496)


--- trunk/Source/WebCore/html/track/TrackListBase.h	2018-07-04 03:18:55 UTC (rev 233495)
+++ trunk/Source/WebCore/html/track/TrackListBase.h	2018-07-04 03:31:29 UTC (rev 233496)
@@ -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 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 @@
 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(); }

Modified: trunk/Source/WebCore/html/track/VideoTrackList.cpp (233495 => 233496)


--- trunk/Source/WebCore/html/track/VideoTrackList.cpp	2018-07-04 03:18:55 UTC (rev 233495)
+++ trunk/Source/WebCore/html/track/VideoTrackList.cpp	2018-07-04 03:31:29 UTC (rev 233496)
@@ -94,4 +94,9 @@
     return VideoTrackListEventTargetInterfaceType;
 }
 
+const char* VideoTrackList::activeDOMObjectName() const
+{
+    return "VideoTrackList";
+}
+
 #endif

Modified: trunk/Source/WebCore/html/track/VideoTrackList.h (233495 => 233496)


--- trunk/Source/WebCore/html/track/VideoTrackList.h	2018-07-04 03:18:55 UTC (rev 233495)
+++ trunk/Source/WebCore/html/track/VideoTrackList.h	2018-07-04 03:31:29 UTC (rev 233496)
@@ -53,6 +53,7 @@
 
 private:
     VideoTrackList(HTMLMediaElement*, ScriptExecutionContext*);
+    const char* activeDOMObjectName() const final;
 };
 
 } // namespace WebCore

Modified: trunk/Source/WebCore/html/track/VideoTrackList.idl (233495 => 233496)


--- trunk/Source/WebCore/html/track/VideoTrackList.idl	2018-07-04 03:18:55 UTC (rev 233495)
+++ trunk/Source/WebCore/html/track/VideoTrackList.idl	2018-07-04 03:31:29 UTC (rev 233496)
@@ -24,6 +24,7 @@
  */
 
 [
+    ActiveDOMObject,
     Conditional=VIDEO_TRACK,
     GenerateIsReachable=ImplElementRoot,
     JSCustomMarkFunction,
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to