Title: [200627] trunk
Revision
200627
Author
[email protected]
Date
2016-05-10 10:27:12 -0700 (Tue, 10 May 2016)

Log Message

Return a Promise from HTMLMediaElement.play()
https://bugs.webkit.org/show_bug.cgi?id=157400

Reviewed by Eric Carlson.

LayoutTests/imported/w3c:

Rebaseline web-platform-tests/html/dom/interfaces-expected.txt with new (failing) result.

* web-platform-tests/html/dom/interfaces-expected.txt:

Source/WebCore:

Tests: media/media-play-promise-reject-error-notsupported.html
       media/media-play-promise-reject-load-abort.html
       media/media-play-promise-reject-pause-abort.html
       media/media-play-promise-reject-play-notallowed.html
       media/media-play-promise-reject-play-notsupported.html
       media/media-play-promise-resolve-when-playing.html
       media/media-play-promise-resolve.html

The HTML Living Standard Spec <https://html.spec.whatwg.org/multipage/embedded-content.html>
(5 May 2016) adds support for a Promise to be returned by the play() method, to be resolved
or rejected at defined points during loading and playback.

Add utility methods which encapsulate the definitions of the equivalent algorithms from the
HTML Spec.  Add a new, overloaded play() method on HTMLMediaElement which takes a DeferredWrapper
reference.

After the change to use scheduleNotifyAboutPlaying() instead of enqueueing the "playing" event
directly, we must ensure that the notifyAboutPlaying() task does not get fired before the
"play" event preceeding it does. So re-implement GenericEventQueue (which previously used
a timer to dispatch events) to use a GenericTaskQueue instead. This ensures that all tasks and
events are interleaved in the order in which they were enqueued.

Additionally, the new pauseAfterDetachedTimerFired() event was firing out of microtask order, which
broke some W3C tests after the changes to GenericEventQueue. Move GenericEventQueue and
GenericTaskQueue to the same timing source (namely, a WebCore Timer) and interleave Events
and Tasks by having GenericEventQueue use GenericTaskQueue to issue its Events. Because
Document::postTask() cannot ensure ordering with Timer-based events, switch HTMLMediaElement
over to Timer-backed GenericTaskQueues.

* dom/GenericEventQueue.cpp:
(WebCore::GenericEventQueue::GenericEventQueue):
(WebCore::GenericEventQueue::enqueueEvent):
(WebCore::GenericEventQueue::close):
(WebCore::GenericEventQueue::cancelAllEvents):
(WebCore::GenericEventQueue::suspend):
(WebCore::GenericEventQueue::resume):
(WebCore::GenericEventQueue::sharedTimer): Deleted.
(WebCore::GenericEventQueue::sharedTimerFired): Deleted.
(WebCore::GenericEventQueue::pendingQueues): Deleted.
* dom/GenericEventQueue.h:
* platform/GenericTaskQueue.cpp: Added.
(WebCore::TaskDispatcher<Timer>::~TaskDispatcher):
(WebCore::TaskDispatcher<Timer>::postTask):
(WebCore::TaskDispatcher<Timer>::sharedTimer):
(WebCore::TaskDispatcher<Timer>::sharedTimerFired):
(WebCore::TaskDispatcher<Timer>::pendingDispatchers):
(WebCore::TaskDispatcher<Timer>::dispatchOneTask):
* platform/GenericTaskQueue.h:
(WebCore::TaskDispatcher<Timer>::TaskDispatcher): Deleted.
(WebCore::TaskDispatcher<Timer>::postTask): Deleted.
(WebCore::TaskDispatcher<Timer>::timerFired): Deleted.
* html/HTMLMediaElement.cpp:
(WebCore::HTMLMediaElement::HTMLMediaElement):
(WebCore::HTMLMediaElement::~HTMLMediaElement):
(WebCore::HTMLMediaElement::scheduleResolvePendingPlayPromises):
(WebCore::HTMLMediaElement::rejectPendingPlayPromises):
(WebCore::HTMLMediaElement::resolvePendingPlayPromises):
(WebCore::HTMLMediaElement::scheduleNotifyAboutPlaying):
(WebCore::HTMLMediaElement::notifyAboutPlaying):
(WebCore::HTMLMediaElement::noneSupported):
(WebCore::HTMLMediaElement::cancelPendingEventsAndCallbacks):
(WebCore::HTMLMediaElement::setReadyState):
(WebCore::HTMLMediaElement::play):
(WebCore::HTMLMediaElement::playInternal):
(WebCore::HTMLMediaElement::pauseInternal):
(WebCore::HTMLMediaElement::contextDestroyed):
(WebCore::HTMLMediaElement::stop):
(WebCore::HTMLMediaElement::pauseAfterDetachedTask): Renamed from pauseAfterDetachedTimerFired.
(WebCore::HTMLMediaElement::removedFrom):
(WebCore::HTMLMediaElement::contextDestroyed):
* html/HTMLMediaElement.h:
* html/HTMLMediaElement.idl:
* CMakeLists.txt:
* WebCore.xcodeproj/project.pbxproj:

LayoutTests:

* media/media-play-promise-reject-error-notsupported-expected.txt: Added.
* media/media-play-promise-reject-error-notsupported.html: Added.
* media/media-play-promise-reject-load-abort-expected.txt: Added.
* media/media-play-promise-reject-load-abort.html: Added.
* media/media-play-promise-reject-pause-abort-expected.txt: Added.
* media/media-play-promise-reject-pause-abort.html: Added.
* media/media-play-promise-reject-play-notallowed-expected.txt: Added.
* media/media-play-promise-reject-play-notallowed.html: Added.
* media/media-play-promise-reject-play-notsupported-expected.txt: Added.
* media/media-play-promise-reject-play-notsupported.html: Added.
* media/media-play-promise-resolve-expected.txt: Added.
* media/media-play-promise-resolve-when-playing-expected.txt: Added.
* media/media-play-promise-resolve-when-playing.html: Added.
* media/media-play-promise-resolve.html: Added.

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (200626 => 200627)


--- trunk/LayoutTests/ChangeLog	2016-05-10 17:23:09 UTC (rev 200626)
+++ trunk/LayoutTests/ChangeLog	2016-05-10 17:27:12 UTC (rev 200627)
@@ -1,3 +1,25 @@
+2016-05-05  Jer Noble  <[email protected]>
+
+        Return a Promise from HTMLMediaElement.play()
+        https://bugs.webkit.org/show_bug.cgi?id=157400
+
+        Reviewed by Eric Carlson.
+
+        * media/media-play-promise-reject-error-notsupported-expected.txt: Added.
+        * media/media-play-promise-reject-error-notsupported.html: Added.
+        * media/media-play-promise-reject-load-abort-expected.txt: Added.
+        * media/media-play-promise-reject-load-abort.html: Added.
+        * media/media-play-promise-reject-pause-abort-expected.txt: Added.
+        * media/media-play-promise-reject-pause-abort.html: Added.
+        * media/media-play-promise-reject-play-notallowed-expected.txt: Added.
+        * media/media-play-promise-reject-play-notallowed.html: Added.
+        * media/media-play-promise-reject-play-notsupported-expected.txt: Added.
+        * media/media-play-promise-reject-play-notsupported.html: Added.
+        * media/media-play-promise-resolve-expected.txt: Added.
+        * media/media-play-promise-resolve-when-playing-expected.txt: Added.
+        * media/media-play-promise-resolve-when-playing.html: Added.
+        * media/media-play-promise-resolve.html: Added.
+
 2016-05-10  Zalan Bujtas  <[email protected]>
 
         REGRESSION (r193610): Drop down menu doesn’t expand at allofbach.com

Modified: trunk/LayoutTests/imported/w3c/ChangeLog (200626 => 200627)


--- trunk/LayoutTests/imported/w3c/ChangeLog	2016-05-10 17:23:09 UTC (rev 200626)
+++ trunk/LayoutTests/imported/w3c/ChangeLog	2016-05-10 17:27:12 UTC (rev 200627)
@@ -1,3 +1,14 @@
+2016-05-06  Jer Noble  <[email protected]>
+
+        Return a Promise from HTMLMediaElement.play()
+        https://bugs.webkit.org/show_bug.cgi?id=157400
+
+        Reviewed by Eric Carlson.
+
+        Rebaseline web-platform-tests/html/dom/interfaces-expected.txt with new (failing) result.
+
+        * web-platform-tests/html/dom/interfaces-expected.txt:
+
 2016-05-04  Chris Dumez  <[email protected]>
 
         Media elements should not be paused right away when removed from the document

Modified: trunk/LayoutTests/imported/w3c/web-platform-tests/html/dom/interfaces-expected.txt (200626 => 200627)


--- trunk/LayoutTests/imported/w3c/web-platform-tests/html/dom/interfaces-expected.txt	2016-05-10 17:23:09 UTC (rev 200626)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/html/dom/interfaces-expected.txt	2016-05-10 17:27:12 UTC (rev 200627)
@@ -2447,7 +2447,9 @@
 PASS HTMLMediaElement interface: attribute ended 
 PASS HTMLMediaElement interface: attribute autoplay 
 PASS HTMLMediaElement interface: attribute loop 
-PASS HTMLMediaElement interface: operation play() 
+FAIL HTMLMediaElement interface: operation play() assert_throws: calling operation with this = null didn't throw TypeError function "function () {
+            fn.apply(obj, args);
+        }" did not throw
 PASS HTMLMediaElement interface: operation pause() 
 PASS HTMLMediaElement interface: attribute mediaGroup 
 PASS HTMLMediaElement interface: attribute controller 

Added: trunk/LayoutTests/media/media-play-promise-reject-error-notsupported-expected.txt (0 => 200627)


--- trunk/LayoutTests/media/media-play-promise-reject-error-notsupported-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/media/media-play-promise-reject-error-notsupported-expected.txt	2016-05-10 17:27:12 UTC (rev 200627)
@@ -0,0 +1,7 @@
+
+RUN(mediaElement.src = "" "content/invalid"))
+RUN(mediaElement.play().then(failTest).catch(promiseRejected))
+Promise rejected. OK
+EXPECTED (error.name == 'NotSupportedError') OK
+END OF TEST
+

Added: trunk/LayoutTests/media/media-play-promise-reject-error-notsupported.html (0 => 200627)


--- trunk/LayoutTests/media/media-play-promise-reject-error-notsupported.html	                        (rev 0)
+++ trunk/LayoutTests/media/media-play-promise-reject-error-notsupported.html	2016-05-10 17:27:12 UTC (rev 200627)
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<html>
+    <head>
+        <script src=""
+        <script src=""
+
+        <script>
+        var promise;
+        var error;
+        function start()
+        {
+            findMediaElement();
+            run('mediaElement.src = "" "content/invalid")');
+            run('mediaElement.play().then(failTest).catch(promiseRejected)');    
+        }
+        function promiseRejected(e)
+        {
+            error = e;
+            logResult(true, "Promise rejected.");
+            testExpected('error.name', 'NotSupportedError');
+            endTest();
+        }
+        </script>
+    </head>
+
+    <body _onload_="start()">
+
+        <video></video>
+    
+    </body>
+</html>

Added: trunk/LayoutTests/media/media-play-promise-reject-load-abort-expected.txt (0 => 200627)


--- trunk/LayoutTests/media/media-play-promise-reject-load-abort-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/media/media-play-promise-reject-load-abort-expected.txt	2016-05-10 17:27:12 UTC (rev 200627)
@@ -0,0 +1,7 @@
+
+RUN(mediaElement.play().then(failTest).catch(promiseRejected))
+RUN(mediaElement.src = "" "content/test"))
+Promise rejected. OK
+EXPECTED (error.name == 'AbortError') OK
+END OF TEST
+

Added: trunk/LayoutTests/media/media-play-promise-reject-load-abort.html (0 => 200627)


--- trunk/LayoutTests/media/media-play-promise-reject-load-abort.html	                        (rev 0)
+++ trunk/LayoutTests/media/media-play-promise-reject-load-abort.html	2016-05-10 17:27:12 UTC (rev 200627)
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<html>
+    <head>
+        <script src=""
+        <script src=""
+
+        <script>
+        var promise;
+        var error;
+        function start()
+        {
+            findMediaElement();
+            run('mediaElement.play().then(failTest).catch(promiseRejected)');    
+            run('mediaElement.src = "" "content/test")');
+        }
+        function promiseRejected(e)
+        {
+            error = e;
+            logResult(true, "Promise rejected.");
+            testExpected('error.name', 'AbortError');
+            endTest();
+        }
+        </script>
+    </head>
+
+    <body _onload_="start()">
+
+        <video></video>
+    
+    </body>
+</html>

Added: trunk/LayoutTests/media/media-play-promise-reject-pause-abort-expected.txt (0 => 200627)


--- trunk/LayoutTests/media/media-play-promise-reject-pause-abort-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/media/media-play-promise-reject-pause-abort-expected.txt	2016-05-10 17:27:12 UTC (rev 200627)
@@ -0,0 +1,7 @@
+
+RUN(mediaElement.play().then(failTest).catch(promiseRejected))
+RUN(mediaElement.pause())
+Promise rejected. OK
+EXPECTED (error.name == 'AbortError') OK
+END OF TEST
+

Added: trunk/LayoutTests/media/media-play-promise-reject-pause-abort.html (0 => 200627)


--- trunk/LayoutTests/media/media-play-promise-reject-pause-abort.html	                        (rev 0)
+++ trunk/LayoutTests/media/media-play-promise-reject-pause-abort.html	2016-05-10 17:27:12 UTC (rev 200627)
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<html>
+    <head>
+        <script src=""
+        <script src=""
+
+        <script>
+        var promise;
+        var error;
+        function start()
+        {
+            findMediaElement();
+            run('mediaElement.play().then(failTest).catch(promiseRejected)');    
+            run('mediaElement.pause()');
+        }
+        function promiseRejected(e)
+        {
+            error = e;
+            logResult(true, "Promise rejected.");
+            testExpected('error.name', 'AbortError');
+            endTest();
+        }
+        </script>
+    </head>
+
+    <body _onload_="start()">
+
+        <video></video>
+    
+    </body>
+</html>

Added: trunk/LayoutTests/media/media-play-promise-reject-play-notallowed-expected.txt (0 => 200627)


--- trunk/LayoutTests/media/media-play-promise-reject-play-notallowed-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/media/media-play-promise-reject-play-notallowed-expected.txt	2016-05-10 17:27:12 UTC (rev 200627)
@@ -0,0 +1,8 @@
+
+RUN(internals.setMediaElementRestrictions(mediaElement, "RequireUserGestureForVideoRateChange"))
+RUN(mediaElement.src = "" "content/test"))
+RUN(mediaElement.play().then(failTest).catch(promiseRejected))
+Promise rejected. OK
+EXPECTED (error.name == 'NotAllowedError') OK
+END OF TEST
+

Added: trunk/LayoutTests/media/media-play-promise-reject-play-notallowed.html (0 => 200627)


--- trunk/LayoutTests/media/media-play-promise-reject-play-notallowed.html	                        (rev 0)
+++ trunk/LayoutTests/media/media-play-promise-reject-play-notallowed.html	2016-05-10 17:27:12 UTC (rev 200627)
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<html>
+    <head>
+        <script src=""
+        <script src=""
+
+        <script>
+        var promise;
+        var error;
+        function start()
+        {
+            findMediaElement();
+	        if (window.internals)
+	            run('internals.setMediaElementRestrictions(mediaElement, "RequireUserGestureForVideoRateChange")');
+
+            run('mediaElement.src = "" "content/test")');
+            run('mediaElement.play().then(failTest).catch(promiseRejected)');    
+        }
+
+        function promiseRejected(e)
+        {
+            error = e;
+            logResult(true, "Promise rejected.");
+            testExpected('error.name', 'NotAllowedError');
+            endTest();
+        }
+        </script>
+    </head>
+
+    <body _onload_="start()">
+
+        <video></video>
+    
+    </body>
+</html>

Added: trunk/LayoutTests/media/media-play-promise-reject-play-notsupported-expected.txt (0 => 200627)


--- trunk/LayoutTests/media/media-play-promise-reject-play-notsupported-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/media/media-play-promise-reject-play-notsupported-expected.txt	2016-05-10 17:27:12 UTC (rev 200627)
@@ -0,0 +1,8 @@
+
+RUN(mediaElement.src = "" "content/invalid"))
+EVENT(error)
+RUN(mediaElement.play().then(failTest).catch(promiseRejected))
+Promise rejected. OK
+EXPECTED (error.name == 'NotSupportedError') OK
+END OF TEST
+

Added: trunk/LayoutTests/media/media-play-promise-reject-play-notsupported.html (0 => 200627)


--- trunk/LayoutTests/media/media-play-promise-reject-play-notsupported.html	                        (rev 0)
+++ trunk/LayoutTests/media/media-play-promise-reject-play-notsupported.html	2016-05-10 17:27:12 UTC (rev 200627)
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<html>
+    <head>
+        <script src=""
+        <script src=""
+
+        <script>
+        var promise;
+        var error;
+        function start()
+        {
+            findMediaElement();
+            run('mediaElement.src = "" "content/invalid")');
+            waitForEventOnce('error', error);
+        }
+
+        function error()
+        {
+            run('mediaElement.play().then(failTest).catch(promiseRejected)');    
+        }
+
+        function promiseRejected(e)
+        {
+            error = e;
+            logResult(true, "Promise rejected.");
+            testExpected('error.name', 'NotSupportedError');
+            endTest();
+        }
+        </script>
+    </head>
+
+    <body _onload_="start()">
+
+        <video></video>
+    
+    </body>
+</html>

Added: trunk/LayoutTests/media/media-play-promise-resolve-expected.txt (0 => 200627)


--- trunk/LayoutTests/media/media-play-promise-resolve-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/media/media-play-promise-resolve-expected.txt	2016-05-10 17:27:12 UTC (rev 200627)
@@ -0,0 +1,6 @@
+
+RUN(mediaElement.src = "" "content/test"))
+RUN(mediaElement.play().then(promiseResolved).catch(failTest))
+Promise resolved. OK
+END OF TEST
+

Added: trunk/LayoutTests/media/media-play-promise-resolve-when-playing-expected.txt (0 => 200627)


--- trunk/LayoutTests/media/media-play-promise-resolve-when-playing-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/media/media-play-promise-resolve-when-playing-expected.txt	2016-05-10 17:27:12 UTC (rev 200627)
@@ -0,0 +1,8 @@
+
+RUN(mediaElement.src = "" "content/test"))
+RUN(mediaElement.play())
+EVENT(playing)
+RUN(mediaElement.play().then(promiseResolved).catch(failTest))
+Promise resolved. OK
+END OF TEST
+

Added: trunk/LayoutTests/media/media-play-promise-resolve-when-playing.html (0 => 200627)


--- trunk/LayoutTests/media/media-play-promise-resolve-when-playing.html	                        (rev 0)
+++ trunk/LayoutTests/media/media-play-promise-resolve-when-playing.html	2016-05-10 17:27:12 UTC (rev 200627)
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<html>
+    <head>
+        <script src=""
+        <script src=""
+
+        <script>
+        var promise;
+        function start()
+        {
+            findMediaElement();
+            run('mediaElement.src = "" "content/test")');
+            run('mediaElement.play()');
+            waitForEventOnce('playing', playing);
+        }
+
+        function playing()
+        {
+            run('mediaElement.play().then(promiseResolved).catch(failTest)');    
+        }
+
+        function promiseResolved()
+        {
+            logResult(true, "Promise resolved.");
+            endTest();
+        }
+        </script>
+    </head>
+
+    <body _onload_="start()">
+
+        <video></video>
+    
+    </body>
+</html>

Added: trunk/LayoutTests/media/media-play-promise-resolve.html (0 => 200627)


--- trunk/LayoutTests/media/media-play-promise-resolve.html	                        (rev 0)
+++ trunk/LayoutTests/media/media-play-promise-resolve.html	2016-05-10 17:27:12 UTC (rev 200627)
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html>
+    <head>
+        <script src=""
+        <script src=""
+
+        <script>
+        var promise;
+        function start()
+        {
+            findMediaElement();
+            run('mediaElement.src = "" "content/test")');
+            run('mediaElement.play().then(promiseResolved).catch(failTest)');
+        }
+
+        function promiseResolved()
+        {
+            logResult(true, "Promise resolved.");
+            endTest();
+        }
+        </script>
+    </head>
+
+    <body _onload_="start()">
+
+        <video></video>
+    
+    </body>
+</html>

Modified: trunk/Source/WebCore/CMakeLists.txt (200626 => 200627)


--- trunk/Source/WebCore/CMakeLists.txt	2016-05-10 17:23:09 UTC (rev 200626)
+++ trunk/Source/WebCore/CMakeLists.txt	2016-05-10 17:27:12 UTC (rev 200627)
@@ -2109,6 +2109,7 @@
     platform/FileChooser.cpp
     platform/FileStream.cpp
     platform/FileSystem.cpp
+    platform/GenericTaskQueue.cpp
     platform/Language.cpp
     platform/Length.cpp
     platform/LengthBox.cpp

Modified: trunk/Source/WebCore/ChangeLog (200626 => 200627)


--- trunk/Source/WebCore/ChangeLog	2016-05-10 17:23:09 UTC (rev 200626)
+++ trunk/Source/WebCore/ChangeLog	2016-05-10 17:27:12 UTC (rev 200627)
@@ -1,3 +1,85 @@
+2016-05-05  Jer Noble  <[email protected]>
+
+        Return a Promise from HTMLMediaElement.play()
+        https://bugs.webkit.org/show_bug.cgi?id=157400
+
+        Reviewed by Eric Carlson.
+
+        Tests: media/media-play-promise-reject-error-notsupported.html
+               media/media-play-promise-reject-load-abort.html
+               media/media-play-promise-reject-pause-abort.html
+               media/media-play-promise-reject-play-notallowed.html
+               media/media-play-promise-reject-play-notsupported.html
+               media/media-play-promise-resolve-when-playing.html
+               media/media-play-promise-resolve.html
+
+        The HTML Living Standard Spec <https://html.spec.whatwg.org/multipage/embedded-content.html>
+        (5 May 2016) adds support for a Promise to be returned by the play() method, to be resolved
+        or rejected at defined points during loading and playback.
+
+        Add utility methods which encapsulate the definitions of the equivalent algorithms from the
+        HTML Spec.  Add a new, overloaded play() method on HTMLMediaElement which takes a DeferredWrapper
+        reference.
+
+        After the change to use scheduleNotifyAboutPlaying() instead of enqueueing the "playing" event
+        directly, we must ensure that the notifyAboutPlaying() task does not get fired before the
+        "play" event preceeding it does. So re-implement GenericEventQueue (which previously used
+        a timer to dispatch events) to use a GenericTaskQueue instead. This ensures that all tasks and
+        events are interleaved in the order in which they were enqueued.
+
+        Additionally, the new pauseAfterDetachedTimerFired() event was firing out of microtask order, which
+        broke some W3C tests after the changes to GenericEventQueue. Move GenericEventQueue and
+        GenericTaskQueue to the same timing source (namely, a WebCore Timer) and interleave Events
+        and Tasks by having GenericEventQueue use GenericTaskQueue to issue its Events. Because
+        Document::postTask() cannot ensure ordering with Timer-based events, switch HTMLMediaElement
+        over to Timer-backed GenericTaskQueues.
+
+        * dom/GenericEventQueue.cpp:
+        (WebCore::GenericEventQueue::GenericEventQueue):
+        (WebCore::GenericEventQueue::enqueueEvent):
+        (WebCore::GenericEventQueue::close):
+        (WebCore::GenericEventQueue::cancelAllEvents):
+        (WebCore::GenericEventQueue::suspend):
+        (WebCore::GenericEventQueue::resume):
+        (WebCore::GenericEventQueue::sharedTimer): Deleted.
+        (WebCore::GenericEventQueue::sharedTimerFired): Deleted.
+        (WebCore::GenericEventQueue::pendingQueues): Deleted.
+        * dom/GenericEventQueue.h:
+        * platform/GenericTaskQueue.cpp: Added.
+        (WebCore::TaskDispatcher<Timer>::~TaskDispatcher):
+        (WebCore::TaskDispatcher<Timer>::postTask):
+        (WebCore::TaskDispatcher<Timer>::sharedTimer):
+        (WebCore::TaskDispatcher<Timer>::sharedTimerFired):
+        (WebCore::TaskDispatcher<Timer>::pendingDispatchers):
+        (WebCore::TaskDispatcher<Timer>::dispatchOneTask):
+        * platform/GenericTaskQueue.h:
+        (WebCore::TaskDispatcher<Timer>::TaskDispatcher): Deleted.
+        (WebCore::TaskDispatcher<Timer>::postTask): Deleted.
+        (WebCore::TaskDispatcher<Timer>::timerFired): Deleted.
+        * html/HTMLMediaElement.cpp:
+        (WebCore::HTMLMediaElement::HTMLMediaElement):
+        (WebCore::HTMLMediaElement::~HTMLMediaElement):
+        (WebCore::HTMLMediaElement::scheduleResolvePendingPlayPromises):
+        (WebCore::HTMLMediaElement::rejectPendingPlayPromises):
+        (WebCore::HTMLMediaElement::resolvePendingPlayPromises):
+        (WebCore::HTMLMediaElement::scheduleNotifyAboutPlaying):
+        (WebCore::HTMLMediaElement::notifyAboutPlaying):
+        (WebCore::HTMLMediaElement::noneSupported):
+        (WebCore::HTMLMediaElement::cancelPendingEventsAndCallbacks):
+        (WebCore::HTMLMediaElement::setReadyState):
+        (WebCore::HTMLMediaElement::play):
+        (WebCore::HTMLMediaElement::playInternal):
+        (WebCore::HTMLMediaElement::pauseInternal):
+        (WebCore::HTMLMediaElement::contextDestroyed):
+        (WebCore::HTMLMediaElement::stop):
+        (WebCore::HTMLMediaElement::pauseAfterDetachedTask): Renamed from pauseAfterDetachedTimerFired.
+        (WebCore::HTMLMediaElement::removedFrom):
+        (WebCore::HTMLMediaElement::contextDestroyed):
+        * html/HTMLMediaElement.h:
+        * html/HTMLMediaElement.idl:
+        * CMakeLists.txt:
+        * WebCore.xcodeproj/project.pbxproj:
+
 2016-05-10  Chris Dumez  <[email protected]>
 
         Get rid of a lot of calls to RefPtr::release()

Modified: trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj (200626 => 200627)


--- trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj	2016-05-10 17:23:09 UTC (rev 200626)
+++ trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj	2016-05-10 17:27:12 UTC (rev 200627)
@@ -6042,6 +6042,7 @@
 		CD3E252318046BCD00E27F56 /* CSSGridTemplateAreasValue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CD3E252118046BCD00E27F56 /* CSSGridTemplateAreasValue.cpp */; };
 		CD3E252418046BCD00E27F56 /* CSSGridTemplateAreasValue.h in Headers */ = {isa = PBXBuildFile; fileRef = CD3E252218046BCD00E27F56 /* CSSGridTemplateAreasValue.h */; };
 		CD4AC52A1496AE9A0087C4EF /* Composite.wav in Copy Audio Resources */ = {isa = PBXBuildFile; fileRef = CD4AC5281496AE2F0087C4EF /* Composite.wav */; };
+		CD4BE52A1CE136EF009D87DA /* GenericTaskQueue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CD4BE5291CE13425009D87DA /* GenericTaskQueue.cpp */; };
 		CD5209E41B0BD8380077184E /* MediaPlayerEnums.h in Headers */ = {isa = PBXBuildFile; fileRef = CD5209E31B0BD8380077184E /* MediaPlayerEnums.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		CD5209E61B0BD9E10077184E /* HTMLMediaElementEnums.h in Headers */ = {isa = PBXBuildFile; fileRef = CD5209E51B0BD9E10077184E /* HTMLMediaElementEnums.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		CD52481A18E200ED0008A07D /* DisplaySleepDisabler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CD52481818E200ED0008A07D /* DisplaySleepDisabler.cpp */; };
@@ -14032,6 +14033,7 @@
 		CD3E252218046BCD00E27F56 /* CSSGridTemplateAreasValue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CSSGridTemplateAreasValue.h; sourceTree = "<group>"; };
 		CD4097FF1A8C855F004C65E9 /* CFNSURLConnectionSPI.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CFNSURLConnectionSPI.h; sourceTree = "<group>"; };
 		CD4AC5281496AE2F0087C4EF /* Composite.wav */ = {isa = PBXFileReference; lastKnownFileType = audio.wav; name = Composite.wav; path = platform/audio/resources/Composite.wav; sourceTree = SOURCE_ROOT; };
+		CD4BE5291CE13425009D87DA /* GenericTaskQueue.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GenericTaskQueue.cpp; sourceTree = "<group>"; };
 		CD4E0AFA11F7BC27009D3811 /* fullscreen.css */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.css; path = fullscreen.css; sourceTree = "<group>"; };
 		CD5209E31B0BD8380077184E /* MediaPlayerEnums.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MediaPlayerEnums.h; sourceTree = "<group>"; };
 		CD5209E51B0BD9E10077184E /* HTMLMediaElementEnums.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLMediaElementEnums.h; sourceTree = "<group>"; };
@@ -22949,6 +22951,7 @@
 				5179CE23195C81420019C198 /* GamepadProvider.h */,
 				5179CE29195C91860019C198 /* GamepadProviderClient.h */,
 				CD62FB941AF018E70012ED7D /* GenericTaskQueue.h */,
+				CD4BE5291CE13425009D87DA /* GenericTaskQueue.cpp */,
 				A8748BDF12CBF2DC001FBA41 /* HashTools.h */,
 				BC3BC29B0E91AB0F00835588 /* HostWindow.h */,
 				862F129F18C1DCE4005C54AF /* HysteresisActivity.h */,
@@ -31340,6 +31343,7 @@
 				D70AD65713E1342B005B50B4 /* RenderRegion.cpp in Sources */,
 				BCE93F471517C6D5008CCF74 /* RenderRegionSet.cpp in Sources */,
 				A871DFE20A15376B00B12A68 /* RenderReplaced.cpp in Sources */,
+				CD4BE52A1CE136EF009D87DA /* GenericTaskQueue.cpp in Sources */,
 				BCA846D60DC67A350026C309 /* RenderReplica.cpp in Sources */,
 				1479FAED109AE37500DED655 /* RenderRuby.cpp in Sources */,
 				1479FAEF109AE37500DED655 /* RenderRubyBase.cpp in Sources */,

Modified: trunk/Source/WebCore/dom/GenericEventQueue.cpp (200626 => 200627)


--- trunk/Source/WebCore/dom/GenericEventQueue.cpp	2016-05-10 17:23:09 UTC (rev 200626)
+++ trunk/Source/WebCore/dom/GenericEventQueue.cpp	2016-05-10 17:27:12 UTC (rev 200627)
@@ -28,6 +28,7 @@
 
 #include "Event.h"
 #include "EventTarget.h"
+#include "ScriptExecutionContext.h"
 #include "Timer.h"
 #include <wtf/MainThread.h>
 #include <wtf/NeverDestroyed.h>
@@ -36,7 +37,6 @@
 
 GenericEventQueue::GenericEventQueue(EventTarget& owner)
     : m_owner(owner)
-    , m_weakPtrFactory(this)
     , m_isClosed(false)
 {
 }
@@ -58,42 +58,9 @@
     if (m_isSuspended)
         return;
 
-    pendingQueues().append(m_weakPtrFactory.createWeakPtr());
-    if (!sharedTimer().isActive())
-        sharedTimer().startOneShot(0);
+    m_taskQueue.enqueueTask(std::bind(&GenericEventQueue::dispatchOneEvent, this));
 }
 
-Timer& GenericEventQueue::sharedTimer()
-{
-    ASSERT(isMainThread());
-    static NeverDestroyed<Timer> timer(GenericEventQueue::sharedTimerFired);
-    return timer.get();
-}
-
-void GenericEventQueue::sharedTimerFired()
-{
-    ASSERT(!sharedTimer().isActive());
-    ASSERT(!pendingQueues().isEmpty());
-
-    // Copy the pending events first because we don't want to process synchronously the new events
-    // queued by the JS events handlers that are executed in the loop below.
-    Deque<WeakPtr<GenericEventQueue>> queuedEvents;
-    std::swap(queuedEvents, pendingQueues());
-    while (!queuedEvents.isEmpty()) {
-        WeakPtr<GenericEventQueue> queue = queuedEvents.takeFirst();
-        if (!queue)
-            continue;
-        queue->dispatchOneEvent();
-    }
-}
-
-Deque<WeakPtr<GenericEventQueue>>& GenericEventQueue::pendingQueues()
-{
-    ASSERT(isMainThread());
-    static NeverDestroyed<Deque<WeakPtr<GenericEventQueue>>> queues;
-    return queues.get();
-}
-
 void GenericEventQueue::dispatchOneEvent()
 {
     ASSERT(!m_pendingEvents.isEmpty());
@@ -108,13 +75,13 @@
 {
     m_isClosed = true;
 
-    m_weakPtrFactory.revokeAll();
+    m_taskQueue.close();
     m_pendingEvents.clear();
 }
 
 void GenericEventQueue::cancelAllEvents()
 {
-    m_weakPtrFactory.revokeAll();
+    m_taskQueue.cancelAllTasks();
     m_pendingEvents.clear();
 }
 
@@ -127,7 +94,7 @@
 {
     ASSERT(!m_isSuspended);
     m_isSuspended = true;
-    m_weakPtrFactory.revokeAll();
+    m_taskQueue.cancelAllTasks();
 }
 
 void GenericEventQueue::resume()
@@ -141,10 +108,7 @@
         return;
 
     for (unsigned i = 0; i < m_pendingEvents.size(); ++i)
-        pendingQueues().append(m_weakPtrFactory.createWeakPtr());
-
-    if (!sharedTimer().isActive())
-        sharedTimer().startOneShot(0);
+        m_taskQueue.enqueueTask(std::bind(&GenericEventQueue::dispatchOneEvent, this));
 }
 
 }

Modified: trunk/Source/WebCore/dom/GenericEventQueue.h (200626 => 200627)


--- trunk/Source/WebCore/dom/GenericEventQueue.h	2016-05-10 17:23:09 UTC (rev 200626)
+++ trunk/Source/WebCore/dom/GenericEventQueue.h	2016-05-10 17:27:12 UTC (rev 200627)
@@ -26,6 +26,7 @@
 #ifndef GenericEventQueue_h
 #define GenericEventQueue_h
 
+#include "GenericTaskQueue.h"
 #include <wtf/Deque.h>
 #include <wtf/Forward.h>
 #include <wtf/RefPtr.h>
@@ -52,15 +53,11 @@
     void resume();
 
 private:
-    static Timer& sharedTimer();
-    static void sharedTimerFired();
-    static Deque<WeakPtr<GenericEventQueue>>& pendingQueues();
-
     void dispatchOneEvent();
 
     EventTarget& m_owner;
+    GenericTaskQueue<Timer> m_taskQueue;
     Deque<RefPtr<Event>> m_pendingEvents;
-    WeakPtrFactory<GenericEventQueue> m_weakPtrFactory;
     bool m_isClosed;
     bool m_isSuspended { false };
 };

Modified: trunk/Source/WebCore/html/HTMLMediaElement.cpp (200626 => 200627)


--- trunk/Source/WebCore/html/HTMLMediaElement.cpp	2016-05-10 17:23:09 UTC (rev 200626)
+++ trunk/Source/WebCore/html/HTMLMediaElement.cpp	2016-05-10 17:27:12 UTC (rev 200627)
@@ -51,6 +51,7 @@
 #include "FrameView.h"
 #include "HTMLSourceElement.h"
 #include "HTMLVideoElement.h"
+#include "JSDOMError.h"
 #include "JSHTMLMediaElement.h"
 #include "Language.h"
 #include "Logging.h"
@@ -348,10 +349,6 @@
     , m_progressEventTimer(*this, &HTMLMediaElement::progressEventTimerFired)
     , m_playbackProgressTimer(*this, &HTMLMediaElement::playbackProgressTimerFired)
     , m_scanTimer(*this, &HTMLMediaElement::scanTimerFired)
-    , m_pauseAfterDetachedTimer(*this, &HTMLMediaElement::pauseAfterDetachedTimerFired)
-    , m_seekTaskQueue(document)
-    , m_resizeTaskQueue(document)
-    , m_shadowDOMTaskQueue(document)
     , m_playedTimeRanges()
     , m_asyncEventQueue(*this)
     , m_requestedPlaybackRate(1)
@@ -560,6 +557,8 @@
 #endif
 
     m_seekTaskQueue.close();
+    m_promiseTaskQueue.close();
+    m_pauseAfterDetachedTaskQueue.close();
 
     m_completelyLoaded = true;
 }
@@ -779,7 +778,7 @@
     return InsertionDone;
 }
 
-void HTMLMediaElement::pauseAfterDetachedTimerFired()
+void HTMLMediaElement::pauseAfterDetachedTask()
 {
     // If we were re-inserted into an active document, no need to pause.
     if (m_inActiveDocument)
@@ -815,7 +814,7 @@
     m_inActiveDocument = false;
     if (insertionPoint.inDocument()) {
         // Pause asynchronously to let the operation that removed us finish, in case we get inserted back into a document.
-        m_pauseAfterDetachedTimer.startOneShot(0);
+        m_pauseAfterDetachedTaskQueue.enqueueTask(std::bind(&HTMLMediaElement::pauseAfterDetachedTask, this));
     }
 
     HTMLElement::removedFrom(insertionPoint);
@@ -903,6 +902,38 @@
     m_asyncEventQueue.enqueueEvent(WTFMove(event));
 }
 
+void HTMLMediaElement::scheduleResolvePendingPlayPromises()
+{
+    m_promiseTaskQueue.enqueueTask(std::bind(&HTMLMediaElement::resolvePendingPlayPromises, this));
+}
+
+void HTMLMediaElement::rejectPendingPlayPromises(DOMError& error)
+{
+    Vector<PlayPromise> pendingPlayPromises = WTFMove(m_pendingPlayPromises);
+
+    for (auto& promise : pendingPlayPromises)
+        promise.reject(error);
+}
+
+void HTMLMediaElement::resolvePendingPlayPromises()
+{
+    Vector<PlayPromise> pendingPlayPromises = WTFMove(m_pendingPlayPromises);
+
+    for (auto& promise : pendingPlayPromises)
+        promise.resolve(nullptr);
+}
+
+void HTMLMediaElement::scheduleNotifyAboutPlaying()
+{
+    m_promiseTaskQueue.enqueueTask(std::bind(&HTMLMediaElement::notifyAboutPlaying, this));
+}
+
+void HTMLMediaElement::notifyAboutPlaying()
+{
+    dispatchEvent(Event::create(eventNames().playingEvent, false, true));
+    resolvePendingPlayPromises();
+}
+
 void HTMLMediaElement::pendingActionTimerFired()
 {
     Ref<HTMLMediaElement> protect(*this); // loadNextSourceChild may fire 'beforeload', which can make arbitrary DOM mutations.
@@ -1915,6 +1946,8 @@
     // 7 - Queue a task to fire a simple event named error at the media element.
     scheduleEvent(eventNames().errorEvent);
 
+    rejectPendingPlayPromises(DOMError::create("NotSupportedError", "The operation is not supported."));
+
 #if ENABLE(MEDIA_SOURCE)
     closeMediaSource();
 #endif
@@ -1979,6 +2012,8 @@
 
     for (auto& source : childrenOfType<HTMLSourceElement>(*this))
         source.cancelPendingErrorEvent();
+
+    rejectPendingPlayPromises(DOMError::create("AbortError", "The operation was aborted."));
 }
 
 void HTMLMediaElement::mediaPlayerNetworkStateChanged(MediaPlayer*)
@@ -2242,7 +2277,7 @@
     if (m_readyState == HAVE_FUTURE_DATA && oldState <= HAVE_CURRENT_DATA && tracksAreReady) {
         scheduleEvent(eventNames().canplayEvent);
         if (isPotentiallyPlaying)
-            scheduleEvent(eventNames().playingEvent);
+            scheduleNotifyAboutPlaying();
         shouldUpdateDisplayState = true;
     }
 
@@ -2253,13 +2288,13 @@
         scheduleEvent(eventNames().canplaythroughEvent);
 
         if (isPotentiallyPlaying && oldState <= HAVE_CURRENT_DATA)
-            scheduleEvent(eventNames().playingEvent);
+            scheduleNotifyAboutPlaying();
 
         if (canTransitionFromAutoplayToPlay()) {
             m_paused = false;
             invalidateCachedTime();
             scheduleEvent(eventNames().playEvent);
-            scheduleEvent(eventNames().playingEvent);
+            scheduleNotifyAboutPlaying();
         }
 
         shouldUpdateDisplayState = true;
@@ -2986,6 +3021,29 @@
     setAttribute(preloadAttr, preload);
 }
 
+void HTMLMediaElement::play(PlayPromise&& promise)
+{
+    LOG(Media, "HTMLMediaElement::play(%p)", this);
+
+    if (!m_mediaSession->playbackPermitted(*this)) {
+        promise.reject(DOMError::create("NotAllowedError", "The request is not allowed by the user agent or the platform in the current context."));
+        return;
+    }
+
+    if (m_error && m_error->code() == MediaError::MEDIA_ERR_SRC_NOT_SUPPORTED) {
+        promise.reject(DOMError::create("NotSupportedError", "The operation is not supported.."));
+        return;
+    }
+
+    if (ScriptController::processingUserGestureForMedia())
+        removeBehaviorsRestrictionsAfterFirstUserGesture();
+
+    if (!playInternal())
+        promise.reject(DOMError::create("NotAllowedError", "The request is not allowed by the user agent or the platform in the current context."));
+
+    m_pendingPlayPromises.append(WTFMove(promise));
+}
+
 void HTMLMediaElement::play()
 {
     LOG(Media, "HTMLMediaElement::play(%p)", this);
@@ -2998,13 +3056,13 @@
     playInternal();
 }
 
-void HTMLMediaElement::playInternal()
+bool HTMLMediaElement::playInternal()
 {
     LOG(Media, "HTMLMediaElement::playInternal(%p)", this);
     
     if (!m_mediaSession->clientWillBeginPlayback()) {
         LOG(Media, "  returning because of interruption");
-        return;
+        return false;
     }
 
     // 4.8.10.9. Playing the media resource
@@ -3025,7 +3083,7 @@
         if (m_readyState <= HAVE_CURRENT_DATA)
             scheduleEvent(eventNames().waitingEvent);
         else if (m_readyState >= HAVE_FUTURE_DATA)
-            scheduleEvent(eventNames().playingEvent);
+            scheduleNotifyAboutPlaying();
 
 #if ENABLE(MEDIA_SESSION)
         // 6.3 Activating a media session from a media element
@@ -3047,15 +3105,18 @@
 
                 if (!m_session->invoke()) {
                     pause();
-                    return;
+                    return false;
                 }
             }
         }
 #endif
-    }
+    } else if (m_readyState >= HAVE_FUTURE_DATA)
+        scheduleResolvePendingPlayPromises();
+
     m_autoplaying = false;
     updatePlayState();
     updateMediaController();
+    return true;
 }
 
 void HTMLMediaElement::pause()
@@ -3093,6 +3154,7 @@
         m_paused = true;
         scheduleTimeupdateEvent(false);
         scheduleEvent(eventNames().pauseEvent);
+        rejectPendingPlayPromises(DOMError::create("AbortError", "The operation was aborted."));
 
         if (MemoryPressureHandler::singleton().isUnderMemoryPressure())
             purgeBufferedDataIfPossible();
@@ -5005,6 +5067,8 @@
     m_seekTaskQueue.close();
     m_resizeTaskQueue.close();
     m_shadowDOMTaskQueue.close();
+    m_promiseTaskQueue.close();
+    m_pauseAfterDetachedTaskQueue.close();
 
     ActiveDOMObject::contextDestroyed();
 }
@@ -5016,6 +5080,7 @@
     stopWithoutDestroyingMediaPlayer();
 
     m_asyncEventQueue.close();
+    m_promiseTaskQueue.close();
 
     // Once an active DOM object has been stopped it can not be restarted, so we can deallocate
     // the media player now. Note that userCancelledLoad will already called clearMediaPlayer

Modified: trunk/Source/WebCore/html/HTMLMediaElement.h (200626 => 200627)


--- trunk/Source/WebCore/html/HTMLMediaElement.h	2016-05-10 17:23:09 UTC (rev 200626)
+++ trunk/Source/WebCore/html/HTMLMediaElement.h	2016-05-10 17:27:12 UTC (rev 200627)
@@ -32,6 +32,7 @@
 #include "GenericEventQueue.h"
 #include "GenericTaskQueue.h"
 #include "HTMLMediaElementEnums.h"
+#include "JSDOMPromise.h"
 #include "MediaCanStartListener.h"
 #include "MediaControllerInterface.h"
 #include "MediaElementSession.h"
@@ -58,6 +59,7 @@
 class AudioSourceProvider;
 class MediaElementAudioSourceNode;
 #endif
+class DOMError;
 class DisplaySleepDisabler;
 class Event;
 class HTMLSourceElement;
@@ -141,6 +143,11 @@
 
     using HTMLMediaElementEnums::DelayedActionType;
     void scheduleDelayedAction(DelayedActionType);
+    void scheduleResolvePendingPlayPromises();
+    void rejectPendingPlayPromises(DOMError&);
+    void resolvePendingPlayPromises();
+    void scheduleNotifyAboutPlaying();
+    void notifyAboutPlaying();
     
     MediaPlayerEnums::MovieLoadType movieLoadType() const;
     
@@ -204,6 +211,10 @@
     bool isAutoplaying() const { return m_autoplaying; }
     bool loop() const;
     void setLoop(bool b);
+
+    typedef DOMPromise<std::nullptr_t, DOMError&> PlayPromise;
+    void play(PlayPromise&&);
+
     WEBCORE_EXPORT void play() override;
     WEBCORE_EXPORT void pause() override;
     void setShouldBufferData(bool) override;
@@ -671,7 +682,7 @@
 
     // These "internal" functions do not check user gesture restrictions.
     void loadInternal();
-    void playInternal();
+    bool playInternal();
     void pauseInternal();
 
     void prepareForLoad();
@@ -772,19 +783,22 @@
     void isVisibleInViewportChanged() final;
     void updateShouldAutoplay();
 
-    void pauseAfterDetachedTimerFired();
+    void pauseAfterDetachedTask();
 
     Timer m_pendingActionTimer;
     Timer m_progressEventTimer;
     Timer m_playbackProgressTimer;
     Timer m_scanTimer;
-    Timer m_pauseAfterDetachedTimer;
-    GenericTaskQueue<ScriptExecutionContext> m_seekTaskQueue;
-    GenericTaskQueue<ScriptExecutionContext> m_resizeTaskQueue;
-    GenericTaskQueue<ScriptExecutionContext> m_shadowDOMTaskQueue;
+    GenericTaskQueue<Timer> m_seekTaskQueue;
+    GenericTaskQueue<Timer> m_resizeTaskQueue;
+    GenericTaskQueue<Timer> m_shadowDOMTaskQueue;
+    GenericTaskQueue<Timer> m_promiseTaskQueue;
+    GenericTaskQueue<Timer> m_pauseAfterDetachedTaskQueue;
     RefPtr<TimeRanges> m_playedTimeRanges;
     GenericEventQueue m_asyncEventQueue;
 
+    Vector<PlayPromise> m_pendingPlayPromises;
+
     double m_requestedPlaybackRate;
     double m_reportedPlaybackRate;
     double m_defaultPlaybackRate;

Modified: trunk/Source/WebCore/html/HTMLMediaElement.idl (200626 => 200627)


--- trunk/Source/WebCore/html/HTMLMediaElement.idl	2016-05-10 17:23:09 UTC (rev 200626)
+++ trunk/Source/WebCore/html/HTMLMediaElement.idl	2016-05-10 17:27:12 UTC (rev 200627)
@@ -75,7 +75,7 @@
     readonly attribute boolean ended;
     [Reflect] attribute boolean autoplay;
     [Reflect] attribute boolean loop;
-    void play();
+    Promise play();
     void pause();
     void fastSeek(unrestricted double time);
 

Added: trunk/Source/WebCore/platform/GenericTaskQueue.cpp (0 => 200627)


--- trunk/Source/WebCore/platform/GenericTaskQueue.cpp	                        (rev 0)
+++ trunk/Source/WebCore/platform/GenericTaskQueue.cpp	2016-05-10 17:27:12 UTC (rev 200627)
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "GenericTaskQueue.h"
+
+#include <wtf/MainThread.h>
+#include <wtf/NeverDestroyed.h>
+
+namespace WebCore {
+
+TaskDispatcher<Timer>::~TaskDispatcher()
+{
+    Deque<TaskDispatcher<Timer>*> replacementQueue;
+    for (auto* dispatcher : pendingDispatchers()) {
+        if (dispatcher != this)
+            replacementQueue.append(dispatcher);
+    }
+    if (replacementQueue.isEmpty())
+        sharedTimer().stop();
+    pendingDispatchers() = WTFMove(replacementQueue);
+}
+
+void TaskDispatcher<Timer>::postTask(std::function<void()> function)
+{
+    m_pendingTasks.append(WTFMove(function));
+    pendingDispatchers().append(this);
+    if (!sharedTimer().isActive())
+        sharedTimer().startOneShot(0);
+}
+
+Timer& TaskDispatcher<Timer>::sharedTimer()
+{
+    ASSERT(isMainThread());
+    static NeverDestroyed<Timer> timer(TaskDispatcher<Timer>::sharedTimerFired);
+    return timer.get();
+}
+
+void TaskDispatcher<Timer>::sharedTimerFired()
+{
+    ASSERT(!sharedTimer().isActive());
+    ASSERT(!pendingDispatchers().isEmpty());
+
+    // Copy the pending events first because we don't want to process synchronously the new events
+    // queued by the JS events handlers that are executed in the loop below.
+    Deque<TaskDispatcher<Timer>*> queuedDispatchers = WTFMove(pendingDispatchers());
+    while (!queuedDispatchers.isEmpty()) {
+        TaskDispatcher<Timer>* dispatcher = queuedDispatchers.takeFirst();
+        if (!dispatcher)
+            continue;
+        dispatcher->dispatchOneTask();
+    }
+}
+
+Deque<TaskDispatcher<Timer>*>& TaskDispatcher<Timer>::pendingDispatchers()
+{
+    ASSERT(isMainThread());
+    static NeverDestroyed<Deque<TaskDispatcher<Timer>*>> dispatchers;
+    return dispatchers.get();
+}
+
+void TaskDispatcher<Timer>::dispatchOneTask()
+{
+    ASSERT(!m_pendingTasks.isEmpty());
+    std::function<void()> task = m_pendingTasks.takeFirst();
+    task();
+}
+
+}
+

Modified: trunk/Source/WebCore/platform/GenericTaskQueue.h (200626 => 200627)


--- trunk/Source/WebCore/platform/GenericTaskQueue.h	2016-05-10 17:23:09 UTC (rev 200626)
+++ trunk/Source/WebCore/platform/GenericTaskQueue.h	2016-05-10 17:27:12 UTC (rev 200627)
@@ -52,27 +52,17 @@
 template<>
 class TaskDispatcher<Timer> {
 public:
-    TaskDispatcher()
-        : m_timer(*this, &TaskDispatcher<Timer>::timerFired)
-    {
-    }
+    ~TaskDispatcher();
+    void postTask(std::function<void()>);
 
-    void postTask(std::function<void()> function)
-    {
-        m_queue.append(function);
-        m_timer.startOneShot(0);
-    }
+private:
+    static Timer& sharedTimer();
+    static void sharedTimerFired();
+    static Deque<TaskDispatcher<Timer>*>& pendingDispatchers();
 
-    void timerFired()
-    {
-        Deque<std::function<void()>> queue;
-        queue.swap(m_queue);
-        for (std::function<void()>& function : queue)
-            function();
-    }
+    void dispatchOneTask();
 
-    Timer m_timer;
-    Deque<std::function<void()>> m_queue;
+    Deque<std::function<void()>> m_pendingTasks;
 };
 
 template <typename T>
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to