<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[200638] trunk</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/200638">200638</a></dd>
<dt>Author</dt> <dd>jer.noble@apple.com</dd>
<dt>Date</dt> <dd>2016-05-10 13:32:02 -0700 (Tue, 10 May 2016)</dd>
</dl>

<h3>Log Message</h3>
<pre>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 &lt;https://html.spec.whatwg.org/multipage/embedded-content.html&gt;
(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 &quot;playing&quot; event
directly, we must ensure that the notifyAboutPlaying() task does not get fired before the
&quot;play&quot; 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.

Use a WeakPtr to track the destruction of TaskDispatcher&lt;Timer&gt; objects in pendingDispatchers().

* 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&lt;Timer&gt;::~TaskDispatcher):
(WebCore::TaskDispatcher&lt;Timer&gt;::postTask):
(WebCore::TaskDispatcher&lt;Timer&gt;::sharedTimer):
(WebCore::TaskDispatcher&lt;Timer&gt;::sharedTimerFired):
(WebCore::TaskDispatcher&lt;Timer&gt;::pendingDispatchers):
(WebCore::TaskDispatcher&lt;Timer&gt;::dispatchOneTask):
* platform/GenericTaskQueue.h:
(WebCore::TaskDispatcher&lt;Timer&gt;::TaskDispatcher): Deleted.
(WebCore::TaskDispatcher&lt;Timer&gt;::postTask): Deleted.
(WebCore::TaskDispatcher&lt;Timer&gt;::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.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsChangeLog">trunk/LayoutTests/ChangeLog</a></li>
<li><a href="#trunkLayoutTestsimportedw3cChangeLog">trunk/LayoutTests/imported/w3c/ChangeLog</a></li>
<li><a href="#trunkLayoutTestsimportedw3cwebplatformtestshtmldominterfacesexpectedtxt">trunk/LayoutTests/imported/w3c/web-platform-tests/html/dom/interfaces-expected.txt</a></li>
<li><a href="#trunkSourceWebCoreCMakeListstxt">trunk/Source/WebCore/CMakeLists.txt</a></li>
<li><a href="#trunkSourceWebCoreChangeLog">trunk/Source/WebCore/ChangeLog</a></li>
<li><a href="#trunkSourceWebCoreWebCorexcodeprojprojectpbxproj">trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj</a></li>
<li><a href="#trunkSourceWebCoredomGenericEventQueuecpp">trunk/Source/WebCore/dom/GenericEventQueue.cpp</a></li>
<li><a href="#trunkSourceWebCoredomGenericEventQueueh">trunk/Source/WebCore/dom/GenericEventQueue.h</a></li>
<li><a href="#trunkSourceWebCorehtmlHTMLMediaElementcpp">trunk/Source/WebCore/html/HTMLMediaElement.cpp</a></li>
<li><a href="#trunkSourceWebCorehtmlHTMLMediaElementh">trunk/Source/WebCore/html/HTMLMediaElement.h</a></li>
<li><a href="#trunkSourceWebCorehtmlHTMLMediaElementidl">trunk/Source/WebCore/html/HTMLMediaElement.idl</a></li>
<li><a href="#trunkSourceWebCoreplatformGenericTaskQueueh">trunk/Source/WebCore/platform/GenericTaskQueue.h</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsmediamediaplaypromiserejecterrornotsupportedexpectedtxt">trunk/LayoutTests/media/media-play-promise-reject-error-notsupported-expected.txt</a></li>
<li><a href="#trunkLayoutTestsmediamediaplaypromiserejecterrornotsupportedhtml">trunk/LayoutTests/media/media-play-promise-reject-error-notsupported.html</a></li>
<li><a href="#trunkLayoutTestsmediamediaplaypromiserejectloadabortexpectedtxt">trunk/LayoutTests/media/media-play-promise-reject-load-abort-expected.txt</a></li>
<li><a href="#trunkLayoutTestsmediamediaplaypromiserejectloadaborthtml">trunk/LayoutTests/media/media-play-promise-reject-load-abort.html</a></li>
<li><a href="#trunkLayoutTestsmediamediaplaypromiserejectpauseabortexpectedtxt">trunk/LayoutTests/media/media-play-promise-reject-pause-abort-expected.txt</a></li>
<li><a href="#trunkLayoutTestsmediamediaplaypromiserejectpauseaborthtml">trunk/LayoutTests/media/media-play-promise-reject-pause-abort.html</a></li>
<li><a href="#trunkLayoutTestsmediamediaplaypromiserejectplaynotallowedexpectedtxt">trunk/LayoutTests/media/media-play-promise-reject-play-notallowed-expected.txt</a></li>
<li><a href="#trunkLayoutTestsmediamediaplaypromiserejectplaynotallowedhtml">trunk/LayoutTests/media/media-play-promise-reject-play-notallowed.html</a></li>
<li><a href="#trunkLayoutTestsmediamediaplaypromiserejectplaynotsupportedexpectedtxt">trunk/LayoutTests/media/media-play-promise-reject-play-notsupported-expected.txt</a></li>
<li><a href="#trunkLayoutTestsmediamediaplaypromiserejectplaynotsupportedhtml">trunk/LayoutTests/media/media-play-promise-reject-play-notsupported.html</a></li>
<li><a href="#trunkLayoutTestsmediamediaplaypromiseresolveexpectedtxt">trunk/LayoutTests/media/media-play-promise-resolve-expected.txt</a></li>
<li><a href="#trunkLayoutTestsmediamediaplaypromiseresolvewhenplayingexpectedtxt">trunk/LayoutTests/media/media-play-promise-resolve-when-playing-expected.txt</a></li>
<li><a href="#trunkLayoutTestsmediamediaplaypromiseresolvewhenplayinghtml">trunk/LayoutTests/media/media-play-promise-resolve-when-playing.html</a></li>
<li><a href="#trunkLayoutTestsmediamediaplaypromiseresolvehtml">trunk/LayoutTests/media/media-play-promise-resolve.html</a></li>
<li><a href="#trunkSourceWebCoreplatformGenericTaskQueuecpp">trunk/Source/WebCore/platform/GenericTaskQueue.cpp</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkLayoutTestsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/ChangeLog (200637 => 200638)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/ChangeLog        2016-05-10 20:21:45 UTC (rev 200637)
+++ trunk/LayoutTests/ChangeLog        2016-05-10 20:32:02 UTC (rev 200638)
</span><span class="lines">@@ -1,3 +1,25 @@
</span><ins>+2016-05-05  Jer Noble  &lt;jer.noble@apple.com&gt;
+
+        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.
+
</ins><span class="cx"> 2016-05-10  Commit Queue  &lt;commit-queue@webkit.org&gt;
</span><span class="cx"> 
</span><span class="cx">         Unreviewed, rolling out r200627.
</span></span></pre></div>
<a id="trunkLayoutTestsimportedw3cChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/imported/w3c/ChangeLog (200637 => 200638)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/imported/w3c/ChangeLog        2016-05-10 20:21:45 UTC (rev 200637)
+++ trunk/LayoutTests/imported/w3c/ChangeLog        2016-05-10 20:32:02 UTC (rev 200638)
</span><span class="lines">@@ -1,3 +1,14 @@
</span><ins>+2016-05-06  Jer Noble  &lt;jer.noble@apple.com&gt;
+
+        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:
+
</ins><span class="cx"> 2016-05-10  Commit Queue  &lt;commit-queue@webkit.org&gt;
</span><span class="cx"> 
</span><span class="cx">         Unreviewed, rolling out r200627.
</span></span></pre></div>
<a id="trunkLayoutTestsimportedw3cwebplatformtestshtmldominterfacesexpectedtxt"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/imported/w3c/web-platform-tests/html/dom/interfaces-expected.txt (200637 => 200638)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/imported/w3c/web-platform-tests/html/dom/interfaces-expected.txt        2016-05-10 20:21:45 UTC (rev 200637)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/html/dom/interfaces-expected.txt        2016-05-10 20:32:02 UTC (rev 200638)
</span><span class="lines">@@ -2447,7 +2447,9 @@
</span><span class="cx"> PASS HTMLMediaElement interface: attribute ended 
</span><span class="cx"> PASS HTMLMediaElement interface: attribute autoplay 
</span><span class="cx"> PASS HTMLMediaElement interface: attribute loop 
</span><del>-PASS HTMLMediaElement interface: operation play() 
</del><ins>+FAIL HTMLMediaElement interface: operation play() assert_throws: calling operation with this = null didn't throw TypeError function &quot;function () {
+            fn.apply(obj, args);
+        }&quot; did not throw
</ins><span class="cx"> PASS HTMLMediaElement interface: operation pause() 
</span><span class="cx"> PASS HTMLMediaElement interface: attribute mediaGroup 
</span><span class="cx"> PASS HTMLMediaElement interface: attribute controller 
</span></span></pre></div>
<a id="trunkLayoutTestsmediamediaplaypromiserejecterrornotsupportedexpectedtxt"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/media/media-play-promise-reject-error-notsupported-expected.txt (0 => 200638)</h4>
<pre class="diff"><span>
<span class="info">--- 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 20:32:02 UTC (rev 200638)
</span><span class="lines">@@ -0,0 +1,7 @@
</span><ins>+
+RUN(mediaElement.src = findMediaFile(&quot;video&quot;, &quot;content/invalid&quot;))
+RUN(mediaElement.play().then(failTest).catch(promiseRejected))
+Promise rejected. OK
+EXPECTED (error.name == 'NotSupportedError') OK
+END OF TEST
+
</ins></span></pre></div>
<a id="trunkLayoutTestsmediamediaplaypromiserejecterrornotsupportedhtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/media/media-play-promise-reject-error-notsupported.html (0 => 200638)</h4>
<pre class="diff"><span>
<span class="info">--- 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 20:32:02 UTC (rev 200638)
</span><span class="lines">@@ -0,0 +1,31 @@
</span><ins>+&lt;!DOCTYPE html&gt;
+&lt;html&gt;
+    &lt;head&gt;
+        &lt;script src=media-file.js&gt;&lt;/script&gt;
+        &lt;script src=video-test.js&gt;&lt;/script&gt;
+
+        &lt;script&gt;
+        var promise;
+        var error;
+        function start()
+        {
+            findMediaElement();
+            run('mediaElement.src = findMediaFile(&quot;video&quot;, &quot;content/invalid&quot;)');
+            run('mediaElement.play().then(failTest).catch(promiseRejected)');    
+        }
+        function promiseRejected(e)
+        {
+            error = e;
+            logResult(true, &quot;Promise rejected.&quot;);
+            testExpected('error.name', 'NotSupportedError');
+            endTest();
+        }
+        &lt;/script&gt;
+    &lt;/head&gt;
+
+    &lt;body onload=&quot;start()&quot;&gt;
+
+        &lt;video&gt;&lt;/video&gt;
+    
+    &lt;/body&gt;
+&lt;/html&gt;
</ins></span></pre></div>
<a id="trunkLayoutTestsmediamediaplaypromiserejectloadabortexpectedtxt"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/media/media-play-promise-reject-load-abort-expected.txt (0 => 200638)</h4>
<pre class="diff"><span>
<span class="info">--- 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 20:32:02 UTC (rev 200638)
</span><span class="lines">@@ -0,0 +1,7 @@
</span><ins>+
+RUN(mediaElement.play().then(failTest).catch(promiseRejected))
+RUN(mediaElement.src = findMediaFile(&quot;video&quot;, &quot;content/test&quot;))
+Promise rejected. OK
+EXPECTED (error.name == 'AbortError') OK
+END OF TEST
+
</ins></span></pre></div>
<a id="trunkLayoutTestsmediamediaplaypromiserejectloadaborthtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/media/media-play-promise-reject-load-abort.html (0 => 200638)</h4>
<pre class="diff"><span>
<span class="info">--- 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 20:32:02 UTC (rev 200638)
</span><span class="lines">@@ -0,0 +1,31 @@
</span><ins>+&lt;!DOCTYPE html&gt;
+&lt;html&gt;
+    &lt;head&gt;
+        &lt;script src=media-file.js&gt;&lt;/script&gt;
+        &lt;script src=video-test.js&gt;&lt;/script&gt;
+
+        &lt;script&gt;
+        var promise;
+        var error;
+        function start()
+        {
+            findMediaElement();
+            run('mediaElement.play().then(failTest).catch(promiseRejected)');    
+            run('mediaElement.src = findMediaFile(&quot;video&quot;, &quot;content/test&quot;)');
+        }
+        function promiseRejected(e)
+        {
+            error = e;
+            logResult(true, &quot;Promise rejected.&quot;);
+            testExpected('error.name', 'AbortError');
+            endTest();
+        }
+        &lt;/script&gt;
+    &lt;/head&gt;
+
+    &lt;body onload=&quot;start()&quot;&gt;
+
+        &lt;video&gt;&lt;/video&gt;
+    
+    &lt;/body&gt;
+&lt;/html&gt;
</ins></span></pre></div>
<a id="trunkLayoutTestsmediamediaplaypromiserejectpauseabortexpectedtxt"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/media/media-play-promise-reject-pause-abort-expected.txt (0 => 200638)</h4>
<pre class="diff"><span>
<span class="info">--- 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 20:32:02 UTC (rev 200638)
</span><span class="lines">@@ -0,0 +1,7 @@
</span><ins>+
+RUN(mediaElement.play().then(failTest).catch(promiseRejected))
+RUN(mediaElement.pause())
+Promise rejected. OK
+EXPECTED (error.name == 'AbortError') OK
+END OF TEST
+
</ins></span></pre></div>
<a id="trunkLayoutTestsmediamediaplaypromiserejectpauseaborthtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/media/media-play-promise-reject-pause-abort.html (0 => 200638)</h4>
<pre class="diff"><span>
<span class="info">--- 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 20:32:02 UTC (rev 200638)
</span><span class="lines">@@ -0,0 +1,31 @@
</span><ins>+&lt;!DOCTYPE html&gt;
+&lt;html&gt;
+    &lt;head&gt;
+        &lt;script src=media-file.js&gt;&lt;/script&gt;
+        &lt;script src=video-test.js&gt;&lt;/script&gt;
+
+        &lt;script&gt;
+        var promise;
+        var error;
+        function start()
+        {
+            findMediaElement();
+            run('mediaElement.play().then(failTest).catch(promiseRejected)');    
+            run('mediaElement.pause()');
+        }
+        function promiseRejected(e)
+        {
+            error = e;
+            logResult(true, &quot;Promise rejected.&quot;);
+            testExpected('error.name', 'AbortError');
+            endTest();
+        }
+        &lt;/script&gt;
+    &lt;/head&gt;
+
+    &lt;body onload=&quot;start()&quot;&gt;
+
+        &lt;video&gt;&lt;/video&gt;
+    
+    &lt;/body&gt;
+&lt;/html&gt;
</ins></span></pre></div>
<a id="trunkLayoutTestsmediamediaplaypromiserejectplaynotallowedexpectedtxt"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/media/media-play-promise-reject-play-notallowed-expected.txt (0 => 200638)</h4>
<pre class="diff"><span>
<span class="info">--- 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 20:32:02 UTC (rev 200638)
</span><span class="lines">@@ -0,0 +1,8 @@
</span><ins>+
+RUN(internals.setMediaElementRestrictions(mediaElement, &quot;RequireUserGestureForVideoRateChange&quot;))
+RUN(mediaElement.src = findMediaFile(&quot;video&quot;, &quot;content/test&quot;))
+RUN(mediaElement.play().then(failTest).catch(promiseRejected))
+Promise rejected. OK
+EXPECTED (error.name == 'NotAllowedError') OK
+END OF TEST
+
</ins></span></pre></div>
<a id="trunkLayoutTestsmediamediaplaypromiserejectplaynotallowedhtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/media/media-play-promise-reject-play-notallowed.html (0 => 200638)</h4>
<pre class="diff"><span>
<span class="info">--- 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 20:32:02 UTC (rev 200638)
</span><span class="lines">@@ -0,0 +1,35 @@
</span><ins>+&lt;!DOCTYPE html&gt;
+&lt;html&gt;
+    &lt;head&gt;
+        &lt;script src=media-file.js&gt;&lt;/script&gt;
+        &lt;script src=video-test.js&gt;&lt;/script&gt;
+
+        &lt;script&gt;
+        var promise;
+        var error;
+        function start()
+        {
+            findMediaElement();
+                if (window.internals)
+                    run('internals.setMediaElementRestrictions(mediaElement, &quot;RequireUserGestureForVideoRateChange&quot;)');
+
+            run('mediaElement.src = findMediaFile(&quot;video&quot;, &quot;content/test&quot;)');
+            run('mediaElement.play().then(failTest).catch(promiseRejected)');    
+        }
+
+        function promiseRejected(e)
+        {
+            error = e;
+            logResult(true, &quot;Promise rejected.&quot;);
+            testExpected('error.name', 'NotAllowedError');
+            endTest();
+        }
+        &lt;/script&gt;
+    &lt;/head&gt;
+
+    &lt;body onload=&quot;start()&quot;&gt;
+
+        &lt;video&gt;&lt;/video&gt;
+    
+    &lt;/body&gt;
+&lt;/html&gt;
</ins></span></pre></div>
<a id="trunkLayoutTestsmediamediaplaypromiserejectplaynotsupportedexpectedtxt"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/media/media-play-promise-reject-play-notsupported-expected.txt (0 => 200638)</h4>
<pre class="diff"><span>
<span class="info">--- 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 20:32:02 UTC (rev 200638)
</span><span class="lines">@@ -0,0 +1,8 @@
</span><ins>+
+RUN(mediaElement.src = findMediaFile(&quot;video&quot;, &quot;content/invalid&quot;))
+EVENT(error)
+RUN(mediaElement.play().then(failTest).catch(promiseRejected))
+Promise rejected. OK
+EXPECTED (error.name == 'NotSupportedError') OK
+END OF TEST
+
</ins></span></pre></div>
<a id="trunkLayoutTestsmediamediaplaypromiserejectplaynotsupportedhtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/media/media-play-promise-reject-play-notsupported.html (0 => 200638)</h4>
<pre class="diff"><span>
<span class="info">--- 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 20:32:02 UTC (rev 200638)
</span><span class="lines">@@ -0,0 +1,37 @@
</span><ins>+&lt;!DOCTYPE html&gt;
+&lt;html&gt;
+    &lt;head&gt;
+        &lt;script src=media-file.js&gt;&lt;/script&gt;
+        &lt;script src=video-test.js&gt;&lt;/script&gt;
+
+        &lt;script&gt;
+        var promise;
+        var error;
+        function start()
+        {
+            findMediaElement();
+            run('mediaElement.src = findMediaFile(&quot;video&quot;, &quot;content/invalid&quot;)');
+            waitForEventOnce('error', error);
+        }
+
+        function error()
+        {
+            run('mediaElement.play().then(failTest).catch(promiseRejected)');    
+        }
+
+        function promiseRejected(e)
+        {
+            error = e;
+            logResult(true, &quot;Promise rejected.&quot;);
+            testExpected('error.name', 'NotSupportedError');
+            endTest();
+        }
+        &lt;/script&gt;
+    &lt;/head&gt;
+
+    &lt;body onload=&quot;start()&quot;&gt;
+
+        &lt;video&gt;&lt;/video&gt;
+    
+    &lt;/body&gt;
+&lt;/html&gt;
</ins></span></pre></div>
<a id="trunkLayoutTestsmediamediaplaypromiseresolveexpectedtxt"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/media/media-play-promise-resolve-expected.txt (0 => 200638)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/media/media-play-promise-resolve-expected.txt                                (rev 0)
+++ trunk/LayoutTests/media/media-play-promise-resolve-expected.txt        2016-05-10 20:32:02 UTC (rev 200638)
</span><span class="lines">@@ -0,0 +1,6 @@
</span><ins>+
+RUN(mediaElement.src = findMediaFile(&quot;video&quot;, &quot;content/test&quot;))
+RUN(mediaElement.play().then(promiseResolved).catch(failTest))
+Promise resolved. OK
+END OF TEST
+
</ins></span></pre></div>
<a id="trunkLayoutTestsmediamediaplaypromiseresolvewhenplayingexpectedtxt"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/media/media-play-promise-resolve-when-playing-expected.txt (0 => 200638)</h4>
<pre class="diff"><span>
<span class="info">--- 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 20:32:02 UTC (rev 200638)
</span><span class="lines">@@ -0,0 +1,8 @@
</span><ins>+
+RUN(mediaElement.src = findMediaFile(&quot;video&quot;, &quot;content/test&quot;))
+RUN(mediaElement.play())
+EVENT(playing)
+RUN(mediaElement.play().then(promiseResolved).catch(failTest))
+Promise resolved. OK
+END OF TEST
+
</ins></span></pre></div>
<a id="trunkLayoutTestsmediamediaplaypromiseresolvewhenplayinghtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/media/media-play-promise-resolve-when-playing.html (0 => 200638)</h4>
<pre class="diff"><span>
<span class="info">--- 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 20:32:02 UTC (rev 200638)
</span><span class="lines">@@ -0,0 +1,35 @@
</span><ins>+&lt;!DOCTYPE html&gt;
+&lt;html&gt;
+    &lt;head&gt;
+        &lt;script src=media-file.js&gt;&lt;/script&gt;
+        &lt;script src=video-test.js&gt;&lt;/script&gt;
+
+        &lt;script&gt;
+        var promise;
+        function start()
+        {
+            findMediaElement();
+            run('mediaElement.src = findMediaFile(&quot;video&quot;, &quot;content/test&quot;)');
+            run('mediaElement.play()');
+            waitForEventOnce('playing', playing);
+        }
+
+        function playing()
+        {
+            run('mediaElement.play().then(promiseResolved).catch(failTest)');    
+        }
+
+        function promiseResolved()
+        {
+            logResult(true, &quot;Promise resolved.&quot;);
+            endTest();
+        }
+        &lt;/script&gt;
+    &lt;/head&gt;
+
+    &lt;body onload=&quot;start()&quot;&gt;
+
+        &lt;video&gt;&lt;/video&gt;
+    
+    &lt;/body&gt;
+&lt;/html&gt;
</ins></span></pre></div>
<a id="trunkLayoutTestsmediamediaplaypromiseresolvehtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/media/media-play-promise-resolve.html (0 => 200638)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/media/media-play-promise-resolve.html                                (rev 0)
+++ trunk/LayoutTests/media/media-play-promise-resolve.html        2016-05-10 20:32:02 UTC (rev 200638)
</span><span class="lines">@@ -0,0 +1,29 @@
</span><ins>+&lt;!DOCTYPE html&gt;
+&lt;html&gt;
+    &lt;head&gt;
+        &lt;script src=media-file.js&gt;&lt;/script&gt;
+        &lt;script src=video-test.js&gt;&lt;/script&gt;
+
+        &lt;script&gt;
+        var promise;
+        function start()
+        {
+            findMediaElement();
+            run('mediaElement.src = findMediaFile(&quot;video&quot;, &quot;content/test&quot;)');
+            run('mediaElement.play().then(promiseResolved).catch(failTest)');
+        }
+
+        function promiseResolved()
+        {
+            logResult(true, &quot;Promise resolved.&quot;);
+            endTest();
+        }
+        &lt;/script&gt;
+    &lt;/head&gt;
+
+    &lt;body onload=&quot;start()&quot;&gt;
+
+        &lt;video&gt;&lt;/video&gt;
+    
+    &lt;/body&gt;
+&lt;/html&gt;
</ins></span></pre></div>
<a id="trunkSourceWebCoreCMakeListstxt"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/CMakeLists.txt (200637 => 200638)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/CMakeLists.txt        2016-05-10 20:21:45 UTC (rev 200637)
+++ trunk/Source/WebCore/CMakeLists.txt        2016-05-10 20:32:02 UTC (rev 200638)
</span><span class="lines">@@ -2109,6 +2109,7 @@
</span><span class="cx">     platform/FileChooser.cpp
</span><span class="cx">     platform/FileStream.cpp
</span><span class="cx">     platform/FileSystem.cpp
</span><ins>+    platform/GenericTaskQueue.cpp
</ins><span class="cx">     platform/Language.cpp
</span><span class="cx">     platform/Length.cpp
</span><span class="cx">     platform/LengthBox.cpp
</span></span></pre></div>
<a id="trunkSourceWebCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/ChangeLog (200637 => 200638)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/ChangeLog        2016-05-10 20:21:45 UTC (rev 200637)
+++ trunk/Source/WebCore/ChangeLog        2016-05-10 20:32:02 UTC (rev 200638)
</span><span class="lines">@@ -1,3 +1,87 @@
</span><ins>+2016-05-05  Jer Noble  &lt;jer.noble@apple.com&gt;
+
+        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 &lt;https://html.spec.whatwg.org/multipage/embedded-content.html&gt;
+        (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 &quot;playing&quot; event
+        directly, we must ensure that the notifyAboutPlaying() task does not get fired before the
+        &quot;play&quot; 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.
+
+        Use a WeakPtr to track the destruction of TaskDispatcher&lt;Timer&gt; objects in pendingDispatchers().
+
+        * 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&lt;Timer&gt;::~TaskDispatcher):
+        (WebCore::TaskDispatcher&lt;Timer&gt;::postTask):
+        (WebCore::TaskDispatcher&lt;Timer&gt;::sharedTimer):
+        (WebCore::TaskDispatcher&lt;Timer&gt;::sharedTimerFired):
+        (WebCore::TaskDispatcher&lt;Timer&gt;::pendingDispatchers):
+        (WebCore::TaskDispatcher&lt;Timer&gt;::dispatchOneTask):
+        * platform/GenericTaskQueue.h:
+        (WebCore::TaskDispatcher&lt;Timer&gt;::TaskDispatcher): Deleted.
+        (WebCore::TaskDispatcher&lt;Timer&gt;::postTask): Deleted.
+        (WebCore::TaskDispatcher&lt;Timer&gt;::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:
+
</ins><span class="cx"> 2016-05-10  Commit Queue  &lt;commit-queue@webkit.org&gt;
</span><span class="cx"> 
</span><span class="cx">         Unreviewed, rolling out r200627.
</span></span></pre></div>
<a id="trunkSourceWebCoreWebCorexcodeprojprojectpbxproj"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj (200637 => 200638)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj        2016-05-10 20:21:45 UTC (rev 200637)
+++ trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj        2016-05-10 20:32:02 UTC (rev 200638)
</span><span class="lines">@@ -6042,6 +6042,7 @@
</span><span class="cx">                 CD3E252318046BCD00E27F56 /* CSSGridTemplateAreasValue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CD3E252118046BCD00E27F56 /* CSSGridTemplateAreasValue.cpp */; };
</span><span class="cx">                 CD3E252418046BCD00E27F56 /* CSSGridTemplateAreasValue.h in Headers */ = {isa = PBXBuildFile; fileRef = CD3E252218046BCD00E27F56 /* CSSGridTemplateAreasValue.h */; };
</span><span class="cx">                 CD4AC52A1496AE9A0087C4EF /* Composite.wav in Copy Audio Resources */ = {isa = PBXBuildFile; fileRef = CD4AC5281496AE2F0087C4EF /* Composite.wav */; };
</span><ins>+                CD4BE52A1CE136EF009D87DA /* GenericTaskQueue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CD4BE5291CE13425009D87DA /* GenericTaskQueue.cpp */; };
</ins><span class="cx">                 CD5209E41B0BD8380077184E /* MediaPlayerEnums.h in Headers */ = {isa = PBXBuildFile; fileRef = CD5209E31B0BD8380077184E /* MediaPlayerEnums.h */; settings = {ATTRIBUTES = (Private, ); }; };
</span><span class="cx">                 CD5209E61B0BD9E10077184E /* HTMLMediaElementEnums.h in Headers */ = {isa = PBXBuildFile; fileRef = CD5209E51B0BD9E10077184E /* HTMLMediaElementEnums.h */; settings = {ATTRIBUTES = (Private, ); }; };
</span><span class="cx">                 CD52481A18E200ED0008A07D /* DisplaySleepDisabler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CD52481818E200ED0008A07D /* DisplaySleepDisabler.cpp */; };
</span><span class="lines">@@ -14032,6 +14033,7 @@
</span><span class="cx">                 CD3E252218046BCD00E27F56 /* CSSGridTemplateAreasValue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CSSGridTemplateAreasValue.h; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><span class="cx">                 CD4097FF1A8C855F004C65E9 /* CFNSURLConnectionSPI.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CFNSURLConnectionSPI.h; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><span class="cx">                 CD4AC5281496AE2F0087C4EF /* Composite.wav */ = {isa = PBXFileReference; lastKnownFileType = audio.wav; name = Composite.wav; path = platform/audio/resources/Composite.wav; sourceTree = SOURCE_ROOT; };
</span><ins>+                CD4BE5291CE13425009D87DA /* GenericTaskQueue.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GenericTaskQueue.cpp; sourceTree = &quot;&lt;group&gt;&quot;; };
</ins><span class="cx">                 CD4E0AFA11F7BC27009D3811 /* fullscreen.css */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.css; path = fullscreen.css; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><span class="cx">                 CD5209E31B0BD8380077184E /* MediaPlayerEnums.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MediaPlayerEnums.h; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><span class="cx">                 CD5209E51B0BD9E10077184E /* HTMLMediaElementEnums.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLMediaElementEnums.h; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><span class="lines">@@ -22949,6 +22951,7 @@
</span><span class="cx">                                 5179CE23195C81420019C198 /* GamepadProvider.h */,
</span><span class="cx">                                 5179CE29195C91860019C198 /* GamepadProviderClient.h */,
</span><span class="cx">                                 CD62FB941AF018E70012ED7D /* GenericTaskQueue.h */,
</span><ins>+                                CD4BE5291CE13425009D87DA /* GenericTaskQueue.cpp */,
</ins><span class="cx">                                 A8748BDF12CBF2DC001FBA41 /* HashTools.h */,
</span><span class="cx">                                 BC3BC29B0E91AB0F00835588 /* HostWindow.h */,
</span><span class="cx">                                 862F129F18C1DCE4005C54AF /* HysteresisActivity.h */,
</span><span class="lines">@@ -31340,6 +31343,7 @@
</span><span class="cx">                                 D70AD65713E1342B005B50B4 /* RenderRegion.cpp in Sources */,
</span><span class="cx">                                 BCE93F471517C6D5008CCF74 /* RenderRegionSet.cpp in Sources */,
</span><span class="cx">                                 A871DFE20A15376B00B12A68 /* RenderReplaced.cpp in Sources */,
</span><ins>+                                CD4BE52A1CE136EF009D87DA /* GenericTaskQueue.cpp in Sources */,
</ins><span class="cx">                                 BCA846D60DC67A350026C309 /* RenderReplica.cpp in Sources */,
</span><span class="cx">                                 1479FAED109AE37500DED655 /* RenderRuby.cpp in Sources */,
</span><span class="cx">                                 1479FAEF109AE37500DED655 /* RenderRubyBase.cpp in Sources */,
</span></span></pre></div>
<a id="trunkSourceWebCoredomGenericEventQueuecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/dom/GenericEventQueue.cpp (200637 => 200638)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/dom/GenericEventQueue.cpp        2016-05-10 20:21:45 UTC (rev 200637)
+++ trunk/Source/WebCore/dom/GenericEventQueue.cpp        2016-05-10 20:32:02 UTC (rev 200638)
</span><span class="lines">@@ -28,6 +28,7 @@
</span><span class="cx"> 
</span><span class="cx"> #include &quot;Event.h&quot;
</span><span class="cx"> #include &quot;EventTarget.h&quot;
</span><ins>+#include &quot;ScriptExecutionContext.h&quot;
</ins><span class="cx"> #include &quot;Timer.h&quot;
</span><span class="cx"> #include &lt;wtf/MainThread.h&gt;
</span><span class="cx"> #include &lt;wtf/NeverDestroyed.h&gt;
</span><span class="lines">@@ -36,7 +37,6 @@
</span><span class="cx"> 
</span><span class="cx"> GenericEventQueue::GenericEventQueue(EventTarget&amp; owner)
</span><span class="cx">     : m_owner(owner)
</span><del>-    , m_weakPtrFactory(this)
</del><span class="cx">     , m_isClosed(false)
</span><span class="cx"> {
</span><span class="cx"> }
</span><span class="lines">@@ -58,42 +58,9 @@
</span><span class="cx">     if (m_isSuspended)
</span><span class="cx">         return;
</span><span class="cx"> 
</span><del>-    pendingQueues().append(m_weakPtrFactory.createWeakPtr());
-    if (!sharedTimer().isActive())
-        sharedTimer().startOneShot(0);
</del><ins>+    m_taskQueue.enqueueTask(std::bind(&amp;GenericEventQueue::dispatchOneEvent, this));
</ins><span class="cx"> }
</span><span class="cx"> 
</span><del>-Timer&amp; GenericEventQueue::sharedTimer()
-{
-    ASSERT(isMainThread());
-    static NeverDestroyed&lt;Timer&gt; 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&lt;WeakPtr&lt;GenericEventQueue&gt;&gt; queuedEvents;
-    std::swap(queuedEvents, pendingQueues());
-    while (!queuedEvents.isEmpty()) {
-        WeakPtr&lt;GenericEventQueue&gt; queue = queuedEvents.takeFirst();
-        if (!queue)
-            continue;
-        queue-&gt;dispatchOneEvent();
-    }
-}
-
-Deque&lt;WeakPtr&lt;GenericEventQueue&gt;&gt;&amp; GenericEventQueue::pendingQueues()
-{
-    ASSERT(isMainThread());
-    static NeverDestroyed&lt;Deque&lt;WeakPtr&lt;GenericEventQueue&gt;&gt;&gt; queues;
-    return queues.get();
-}
-
</del><span class="cx"> void GenericEventQueue::dispatchOneEvent()
</span><span class="cx"> {
</span><span class="cx">     ASSERT(!m_pendingEvents.isEmpty());
</span><span class="lines">@@ -108,13 +75,13 @@
</span><span class="cx"> {
</span><span class="cx">     m_isClosed = true;
</span><span class="cx"> 
</span><del>-    m_weakPtrFactory.revokeAll();
</del><ins>+    m_taskQueue.close();
</ins><span class="cx">     m_pendingEvents.clear();
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> void GenericEventQueue::cancelAllEvents()
</span><span class="cx"> {
</span><del>-    m_weakPtrFactory.revokeAll();
</del><ins>+    m_taskQueue.cancelAllTasks();
</ins><span class="cx">     m_pendingEvents.clear();
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="lines">@@ -127,7 +94,7 @@
</span><span class="cx"> {
</span><span class="cx">     ASSERT(!m_isSuspended);
</span><span class="cx">     m_isSuspended = true;
</span><del>-    m_weakPtrFactory.revokeAll();
</del><ins>+    m_taskQueue.cancelAllTasks();
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> void GenericEventQueue::resume()
</span><span class="lines">@@ -141,10 +108,7 @@
</span><span class="cx">         return;
</span><span class="cx"> 
</span><span class="cx">     for (unsigned i = 0; i &lt; m_pendingEvents.size(); ++i)
</span><del>-        pendingQueues().append(m_weakPtrFactory.createWeakPtr());
-
-    if (!sharedTimer().isActive())
-        sharedTimer().startOneShot(0);
</del><ins>+        m_taskQueue.enqueueTask(std::bind(&amp;GenericEventQueue::dispatchOneEvent, this));
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> }
</span></span></pre></div>
<a id="trunkSourceWebCoredomGenericEventQueueh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/dom/GenericEventQueue.h (200637 => 200638)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/dom/GenericEventQueue.h        2016-05-10 20:21:45 UTC (rev 200637)
+++ trunk/Source/WebCore/dom/GenericEventQueue.h        2016-05-10 20:32:02 UTC (rev 200638)
</span><span class="lines">@@ -26,6 +26,7 @@
</span><span class="cx"> #ifndef GenericEventQueue_h
</span><span class="cx"> #define GenericEventQueue_h
</span><span class="cx"> 
</span><ins>+#include &quot;GenericTaskQueue.h&quot;
</ins><span class="cx"> #include &lt;wtf/Deque.h&gt;
</span><span class="cx"> #include &lt;wtf/Forward.h&gt;
</span><span class="cx"> #include &lt;wtf/RefPtr.h&gt;
</span><span class="lines">@@ -52,15 +53,11 @@
</span><span class="cx">     void resume();
</span><span class="cx"> 
</span><span class="cx"> private:
</span><del>-    static Timer&amp; sharedTimer();
-    static void sharedTimerFired();
-    static Deque&lt;WeakPtr&lt;GenericEventQueue&gt;&gt;&amp; pendingQueues();
-
</del><span class="cx">     void dispatchOneEvent();
</span><span class="cx"> 
</span><span class="cx">     EventTarget&amp; m_owner;
</span><ins>+    GenericTaskQueue&lt;Timer&gt; m_taskQueue;
</ins><span class="cx">     Deque&lt;RefPtr&lt;Event&gt;&gt; m_pendingEvents;
</span><del>-    WeakPtrFactory&lt;GenericEventQueue&gt; m_weakPtrFactory;
</del><span class="cx">     bool m_isClosed;
</span><span class="cx">     bool m_isSuspended { false };
</span><span class="cx"> };
</span></span></pre></div>
<a id="trunkSourceWebCorehtmlHTMLMediaElementcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/html/HTMLMediaElement.cpp (200637 => 200638)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/html/HTMLMediaElement.cpp        2016-05-10 20:21:45 UTC (rev 200637)
+++ trunk/Source/WebCore/html/HTMLMediaElement.cpp        2016-05-10 20:32:02 UTC (rev 200638)
</span><span class="lines">@@ -51,6 +51,7 @@
</span><span class="cx"> #include &quot;FrameView.h&quot;
</span><span class="cx"> #include &quot;HTMLSourceElement.h&quot;
</span><span class="cx"> #include &quot;HTMLVideoElement.h&quot;
</span><ins>+#include &quot;JSDOMError.h&quot;
</ins><span class="cx"> #include &quot;JSHTMLMediaElement.h&quot;
</span><span class="cx"> #include &quot;Language.h&quot;
</span><span class="cx"> #include &quot;Logging.h&quot;
</span><span class="lines">@@ -348,10 +349,6 @@
</span><span class="cx">     , m_progressEventTimer(*this, &amp;HTMLMediaElement::progressEventTimerFired)
</span><span class="cx">     , m_playbackProgressTimer(*this, &amp;HTMLMediaElement::playbackProgressTimerFired)
</span><span class="cx">     , m_scanTimer(*this, &amp;HTMLMediaElement::scanTimerFired)
</span><del>-    , m_pauseAfterDetachedTimer(*this, &amp;HTMLMediaElement::pauseAfterDetachedTimerFired)
-    , m_seekTaskQueue(document)
-    , m_resizeTaskQueue(document)
-    , m_shadowDOMTaskQueue(document)
</del><span class="cx">     , m_playedTimeRanges()
</span><span class="cx">     , m_asyncEventQueue(*this)
</span><span class="cx">     , m_requestedPlaybackRate(1)
</span><span class="lines">@@ -560,6 +557,8 @@
</span><span class="cx"> #endif
</span><span class="cx"> 
</span><span class="cx">     m_seekTaskQueue.close();
</span><ins>+    m_promiseTaskQueue.close();
+    m_pauseAfterDetachedTaskQueue.close();
</ins><span class="cx"> 
</span><span class="cx">     m_completelyLoaded = true;
</span><span class="cx"> }
</span><span class="lines">@@ -779,7 +778,7 @@
</span><span class="cx">     return InsertionDone;
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-void HTMLMediaElement::pauseAfterDetachedTimerFired()
</del><ins>+void HTMLMediaElement::pauseAfterDetachedTask()
</ins><span class="cx"> {
</span><span class="cx">     // If we were re-inserted into an active document, no need to pause.
</span><span class="cx">     if (m_inActiveDocument)
</span><span class="lines">@@ -815,7 +814,7 @@
</span><span class="cx">     m_inActiveDocument = false;
</span><span class="cx">     if (insertionPoint.inDocument()) {
</span><span class="cx">         // Pause asynchronously to let the operation that removed us finish, in case we get inserted back into a document.
</span><del>-        m_pauseAfterDetachedTimer.startOneShot(0);
</del><ins>+        m_pauseAfterDetachedTaskQueue.enqueueTask(std::bind(&amp;HTMLMediaElement::pauseAfterDetachedTask, this));
</ins><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     HTMLElement::removedFrom(insertionPoint);
</span><span class="lines">@@ -903,6 +902,38 @@
</span><span class="cx">     m_asyncEventQueue.enqueueEvent(WTFMove(event));
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+void HTMLMediaElement::scheduleResolvePendingPlayPromises()
+{
+    m_promiseTaskQueue.enqueueTask(std::bind(&amp;HTMLMediaElement::resolvePendingPlayPromises, this));
+}
+
+void HTMLMediaElement::rejectPendingPlayPromises(DOMError&amp; error)
+{
+    Vector&lt;PlayPromise&gt; pendingPlayPromises = WTFMove(m_pendingPlayPromises);
+
+    for (auto&amp; promise : pendingPlayPromises)
+        promise.reject(error);
+}
+
+void HTMLMediaElement::resolvePendingPlayPromises()
+{
+    Vector&lt;PlayPromise&gt; pendingPlayPromises = WTFMove(m_pendingPlayPromises);
+
+    for (auto&amp; promise : pendingPlayPromises)
+        promise.resolve(nullptr);
+}
+
+void HTMLMediaElement::scheduleNotifyAboutPlaying()
+{
+    m_promiseTaskQueue.enqueueTask(std::bind(&amp;HTMLMediaElement::notifyAboutPlaying, this));
+}
+
+void HTMLMediaElement::notifyAboutPlaying()
+{
+    dispatchEvent(Event::create(eventNames().playingEvent, false, true));
+    resolvePendingPlayPromises();
+}
+
</ins><span class="cx"> void HTMLMediaElement::pendingActionTimerFired()
</span><span class="cx"> {
</span><span class="cx">     Ref&lt;HTMLMediaElement&gt; protect(*this); // loadNextSourceChild may fire 'beforeload', which can make arbitrary DOM mutations.
</span><span class="lines">@@ -1915,6 +1946,8 @@
</span><span class="cx">     // 7 - Queue a task to fire a simple event named error at the media element.
</span><span class="cx">     scheduleEvent(eventNames().errorEvent);
</span><span class="cx"> 
</span><ins>+    rejectPendingPlayPromises(DOMError::create(&quot;NotSupportedError&quot;, &quot;The operation is not supported.&quot;));
+
</ins><span class="cx"> #if ENABLE(MEDIA_SOURCE)
</span><span class="cx">     closeMediaSource();
</span><span class="cx"> #endif
</span><span class="lines">@@ -1979,6 +2012,8 @@
</span><span class="cx"> 
</span><span class="cx">     for (auto&amp; source : childrenOfType&lt;HTMLSourceElement&gt;(*this))
</span><span class="cx">         source.cancelPendingErrorEvent();
</span><ins>+
+    rejectPendingPlayPromises(DOMError::create(&quot;AbortError&quot;, &quot;The operation was aborted.&quot;));
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> void HTMLMediaElement::mediaPlayerNetworkStateChanged(MediaPlayer*)
</span><span class="lines">@@ -2242,7 +2277,7 @@
</span><span class="cx">     if (m_readyState == HAVE_FUTURE_DATA &amp;&amp; oldState &lt;= HAVE_CURRENT_DATA &amp;&amp; tracksAreReady) {
</span><span class="cx">         scheduleEvent(eventNames().canplayEvent);
</span><span class="cx">         if (isPotentiallyPlaying)
</span><del>-            scheduleEvent(eventNames().playingEvent);
</del><ins>+            scheduleNotifyAboutPlaying();
</ins><span class="cx">         shouldUpdateDisplayState = true;
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="lines">@@ -2253,13 +2288,13 @@
</span><span class="cx">         scheduleEvent(eventNames().canplaythroughEvent);
</span><span class="cx"> 
</span><span class="cx">         if (isPotentiallyPlaying &amp;&amp; oldState &lt;= HAVE_CURRENT_DATA)
</span><del>-            scheduleEvent(eventNames().playingEvent);
</del><ins>+            scheduleNotifyAboutPlaying();
</ins><span class="cx"> 
</span><span class="cx">         if (canTransitionFromAutoplayToPlay()) {
</span><span class="cx">             m_paused = false;
</span><span class="cx">             invalidateCachedTime();
</span><span class="cx">             scheduleEvent(eventNames().playEvent);
</span><del>-            scheduleEvent(eventNames().playingEvent);
</del><ins>+            scheduleNotifyAboutPlaying();
</ins><span class="cx">         }
</span><span class="cx"> 
</span><span class="cx">         shouldUpdateDisplayState = true;
</span><span class="lines">@@ -2986,6 +3021,29 @@
</span><span class="cx">     setAttribute(preloadAttr, preload);
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+void HTMLMediaElement::play(PlayPromise&amp;&amp; promise)
+{
+    LOG(Media, &quot;HTMLMediaElement::play(%p)&quot;, this);
+
+    if (!m_mediaSession-&gt;playbackPermitted(*this)) {
+        promise.reject(DOMError::create(&quot;NotAllowedError&quot;, &quot;The request is not allowed by the user agent or the platform in the current context.&quot;));
+        return;
+    }
+
+    if (m_error &amp;&amp; m_error-&gt;code() == MediaError::MEDIA_ERR_SRC_NOT_SUPPORTED) {
+        promise.reject(DOMError::create(&quot;NotSupportedError&quot;, &quot;The operation is not supported..&quot;));
+        return;
+    }
+
+    if (ScriptController::processingUserGestureForMedia())
+        removeBehaviorsRestrictionsAfterFirstUserGesture();
+
+    if (!playInternal())
+        promise.reject(DOMError::create(&quot;NotAllowedError&quot;, &quot;The request is not allowed by the user agent or the platform in the current context.&quot;));
+
+    m_pendingPlayPromises.append(WTFMove(promise));
+}
+
</ins><span class="cx"> void HTMLMediaElement::play()
</span><span class="cx"> {
</span><span class="cx">     LOG(Media, &quot;HTMLMediaElement::play(%p)&quot;, this);
</span><span class="lines">@@ -2998,13 +3056,13 @@
</span><span class="cx">     playInternal();
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-void HTMLMediaElement::playInternal()
</del><ins>+bool HTMLMediaElement::playInternal()
</ins><span class="cx"> {
</span><span class="cx">     LOG(Media, &quot;HTMLMediaElement::playInternal(%p)&quot;, this);
</span><span class="cx">     
</span><span class="cx">     if (!m_mediaSession-&gt;clientWillBeginPlayback()) {
</span><span class="cx">         LOG(Media, &quot;  returning because of interruption&quot;);
</span><del>-        return;
</del><ins>+        return false;
</ins><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     // 4.8.10.9. Playing the media resource
</span><span class="lines">@@ -3025,7 +3083,7 @@
</span><span class="cx">         if (m_readyState &lt;= HAVE_CURRENT_DATA)
</span><span class="cx">             scheduleEvent(eventNames().waitingEvent);
</span><span class="cx">         else if (m_readyState &gt;= HAVE_FUTURE_DATA)
</span><del>-            scheduleEvent(eventNames().playingEvent);
</del><ins>+            scheduleNotifyAboutPlaying();
</ins><span class="cx"> 
</span><span class="cx"> #if ENABLE(MEDIA_SESSION)
</span><span class="cx">         // 6.3 Activating a media session from a media element
</span><span class="lines">@@ -3047,15 +3105,18 @@
</span><span class="cx"> 
</span><span class="cx">                 if (!m_session-&gt;invoke()) {
</span><span class="cx">                     pause();
</span><del>-                    return;
</del><ins>+                    return false;
</ins><span class="cx">                 }
</span><span class="cx">             }
</span><span class="cx">         }
</span><span class="cx"> #endif
</span><del>-    }
</del><ins>+    } else if (m_readyState &gt;= HAVE_FUTURE_DATA)
+        scheduleResolvePendingPlayPromises();
+
</ins><span class="cx">     m_autoplaying = false;
</span><span class="cx">     updatePlayState();
</span><span class="cx">     updateMediaController();
</span><ins>+    return true;
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> void HTMLMediaElement::pause()
</span><span class="lines">@@ -3093,6 +3154,7 @@
</span><span class="cx">         m_paused = true;
</span><span class="cx">         scheduleTimeupdateEvent(false);
</span><span class="cx">         scheduleEvent(eventNames().pauseEvent);
</span><ins>+        rejectPendingPlayPromises(DOMError::create(&quot;AbortError&quot;, &quot;The operation was aborted.&quot;));
</ins><span class="cx"> 
</span><span class="cx">         if (MemoryPressureHandler::singleton().isUnderMemoryPressure())
</span><span class="cx">             purgeBufferedDataIfPossible();
</span><span class="lines">@@ -5005,6 +5067,8 @@
</span><span class="cx">     m_seekTaskQueue.close();
</span><span class="cx">     m_resizeTaskQueue.close();
</span><span class="cx">     m_shadowDOMTaskQueue.close();
</span><ins>+    m_promiseTaskQueue.close();
+    m_pauseAfterDetachedTaskQueue.close();
</ins><span class="cx"> 
</span><span class="cx">     ActiveDOMObject::contextDestroyed();
</span><span class="cx"> }
</span><span class="lines">@@ -5016,6 +5080,7 @@
</span><span class="cx">     stopWithoutDestroyingMediaPlayer();
</span><span class="cx"> 
</span><span class="cx">     m_asyncEventQueue.close();
</span><ins>+    m_promiseTaskQueue.close();
</ins><span class="cx"> 
</span><span class="cx">     // Once an active DOM object has been stopped it can not be restarted, so we can deallocate
</span><span class="cx">     // the media player now. Note that userCancelledLoad will already called clearMediaPlayer
</span></span></pre></div>
<a id="trunkSourceWebCorehtmlHTMLMediaElementh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/html/HTMLMediaElement.h (200637 => 200638)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/html/HTMLMediaElement.h        2016-05-10 20:21:45 UTC (rev 200637)
+++ trunk/Source/WebCore/html/HTMLMediaElement.h        2016-05-10 20:32:02 UTC (rev 200638)
</span><span class="lines">@@ -32,6 +32,7 @@
</span><span class="cx"> #include &quot;GenericEventQueue.h&quot;
</span><span class="cx"> #include &quot;GenericTaskQueue.h&quot;
</span><span class="cx"> #include &quot;HTMLMediaElementEnums.h&quot;
</span><ins>+#include &quot;JSDOMPromise.h&quot;
</ins><span class="cx"> #include &quot;MediaCanStartListener.h&quot;
</span><span class="cx"> #include &quot;MediaControllerInterface.h&quot;
</span><span class="cx"> #include &quot;MediaElementSession.h&quot;
</span><span class="lines">@@ -58,6 +59,7 @@
</span><span class="cx"> class AudioSourceProvider;
</span><span class="cx"> class MediaElementAudioSourceNode;
</span><span class="cx"> #endif
</span><ins>+class DOMError;
</ins><span class="cx"> class DisplaySleepDisabler;
</span><span class="cx"> class Event;
</span><span class="cx"> class HTMLSourceElement;
</span><span class="lines">@@ -141,6 +143,11 @@
</span><span class="cx"> 
</span><span class="cx">     using HTMLMediaElementEnums::DelayedActionType;
</span><span class="cx">     void scheduleDelayedAction(DelayedActionType);
</span><ins>+    void scheduleResolvePendingPlayPromises();
+    void rejectPendingPlayPromises(DOMError&amp;);
+    void resolvePendingPlayPromises();
+    void scheduleNotifyAboutPlaying();
+    void notifyAboutPlaying();
</ins><span class="cx">     
</span><span class="cx">     MediaPlayerEnums::MovieLoadType movieLoadType() const;
</span><span class="cx">     
</span><span class="lines">@@ -204,6 +211,10 @@
</span><span class="cx">     bool isAutoplaying() const { return m_autoplaying; }
</span><span class="cx">     bool loop() const;
</span><span class="cx">     void setLoop(bool b);
</span><ins>+
+    typedef DOMPromise&lt;std::nullptr_t, DOMError&amp;&gt; PlayPromise;
+    void play(PlayPromise&amp;&amp;);
+
</ins><span class="cx">     WEBCORE_EXPORT void play() override;
</span><span class="cx">     WEBCORE_EXPORT void pause() override;
</span><span class="cx">     void setShouldBufferData(bool) override;
</span><span class="lines">@@ -671,7 +682,7 @@
</span><span class="cx"> 
</span><span class="cx">     // These &quot;internal&quot; functions do not check user gesture restrictions.
</span><span class="cx">     void loadInternal();
</span><del>-    void playInternal();
</del><ins>+    bool playInternal();
</ins><span class="cx">     void pauseInternal();
</span><span class="cx"> 
</span><span class="cx">     void prepareForLoad();
</span><span class="lines">@@ -772,19 +783,22 @@
</span><span class="cx">     void isVisibleInViewportChanged() final;
</span><span class="cx">     void updateShouldAutoplay();
</span><span class="cx"> 
</span><del>-    void pauseAfterDetachedTimerFired();
</del><ins>+    void pauseAfterDetachedTask();
</ins><span class="cx"> 
</span><span class="cx">     Timer m_pendingActionTimer;
</span><span class="cx">     Timer m_progressEventTimer;
</span><span class="cx">     Timer m_playbackProgressTimer;
</span><span class="cx">     Timer m_scanTimer;
</span><del>-    Timer m_pauseAfterDetachedTimer;
-    GenericTaskQueue&lt;ScriptExecutionContext&gt; m_seekTaskQueue;
-    GenericTaskQueue&lt;ScriptExecutionContext&gt; m_resizeTaskQueue;
-    GenericTaskQueue&lt;ScriptExecutionContext&gt; m_shadowDOMTaskQueue;
</del><ins>+    GenericTaskQueue&lt;Timer&gt; m_seekTaskQueue;
+    GenericTaskQueue&lt;Timer&gt; m_resizeTaskQueue;
+    GenericTaskQueue&lt;Timer&gt; m_shadowDOMTaskQueue;
+    GenericTaskQueue&lt;Timer&gt; m_promiseTaskQueue;
+    GenericTaskQueue&lt;Timer&gt; m_pauseAfterDetachedTaskQueue;
</ins><span class="cx">     RefPtr&lt;TimeRanges&gt; m_playedTimeRanges;
</span><span class="cx">     GenericEventQueue m_asyncEventQueue;
</span><span class="cx"> 
</span><ins>+    Vector&lt;PlayPromise&gt; m_pendingPlayPromises;
+
</ins><span class="cx">     double m_requestedPlaybackRate;
</span><span class="cx">     double m_reportedPlaybackRate;
</span><span class="cx">     double m_defaultPlaybackRate;
</span></span></pre></div>
<a id="trunkSourceWebCorehtmlHTMLMediaElementidl"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/html/HTMLMediaElement.idl (200637 => 200638)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/html/HTMLMediaElement.idl        2016-05-10 20:21:45 UTC (rev 200637)
+++ trunk/Source/WebCore/html/HTMLMediaElement.idl        2016-05-10 20:32:02 UTC (rev 200638)
</span><span class="lines">@@ -75,7 +75,7 @@
</span><span class="cx">     readonly attribute boolean ended;
</span><span class="cx">     [Reflect] attribute boolean autoplay;
</span><span class="cx">     [Reflect] attribute boolean loop;
</span><del>-    void play();
</del><ins>+    Promise play();
</ins><span class="cx">     void pause();
</span><span class="cx">     void fastSeek(unrestricted double time);
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformGenericTaskQueuecpp"></a>
<div class="addfile"><h4>Added: trunk/Source/WebCore/platform/GenericTaskQueue.cpp (0 => 200638)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/GenericTaskQueue.cpp                                (rev 0)
+++ trunk/Source/WebCore/platform/GenericTaskQueue.cpp        2016-05-10 20:32:02 UTC (rev 200638)
</span><span class="lines">@@ -0,0 +1,85 @@
</span><ins>+/*
+ * 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 &quot;config.h&quot;
+#include &quot;GenericTaskQueue.h&quot;
+
+#include &lt;wtf/MainThread.h&gt;
+#include &lt;wtf/NeverDestroyed.h&gt;
+
+namespace WebCore {
+
+TaskDispatcher&lt;Timer&gt;::TaskDispatcher()
+    : m_weakPtrFactory(this)
+{
+}
+
+void TaskDispatcher&lt;Timer&gt;::postTask(std::function&lt;void()&gt; function)
+{
+    m_pendingTasks.append(WTFMove(function));
+    pendingDispatchers().append(m_weakPtrFactory.createWeakPtr());
+    if (!sharedTimer().isActive())
+        sharedTimer().startOneShot(0);
+}
+
+Timer&amp; TaskDispatcher&lt;Timer&gt;::sharedTimer()
+{
+    ASSERT(isMainThread());
+    static NeverDestroyed&lt;Timer&gt; timer(TaskDispatcher&lt;Timer&gt;::sharedTimerFired);
+    return timer.get();
+}
+
+void TaskDispatcher&lt;Timer&gt;::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&lt;WeakPtr&lt;TaskDispatcher&lt;Timer&gt;&gt;&gt; queuedDispatchers = WTFMove(pendingDispatchers());
+    while (!queuedDispatchers.isEmpty()) {
+        WeakPtr&lt;TaskDispatcher&lt;Timer&gt;&gt; dispatcher = queuedDispatchers.takeFirst();
+        if (!dispatcher)
+            continue;
+        dispatcher-&gt;dispatchOneTask();
+    }
+}
+
+Deque&lt;WeakPtr&lt;TaskDispatcher&lt;Timer&gt;&gt;&gt;&amp; TaskDispatcher&lt;Timer&gt;::pendingDispatchers()
+{
+    ASSERT(isMainThread());
+    static NeverDestroyed&lt;Deque&lt;WeakPtr&lt;TaskDispatcher&lt;Timer&gt;&gt;&gt;&gt; dispatchers;
+    return dispatchers.get();
+}
+
+void TaskDispatcher&lt;Timer&gt;::dispatchOneTask()
+{
+    ASSERT(!m_pendingTasks.isEmpty());
+    std::function&lt;void()&gt; task = m_pendingTasks.takeFirst();
+    task();
+}
+
+}
+
</ins></span></pre></div>
<a id="trunkSourceWebCoreplatformGenericTaskQueueh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/GenericTaskQueue.h (200637 => 200638)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/GenericTaskQueue.h        2016-05-10 20:21:45 UTC (rev 200637)
+++ trunk/Source/WebCore/platform/GenericTaskQueue.h        2016-05-10 20:32:02 UTC (rev 200638)
</span><span class="lines">@@ -52,27 +52,18 @@
</span><span class="cx"> template&lt;&gt;
</span><span class="cx"> class TaskDispatcher&lt;Timer&gt; {
</span><span class="cx"> public:
</span><del>-    TaskDispatcher()
-        : m_timer(*this, &amp;TaskDispatcher&lt;Timer&gt;::timerFired)
-    {
-    }
</del><ins>+    TaskDispatcher();
+    void postTask(std::function&lt;void()&gt;);
</ins><span class="cx"> 
</span><del>-    void postTask(std::function&lt;void()&gt; function)
-    {
-        m_queue.append(function);
-        m_timer.startOneShot(0);
-    }
</del><ins>+private:
+    static Timer&amp; sharedTimer();
+    static void sharedTimerFired();
+    static Deque&lt;WeakPtr&lt;TaskDispatcher&lt;Timer&gt;&gt;&gt;&amp; pendingDispatchers();
</ins><span class="cx"> 
</span><del>-    void timerFired()
-    {
-        Deque&lt;std::function&lt;void()&gt;&gt; queue;
-        queue.swap(m_queue);
-        for (std::function&lt;void()&gt;&amp; function : queue)
-            function();
-    }
</del><ins>+    void dispatchOneTask();
</ins><span class="cx"> 
</span><del>-    Timer m_timer;
-    Deque&lt;std::function&lt;void()&gt;&gt; m_queue;
</del><ins>+    WeakPtrFactory&lt;TaskDispatcher&gt; m_weakPtrFactory;
+    Deque&lt;std::function&lt;void()&gt;&gt; m_pendingTasks;
</ins><span class="cx"> };
</span><span class="cx"> 
</span><span class="cx"> template &lt;typename T&gt;
</span></span></pre>
</div>
</div>

</body>
</html>