<!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>[200431] 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/200431">200431</a></dd>
<dt>Author</dt> <dd>cdumez@apple.com</dd>
<dt>Date</dt> <dd>2016-05-04 14:33:45 -0700 (Wed, 04 May 2016)</dd>
</dl>

<h3>Log Message</h3>
<pre>Media elements should not be paused right away when removed from the document
https://bugs.webkit.org/show_bug.cgi?id=157347
&lt;rdar://problem/25888758&gt;

Reviewed by Alex Christensen.

LayoutTests/imported/w3c:

Rebaseline now that more W3C tests are passing.

* web-platform-tests/html/semantics/embedded-content/media-elements/playing-the-media-resource/pause-move-to-other-document-expected.txt:
* web-platform-tests/html/semantics/embedded-content/media-elements/playing-the-media-resource/pause-move-within-document-expected.txt:
* web-platform-tests/html/semantics/embedded-content/media-elements/playing-the-media-resource/pause-remove-from-document-expected.txt:

Source/WebCore:

Media elements should not be paused right away when removed from the document.
Instead we should allow the task that removed the media element from the
document to finish because considering pausing.

This avoid inadvertently pausing media elements when the JS merely moves them
on the page (e.g. using Node.replaceChild()).

Text from the HTML specification:
&quot;&quot;&quot;
When a media element is removed from a Document, the user agent must run the
following steps:
1.  Await a stable state, allowing the task that removed the media element
    from the Document to continue. The synchronous section consists of all the
    remaining steps of this algorithm. (Steps in the synchronous section are
    marked with ⌛.)
2. ⌛ If the media element is in a Document, abort these steps.
3. ⌛ Run the internal pause steps for the media element.
&quot;&quot;&quot;

c.f. https://html.spec.whatwg.org/multipage/embedded-content.html#htmlmediaelement

Test: media/replaceChild-should-not-pause-video.html

* dom/GenericEventQueue.cpp:
(WebCore::GenericEventQueue::sharedTimerFired):
Copy the queue of events before processing it so that we don't fire events that
get scheduled by the event handlers as a result of us firing the pending events.
Otherwise, we end up firing events synchronously right after they've been queued,
which is wrong. This was causing several W3C tests to fail.

* html/HTMLMediaElement.cpp:
(WebCore::HTMLMediaElement::HTMLMediaElement):
(WebCore::HTMLMediaElement::pauseAfterDetachedTimerFired):
(WebCore::HTMLMediaElement::removedFrom):
* html/HTMLMediaElement.h:
After the media element gets removed from the document, schedule a 0 timer before
pausing the media element, to give the task that removed us a chance to finish.
When the timer fires, we check if we were added back into an active document and
avoid pausing in such case.

LayoutTests:

* media/remove-from-document-expected.txt:
* media/remove-from-document.html:
Check asynchronously if the video has been paused after removing it from
the document instead of synchronously as we no longer pause the video
synchronously in this case.

* media/replaceChild-should-not-pause-video-expected.txt: Added.
* media/replaceChild-should-not-pause-video.html: Added.
Add test case to make sure that calling replaceChild() on with a video
element as newChild does not pause the video if it is already playing.
This is a regression test for &lt;rdar://problem/25888758&gt;.

* webaudio/audiocontext-state-interrupted-expected.txt:
* webaudio/audiocontext-state-interrupted.html:
Add a missing call to
internals.setMediaSessionRestrictions(&quot;WebAudio&quot;, &quot;InterruptedPlaybackNotPermitted&quot;)
before the fourth test. The fourth test was previously passing by
chance, due to a bug in GenericEventQueue sometimes firing events
synchronously after they are scheduled.</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="#trunkLayoutTestsimportedw3cwebplatformtestshtmlsemanticsembeddedcontentmediaelementsplayingthemediaresourcepausemovetootherdocumentexpectedtxt">trunk/LayoutTests/imported/w3c/web-platform-tests/html/semantics/embedded-content/media-elements/playing-the-media-resource/pause-move-to-other-document-expected.txt</a></li>
<li><a href="#trunkLayoutTestsimportedw3cwebplatformtestshtmlsemanticsembeddedcontentmediaelementsplayingthemediaresourcepausemovewithindocumentexpectedtxt">trunk/LayoutTests/imported/w3c/web-platform-tests/html/semantics/embedded-content/media-elements/playing-the-media-resource/pause-move-within-document-expected.txt</a></li>
<li><a href="#trunkLayoutTestsimportedw3cwebplatformtestshtmlsemanticsembeddedcontentmediaelementsplayingthemediaresourcepauseremovefromdocumentexpectedtxt">trunk/LayoutTests/imported/w3c/web-platform-tests/html/semantics/embedded-content/media-elements/playing-the-media-resource/pause-remove-from-document-expected.txt</a></li>
<li><a href="#trunkLayoutTestsmediaremovefromdocumentexpectedtxt">trunk/LayoutTests/media/remove-from-document-expected.txt</a></li>
<li><a href="#trunkLayoutTestsmediaremovefromdocumenthtml">trunk/LayoutTests/media/remove-from-document.html</a></li>
<li><a href="#trunkLayoutTestswebaudioaudiocontextstateinterruptedexpectedtxt">trunk/LayoutTests/webaudio/audiocontext-state-interrupted-expected.txt</a></li>
<li><a href="#trunkLayoutTestswebaudioaudiocontextstateinterruptedhtml">trunk/LayoutTests/webaudio/audiocontext-state-interrupted.html</a></li>
<li><a href="#trunkSourceWebCoreChangeLog">trunk/Source/WebCore/ChangeLog</a></li>
<li><a href="#trunkSourceWebCoredomGenericEventQueuecpp">trunk/Source/WebCore/dom/GenericEventQueue.cpp</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>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsmediareplaceChildshouldnotpausevideoexpectedtxt">trunk/LayoutTests/media/replaceChild-should-not-pause-video-expected.txt</a></li>
<li><a href="#trunkLayoutTestsmediareplaceChildshouldnotpausevideohtml">trunk/LayoutTests/media/replaceChild-should-not-pause-video.html</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkLayoutTestsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/ChangeLog (200430 => 200431)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/ChangeLog        2016-05-04 21:21:36 UTC (rev 200430)
+++ trunk/LayoutTests/ChangeLog        2016-05-04 21:33:45 UTC (rev 200431)
</span><span class="lines">@@ -1,3 +1,31 @@
</span><ins>+2016-05-04  Chris Dumez  &lt;cdumez@apple.com&gt;
+
+        Media elements should not be paused right away when removed from the document
+        https://bugs.webkit.org/show_bug.cgi?id=157347
+        &lt;rdar://problem/25888758&gt;
+
+        Reviewed by Alex Christensen.
+
+        * media/remove-from-document-expected.txt:
+        * media/remove-from-document.html:
+        Check asynchronously if the video has been paused after removing it from
+        the document instead of synchronously as we no longer pause the video
+        synchronously in this case.
+
+        * media/replaceChild-should-not-pause-video-expected.txt: Added.
+        * media/replaceChild-should-not-pause-video.html: Added.
+        Add test case to make sure that calling replaceChild() on with a video
+        element as newChild does not pause the video if it is already playing.
+        This is a regression test for &lt;rdar://problem/25888758&gt;.
+
+        * webaudio/audiocontext-state-interrupted-expected.txt:
+        * webaudio/audiocontext-state-interrupted.html:
+        Add a missing call to
+        internals.setMediaSessionRestrictions(&quot;WebAudio&quot;, &quot;InterruptedPlaybackNotPermitted&quot;)
+        before the fourth test. The fourth test was previously passing by
+        chance, due to a bug in GenericEventQueue sometimes firing events
+        synchronously after they are scheduled.
+
</ins><span class="cx"> 2016-05-04  Filip Pizlo  &lt;fpizlo@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         Speed up JSGlobalObject initialization by making some properties lazy
</span></span></pre></div>
<a id="trunkLayoutTestsimportedw3cChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/imported/w3c/ChangeLog (200430 => 200431)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/imported/w3c/ChangeLog        2016-05-04 21:21:36 UTC (rev 200430)
+++ trunk/LayoutTests/imported/w3c/ChangeLog        2016-05-04 21:33:45 UTC (rev 200431)
</span><span class="lines">@@ -1,3 +1,17 @@
</span><ins>+2016-05-04  Chris Dumez  &lt;cdumez@apple.com&gt;
+
+        Media elements should not be paused right away when removed from the document
+        https://bugs.webkit.org/show_bug.cgi?id=157347
+        &lt;rdar://problem/25888758&gt;
+
+        Reviewed by Alex Christensen.
+
+        Rebaseline now that more W3C tests are passing.
+
+        * web-platform-tests/html/semantics/embedded-content/media-elements/playing-the-media-resource/pause-move-to-other-document-expected.txt:
+        * web-platform-tests/html/semantics/embedded-content/media-elements/playing-the-media-resource/pause-move-within-document-expected.txt:
+        * web-platform-tests/html/semantics/embedded-content/media-elements/playing-the-media-resource/pause-remove-from-document-expected.txt:
+
</ins><span class="cx"> 2016-05-04  Joseph Pecoraro  &lt;pecoraro@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         Improve the grammar of some error messages 'a argument list' =&gt; 'an argument list'
</span></span></pre></div>
<a id="trunkLayoutTestsimportedw3cwebplatformtestshtmlsemanticsembeddedcontentmediaelementsplayingthemediaresourcepausemovetootherdocumentexpectedtxt"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/imported/w3c/web-platform-tests/html/semantics/embedded-content/media-elements/playing-the-media-resource/pause-move-to-other-document-expected.txt (200430 => 200431)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/imported/w3c/web-platform-tests/html/semantics/embedded-content/media-elements/playing-the-media-resource/pause-move-to-other-document-expected.txt        2016-05-04 21:21:36 UTC (rev 200430)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/html/semantics/embedded-content/media-elements/playing-the-media-resource/pause-move-to-other-document-expected.txt        2016-05-04 21:33:45 UTC (rev 200431)
</span><span class="lines">@@ -1,3 +1,3 @@
</span><span class="cx"> 
</span><del>-FAIL paused state when moving to other document assert_false: paused after moving expected false got true
</del><ins>+PASS paused state when moving to other document 
</ins><span class="cx"> 
</span></span></pre></div>
<a id="trunkLayoutTestsimportedw3cwebplatformtestshtmlsemanticsembeddedcontentmediaelementsplayingthemediaresourcepausemovewithindocumentexpectedtxt"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/imported/w3c/web-platform-tests/html/semantics/embedded-content/media-elements/playing-the-media-resource/pause-move-within-document-expected.txt (200430 => 200431)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/imported/w3c/web-platform-tests/html/semantics/embedded-content/media-elements/playing-the-media-resource/pause-move-within-document-expected.txt        2016-05-04 21:21:36 UTC (rev 200430)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/html/semantics/embedded-content/media-elements/playing-the-media-resource/pause-move-within-document-expected.txt        2016-05-04 21:33:45 UTC (rev 200431)
</span><span class="lines">@@ -1,3 +1,3 @@
</span><span class="cx"> 
</span><del>-FAIL paused state when moving within a document assert_false: paused after moving expected false got true
</del><ins>+PASS paused state when moving within a document 
</ins><span class="cx"> 
</span></span></pre></div>
<a id="trunkLayoutTestsimportedw3cwebplatformtestshtmlsemanticsembeddedcontentmediaelementsplayingthemediaresourcepauseremovefromdocumentexpectedtxt"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/imported/w3c/web-platform-tests/html/semantics/embedded-content/media-elements/playing-the-media-resource/pause-remove-from-document-expected.txt (200430 => 200431)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/imported/w3c/web-platform-tests/html/semantics/embedded-content/media-elements/playing-the-media-resource/pause-remove-from-document-expected.txt        2016-05-04 21:21:36 UTC (rev 200430)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/html/semantics/embedded-content/media-elements/playing-the-media-resource/pause-remove-from-document-expected.txt        2016-05-04 21:33:45 UTC (rev 200431)
</span><span class="lines">@@ -1,3 +1,3 @@
</span><span class="cx"> 
</span><del>-FAIL paused state when removing from a document assert_false: paused after removing expected false got true
</del><ins>+PASS paused state when removing from a document 
</ins><span class="cx"> 
</span></span></pre></div>
<a id="trunkLayoutTestsmediaremovefromdocumentexpectedtxt"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/media/remove-from-document-expected.txt (200430 => 200431)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/media/remove-from-document-expected.txt        2016-05-04 21:21:36 UTC (rev 200430)
+++ trunk/LayoutTests/media/remove-from-document-expected.txt        2016-05-04 21:33:45 UTC (rev 200431)
</span><span class="lines">@@ -5,6 +5,8 @@
</span><span class="cx"> EXPECTED (video.paused == 'false') OK
</span><span class="cx"> RUN(document.body.removeChild(video))
</span><span class="cx"> EXPECTED (video.networkState != '0') OK
</span><ins>+EXPECTED (video.paused == 'false') OK
+EXPECTED (video.networkState != '0') OK
</ins><span class="cx"> EXPECTED (video.paused == 'true') OK
</span><span class="cx"> END OF TEST
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkLayoutTestsmediaremovefromdocumenthtml"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/media/remove-from-document.html (200430 => 200431)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/media/remove-from-document.html        2016-05-04 21:21:36 UTC (rev 200430)
+++ trunk/LayoutTests/media/remove-from-document.html        2016-05-04 21:33:45 UTC (rev 200431)
</span><span class="lines">@@ -19,10 +19,15 @@
</span><span class="cx">             run(&quot;document.body.removeChild(video)&quot;);
</span><span class="cx">     
</span><span class="cx">             testExpected(&quot;video.networkState&quot;, HTMLMediaElement.NETWORK_EMPTY, &quot;!=&quot;);
</span><del>-            testExpected(&quot;video.paused&quot;, true);
-    
-            document.body.offsetTop;
-            endTest();
</del><ins>+            testExpected(&quot;video.paused&quot;, false);
+
+            setTimeout(function() {
+                testExpected(&quot;video.networkState&quot;, HTMLMediaElement.NETWORK_EMPTY, &quot;!=&quot;);
+                testExpected(&quot;video.paused&quot;, true);
+
+                document.body.offsetTop;
+                endTest();
+            }, 0);
</ins><span class="cx">         }
</span><span class="cx"> 
</span><span class="cx">         window.addEventListener('load', doSetup, false);
</span></span></pre></div>
<a id="trunkLayoutTestsmediareplaceChildshouldnotpausevideoexpectedtxt"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/media/replaceChild-should-not-pause-video-expected.txt (0 => 200431)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/media/replaceChild-should-not-pause-video-expected.txt                                (rev 0)
+++ trunk/LayoutTests/media/replaceChild-should-not-pause-video-expected.txt        2016-05-04 21:33:45 UTC (rev 200431)
</span><span class="lines">@@ -0,0 +1,17 @@
</span><ins>+Test that a video element does not get paused when moved using replaceChild().
+
+On success, you will see a series of &quot;PASS&quot; messages, followed by &quot;TEST COMPLETE&quot;.
+
+
+EVENT(canplay)
+video.play()
+EVENT(playing)
+PASS video.paused is false
+parentA.replaceChild(video, childToReplace)
+PASS video.paused is false
+PASS video.paused is false
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
+
</ins></span></pre></div>
<a id="trunkLayoutTestsmediareplaceChildshouldnotpausevideohtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/media/replaceChild-should-not-pause-video.html (0 => 200431)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/media/replaceChild-should-not-pause-video.html                                (rev 0)
+++ trunk/LayoutTests/media/replaceChild-should-not-pause-video.html        2016-05-04 21:33:45 UTC (rev 200431)
</span><span class="lines">@@ -0,0 +1,58 @@
</span><ins>+&lt;!DOCTYPE html&gt;
+&lt;html&gt;
+&lt;head&gt;
+&lt;script src=&quot;../resources/js-test-pre.js&quot;&gt;&lt;/script&gt;
+&lt;script src=&quot;media-file.js&quot;&gt;&lt;/script&gt;
+&lt;/head&gt;
+&lt;body onload=&quot;start()&quot;&gt;
+&lt;div id=&quot;parentA&quot;&gt;
+    &lt;div id=&quot;childToReplace&quot;&gt;&lt;/div&gt;
+&lt;/div&gt;
+&lt;div id=&quot;parentB&quot;&gt;
+    &lt;video id=&quot;vid&quot;&gt;&lt;/video&gt;
+&lt;/div&gt;
+
+&lt;script&gt;
+description(&quot;Test that a video element does not get paused when moved using replaceChild().&quot;);
+jsTestIsAsync = true;
+
+var video = document.getElementsByTagName('video')[0];
+
+function paused()
+{
+    testFailed(&quot;Video was paused&quot;);
+}
+
+video.addEventListener(&quot;pause&quot;, paused);
+
+function playing()
+{
+    debug(&quot;EVENT(playing)&quot;);
+    shouldBeFalse(&quot;video.paused&quot;);
+    parentA = document.getElementById(&quot;parentA&quot;);
+    childToReplace = document.getElementById(&quot;childToReplace&quot;);
+    evalAndLog(&quot;parentA.replaceChild(video, childToReplace)&quot;);
+    shouldBeFalse(&quot;video.paused&quot;);
+    setTimeout(function() {
+        shouldBeFalse(&quot;video.paused&quot;);
+        finishJSTest();
+    }, 0);
+}
+
+function canplay()
+{
+    debug(&quot;EVENT(canplay)&quot;);
+    video.addEventListener(&quot;playing&quot;, playing);
+    evalAndLog(&quot;video.play()&quot;);
+}
+
+function start()
+{
+    video.addEventListener(&quot;canplay&quot;, canplay);
+    video.src = findMediaFile(&quot;video&quot;, &quot;content/test&quot;);
+}
+&lt;/script&gt;
+&lt;script src=&quot;../resources/js-test-post.js&quot;&gt;&lt;/script&gt;
+&lt;/body&gt;
+&lt;/html&gt;
+
</ins></span></pre></div>
<a id="trunkLayoutTestswebaudioaudiocontextstateinterruptedexpectedtxt"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/webaudio/audiocontext-state-interrupted-expected.txt (200430 => 200431)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/webaudio/audiocontext-state-interrupted-expected.txt        2016-05-04 21:21:36 UTC (rev 200430)
+++ trunk/LayoutTests/webaudio/audiocontext-state-interrupted-expected.txt        2016-05-04 21:33:45 UTC (rev 200431)
</span><span class="lines">@@ -37,6 +37,7 @@
</span><span class="cx"> PASS context.state is &quot;suspended&quot;
</span><span class="cx"> 
</span><span class="cx"> Test 4: resume() while interrupted will not resume playback after an interruption.
</span><ins>+internals.setMediaSessionRestrictions(&quot;WebAudio&quot;, &quot;InterruptedPlaybackNotPermitted&quot;)
</ins><span class="cx"> internals.beginMediaSessionInterruption(&quot;System&quot;)
</span><span class="cx"> EVENT statechange
</span><span class="cx"> PASS context.state is &quot;interrupted&quot;
</span></span></pre></div>
<a id="trunkLayoutTestswebaudioaudiocontextstateinterruptedhtml"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/webaudio/audiocontext-state-interrupted.html (200430 => 200431)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/webaudio/audiocontext-state-interrupted.html        2016-05-04 21:21:36 UTC (rev 200430)
+++ trunk/LayoutTests/webaudio/audiocontext-state-interrupted.html        2016-05-04 21:33:45 UTC (rev 200431)
</span><span class="lines">@@ -117,6 +117,9 @@
</span><span class="cx">     debug('');
</span><span class="cx">     debug('Test 4: resume() while interrupted will not resume playback after an interruption.')
</span><span class="cx"> 
</span><ins>+    if (window.internals)
+        evalAndLog('internals.setMediaSessionRestrictions(&quot;WebAudio&quot;, &quot;InterruptedPlaybackNotPermitted&quot;)');
+
</ins><span class="cx">     context.onstatechange = fourthInterruptionStarted;
</span><span class="cx">     if (window.internals)
</span><span class="cx">         evalAndLog('internals.beginMediaSessionInterruption(&quot;System&quot;)');
</span></span></pre></div>
<a id="trunkSourceWebCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/ChangeLog (200430 => 200431)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/ChangeLog        2016-05-04 21:21:36 UTC (rev 200430)
+++ trunk/Source/WebCore/ChangeLog        2016-05-04 21:33:45 UTC (rev 200431)
</span><span class="lines">@@ -1,3 +1,51 @@
</span><ins>+2016-05-04  Chris Dumez  &lt;cdumez@apple.com&gt;
+
+        Media elements should not be paused right away when removed from the document
+        https://bugs.webkit.org/show_bug.cgi?id=157347
+        &lt;rdar://problem/25888758&gt;
+
+        Reviewed by Alex Christensen.
+
+        Media elements should not be paused right away when removed from the document.
+        Instead we should allow the task that removed the media element from the
+        document to finish because considering pausing.
+
+        This avoid inadvertently pausing media elements when the JS merely moves them
+        on the page (e.g. using Node.replaceChild()).
+
+        Text from the HTML specification:
+        &quot;&quot;&quot;
+        When a media element is removed from a Document, the user agent must run the
+        following steps:
+        1.  Await a stable state, allowing the task that removed the media element
+            from the Document to continue. The synchronous section consists of all the
+            remaining steps of this algorithm. (Steps in the synchronous section are
+            marked with ⌛.)
+        2. ⌛ If the media element is in a Document, abort these steps.
+        3. ⌛ Run the internal pause steps for the media element.
+        &quot;&quot;&quot;
+
+        c.f. https://html.spec.whatwg.org/multipage/embedded-content.html#htmlmediaelement
+
+        Test: media/replaceChild-should-not-pause-video.html
+
+        * dom/GenericEventQueue.cpp:
+        (WebCore::GenericEventQueue::sharedTimerFired):
+        Copy the queue of events before processing it so that we don't fire events that
+        get scheduled by the event handlers as a result of us firing the pending events.
+        Otherwise, we end up firing events synchronously right after they've been queued,
+        which is wrong. This was causing several W3C tests to fail.
+
+        * html/HTMLMediaElement.cpp:
+        (WebCore::HTMLMediaElement::HTMLMediaElement):
+        (WebCore::HTMLMediaElement::pauseAfterDetachedTimerFired):
+        (WebCore::HTMLMediaElement::removedFrom):
+        * html/HTMLMediaElement.h:
+        After the media element gets removed from the document, schedule a 0 timer before
+        pausing the media element, to give the task that removed us a chance to finish.
+        When the timer fires, we check if we were added back into an active document and
+        avoid pausing in such case.
+
</ins><span class="cx"> 2016-05-04  Filip Pizlo  &lt;fpizlo@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         Speed up JSGlobalObject initialization by making some properties lazy
</span></span></pre></div>
<a id="trunkSourceWebCoredomGenericEventQueuecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/dom/GenericEventQueue.cpp (200430 => 200431)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/dom/GenericEventQueue.cpp        2016-05-04 21:21:36 UTC (rev 200430)
+++ trunk/Source/WebCore/dom/GenericEventQueue.cpp        2016-05-04 21:33:45 UTC (rev 200431)
</span><span class="lines">@@ -75,15 +75,16 @@
</span><span class="cx">     ASSERT(!sharedTimer().isActive());
</span><span class="cx">     ASSERT(!pendingQueues().isEmpty());
</span><span class="cx"> 
</span><del>-    while (!pendingQueues().isEmpty()) {
-        WeakPtr&lt;GenericEventQueue&gt; queue = pendingQueues().takeFirst();
</del><ins>+    // 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();
</ins><span class="cx">         if (!queue)
</span><span class="cx">             continue;
</span><span class="cx">         queue-&gt;dispatchOneEvent();
</span><span class="cx">     }
</span><del>-
-    if (sharedTimer().isActive())
-        sharedTimer().stop();
</del><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> Deque&lt;WeakPtr&lt;GenericEventQueue&gt;&gt;&amp; GenericEventQueue::pendingQueues()
</span></span></pre></div>
<a id="trunkSourceWebCorehtmlHTMLMediaElementcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/html/HTMLMediaElement.cpp (200430 => 200431)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/html/HTMLMediaElement.cpp        2016-05-04 21:21:36 UTC (rev 200430)
+++ trunk/Source/WebCore/html/HTMLMediaElement.cpp        2016-05-04 21:33:45 UTC (rev 200431)
</span><span class="lines">@@ -348,6 +348,7 @@
</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><ins>+    , m_pauseAfterDetachedTimer(*this, &amp;HTMLMediaElement::pauseAfterDetachedTimerFired)
</ins><span class="cx">     , m_seekTaskQueue(document)
</span><span class="cx">     , m_resizeTaskQueue(document)
</span><span class="cx">     , m_shadowDOMTaskQueue(document)
</span><span class="lines">@@ -783,32 +784,43 @@
</span><span class="cx">     return InsertionDone;
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+void HTMLMediaElement::pauseAfterDetachedTimerFired()
+{
+    // If we were re-inserted into an active document, no need to pause.
+    if (m_inActiveDocument)
+        return;
+
+    if (hasMediaControls())
+        mediaControls()-&gt;hide();
+    if (m_networkState &gt; NETWORK_EMPTY)
+        pause();
+    if (m_videoFullscreenMode != VideoFullscreenModeNone)
+        exitFullscreen();
+
+    if (!m_player)
+        return;
+
+    size_t extraMemoryCost = m_player-&gt;extraMemoryCost();
+    if (extraMemoryCost &gt; m_reportedExtraMemoryCost) {
+        JSC::VM&amp; vm = JSDOMWindowBase::commonVM();
+        JSC::JSLockHolder lock(vm);
+
+        size_t extraMemoryCostDelta = extraMemoryCost - m_reportedExtraMemoryCost;
+        m_reportedExtraMemoryCost = extraMemoryCost;
+        // FIXME: Adopt reportExtraMemoryVisited, and switch to reportExtraMemoryAllocated.
+        // https://bugs.webkit.org/show_bug.cgi?id=142595
+        vm.heap.deprecatedReportExtraMemory(extraMemoryCostDelta);
+    }
+}
+
</ins><span class="cx"> void HTMLMediaElement::removedFrom(ContainerNode&amp; insertionPoint)
</span><span class="cx"> {
</span><span class="cx">     LOG(Media, &quot;HTMLMediaElement::removedFrom(%p)&quot;, this);
</span><span class="cx"> 
</span><span class="cx">     m_inActiveDocument = false;
</span><span class="cx">     if (insertionPoint.inDocument()) {
</span><del>-        if (hasMediaControls())
-            mediaControls()-&gt;hide();
-        if (m_networkState &gt; NETWORK_EMPTY)
-            pause();
-        if (m_videoFullscreenMode != VideoFullscreenModeNone)
-            exitFullscreen();
-
-        if (m_player) {
-            size_t extraMemoryCost = m_player-&gt;extraMemoryCost();
-            if (extraMemoryCost &gt; m_reportedExtraMemoryCost) {
-                JSC::VM&amp; vm = JSDOMWindowBase::commonVM();
-                JSC::JSLockHolder lock(vm);
-
-                size_t extraMemoryCostDelta = extraMemoryCost - m_reportedExtraMemoryCost;
-                m_reportedExtraMemoryCost = extraMemoryCost;
-                // FIXME: Adopt reportExtraMemoryVisited, and switch to reportExtraMemoryAllocated.
-                // https://bugs.webkit.org/show_bug.cgi?id=142595
-                vm.heap.deprecatedReportExtraMemory(extraMemoryCostDelta);
-            }
-        }
</del><ins>+        // Pause asynchronously to let the operation that removed us finish, in case we get inserted back into a document.
+        m_pauseAfterDetachedTimer.startOneShot(0);
</ins><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     HTMLElement::removedFrom(insertionPoint);
</span></span></pre></div>
<a id="trunkSourceWebCorehtmlHTMLMediaElementh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/html/HTMLMediaElement.h (200430 => 200431)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/html/HTMLMediaElement.h        2016-05-04 21:21:36 UTC (rev 200430)
+++ trunk/Source/WebCore/html/HTMLMediaElement.h        2016-05-04 21:33:45 UTC (rev 200431)
</span><span class="lines">@@ -772,10 +772,13 @@
</span><span class="cx">     void isVisibleInViewportChanged() final;
</span><span class="cx">     void updateShouldAutoplay();
</span><span class="cx"> 
</span><ins>+    void pauseAfterDetachedTimerFired();
+
</ins><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><ins>+    Timer m_pauseAfterDetachedTimer;
</ins><span class="cx">     GenericTaskQueue&lt;ScriptExecutionContext&gt; m_seekTaskQueue;
</span><span class="cx">     GenericTaskQueue&lt;ScriptExecutionContext&gt; m_resizeTaskQueue;
</span><span class="cx">     GenericTaskQueue&lt;ScriptExecutionContext&gt; m_shadowDOMTaskQueue;
</span></span></pre>
</div>
</div>

</body>
</html>