<!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>[182901] 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/182901">182901</a></dd>
<dt>Author</dt> <dd>cdumez@apple.com</dd>
<dt>Date</dt> <dd>2015-04-16 12:39:50 -0700 (Thu, 16 Apr 2015)</dd>
</dl>

<h3>Log Message</h3>
<pre>Regression(<a href="http://trac.webkit.org/projects/webkit/changeset/182517">r182517</a>): WebSocket::suspend() causes error event to be fired
https://bugs.webkit.org/show_bug.cgi?id=143806
&lt;rdar://problem/20559812&gt;

Reviewed by Alexey Proskuryakov.

Source/WebCore:

WebSocket::suspend() causes an error event to be fired after <a href="http://trac.webkit.org/projects/webkit/changeset/182517">r182517</a>.
This is not allowed as firing the event could trigger arbitrary JS
execution, which is no longer allowed at this point.

This patch delays the error event firing until after
WebSocket::resume() is called, similarly to what we already do for
the close event.

Also add assertions in WebSocket::suspend() / WebSocket::resume()
that will be hit if JS events are fired from within these functions.
The pre-existing closed-when-entering-page-cache.html test is hitting
one of these assertions without the fix above.

Tests:
  - http/tests/websocket/tests/hybi/closed-when-entering-page-cache.html
  - http/tests/websocket/tests/hybi/stop-on-resume-in-error-handler.html

* Modules/websockets/WebSocket.cpp:
(WebCore::WebSocket::suspend):
(WebCore::WebSocket::resume):
(WebCore::WebSocket::resumeTimerFired):
(WebCore::WebSocket::stop):
(WebCore::WebSocket::didReceiveMessageError):
(WebCore::WebSocket::didClose):
(WebCore::WebSocket::dispatchOrQueueEvent):
* Modules/websockets/WebSocket.h:

LayoutTests:

* http/tests/websocket/tests/hybi/closed-when-entering-page-cache-expected.txt:
* http/tests/websocket/tests/hybi/closed-when-entering-page-cache.html:
Extend WebSocket PageCache test to make sure that the error event is
fired after restoring the page from the PageCache and before the close
Event is fired.

* http/tests/websocket/tests/hybi/resources/page-cache-websocket.html: Added.
* http/tests/websocket/tests/hybi/stop-on-resume-in-error-handler-expected.txt: Copied from LayoutTests/http/tests/websocket/tests/hybi/closed-when-entering-page-cache-expected.txt.
* http/tests/websocket/tests/hybi/stop-on-resume-in-error-handler.html: Copied from LayoutTests/http/tests/websocket/tests/hybi/closed-when-entering-page-cache.html.
Add layout test to cover the case where WebSocket::stop() is called
while firing the pending events upon restoring the page from PageCache.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsChangeLog">trunk/LayoutTests/ChangeLog</a></li>
<li><a href="#trunkLayoutTestshttptestswebsockettestshybiclosedwhenenteringpagecacheexpectedtxt">trunk/LayoutTests/http/tests/websocket/tests/hybi/closed-when-entering-page-cache-expected.txt</a></li>
<li><a href="#trunkLayoutTestshttptestswebsockettestshybiclosedwhenenteringpagecachehtml">trunk/LayoutTests/http/tests/websocket/tests/hybi/closed-when-entering-page-cache.html</a></li>
<li><a href="#trunkSourceWebCoreChangeLog">trunk/Source/WebCore/ChangeLog</a></li>
<li><a href="#trunkSourceWebCoreModuleswebsocketsWebSocketcpp">trunk/Source/WebCore/Modules/websockets/WebSocket.cpp</a></li>
<li><a href="#trunkSourceWebCoreModuleswebsocketsWebSocketh">trunk/Source/WebCore/Modules/websockets/WebSocket.h</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunkLayoutTestshttptestswebsockettestshybiresourcespagecachewebsockethtml">trunk/LayoutTests/http/tests/websocket/tests/hybi/resources/page-cache-websocket.html</a></li>
<li><a href="#trunkLayoutTestshttptestswebsockettestshybistoponresumeinerrorhandlerexpectedtxt">trunk/LayoutTests/http/tests/websocket/tests/hybi/stop-on-resume-in-error-handler-expected.txt</a></li>
<li><a href="#trunkLayoutTestshttptestswebsockettestshybistoponresumeinerrorhandlerhtml">trunk/LayoutTests/http/tests/websocket/tests/hybi/stop-on-resume-in-error-handler.html</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkLayoutTestsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/ChangeLog (182900 => 182901)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/ChangeLog        2015-04-16 19:20:08 UTC (rev 182900)
+++ trunk/LayoutTests/ChangeLog        2015-04-16 19:39:50 UTC (rev 182901)
</span><span class="lines">@@ -1,3 +1,23 @@
</span><ins>+2015-04-16  Chris Dumez  &lt;cdumez@apple.com&gt;
+
+        Regression(r182517): WebSocket::suspend() causes error event to be fired
+        https://bugs.webkit.org/show_bug.cgi?id=143806
+        &lt;rdar://problem/20559812&gt;
+
+        Reviewed by Alexey Proskuryakov.
+
+        * http/tests/websocket/tests/hybi/closed-when-entering-page-cache-expected.txt:
+        * http/tests/websocket/tests/hybi/closed-when-entering-page-cache.html:
+        Extend WebSocket PageCache test to make sure that the error event is
+        fired after restoring the page from the PageCache and before the close
+        Event is fired.
+
+        * http/tests/websocket/tests/hybi/resources/page-cache-websocket.html: Added.
+        * http/tests/websocket/tests/hybi/stop-on-resume-in-error-handler-expected.txt: Copied from LayoutTests/http/tests/websocket/tests/hybi/closed-when-entering-page-cache-expected.txt.
+        * http/tests/websocket/tests/hybi/stop-on-resume-in-error-handler.html: Copied from LayoutTests/http/tests/websocket/tests/hybi/closed-when-entering-page-cache.html.
+        Add layout test to cover the case where WebSocket::stop() is called
+        while firing the pending events upon restoring the page from PageCache.
+
</ins><span class="cx"> 2015-04-16  Myles C. Maxfield  &lt;mmaxfield@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         [iOS] Delete hardcoded font fallback tables
</span></span></pre></div>
<a id="trunkLayoutTestshttptestswebsockettestshybiclosedwhenenteringpagecacheexpectedtxt"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/http/tests/websocket/tests/hybi/closed-when-entering-page-cache-expected.txt (182900 => 182901)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/http/tests/websocket/tests/hybi/closed-when-entering-page-cache-expected.txt        2015-04-16 19:20:08 UTC (rev 182900)
+++ trunk/LayoutTests/http/tests/websocket/tests/hybi/closed-when-entering-page-cache-expected.txt        2015-04-16 19:39:50 UTC (rev 182901)
</span><span class="lines">@@ -8,8 +8,11 @@
</span><span class="cx"> pagehide - entering cache
</span><span class="cx"> pageshow - from cache
</span><span class="cx"> PASS Page did enter and was restored from the page cache
</span><del>-PASS WebSocket was closed
</del><ins>+PASS error event fired
</ins><span class="cx"> PASS wasRestoredFromPageCache is true
</span><ins>+PASS close event fired
+PASS wasRestoredFromPageCache is true
+PASS receivedErrorEvent is true
</ins><span class="cx"> PASS closeEvent.wasClean is false
</span><span class="cx"> PASS closeEvent.code is 1006
</span><span class="cx"> PASS successfullyParsed is true
</span></span></pre></div>
<a id="trunkLayoutTestshttptestswebsockettestshybiclosedwhenenteringpagecachehtml"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/http/tests/websocket/tests/hybi/closed-when-entering-page-cache.html (182900 => 182901)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/http/tests/websocket/tests/hybi/closed-when-entering-page-cache.html        2015-04-16 19:20:08 UTC (rev 182900)
+++ trunk/LayoutTests/http/tests/websocket/tests/hybi/closed-when-entering-page-cache.html        2015-04-16 19:39:50 UTC (rev 182901)
</span><span class="lines">@@ -10,6 +10,7 @@
</span><span class="cx">     testRunner.overridePreference(&quot;WebKitUsesPageCachePreferenceKey&quot;, 1);
</span><span class="cx"> 
</span><span class="cx"> var wasRestoredFromPageCache = false;
</span><ins>+var receivedErrorEvent = false;
</ins><span class="cx"> 
</span><span class="cx"> window.addEventListener(&quot;pageshow&quot;, function(event) {
</span><span class="cx">     debug(&quot;pageshow - &quot; + (event.persisted ? &quot;&quot; : &quot;not &quot;) + &quot;from cache&quot;);
</span><span class="lines">@@ -30,9 +31,15 @@
</span><span class="cx"> 
</span><span class="cx"> window.addEventListener('load', function() {
</span><span class="cx">     ws = new WebSocket(&quot;ws://127.0.0.1:8880/websocket/tests/hybi/echo&quot;);
</span><ins>+    ws.onerror = function(ev) {
+        receivedErrorEvent = true;
+        testPassed(&quot;error event fired&quot;);
+        shouldBeTrue(&quot;wasRestoredFromPageCache&quot;);
+    }
</ins><span class="cx">     ws.onclose = function(ev) {
</span><del>-        testPassed(&quot;WebSocket was closed&quot;);
</del><ins>+        testPassed(&quot;close event fired&quot;);
</ins><span class="cx">         shouldBeTrue(&quot;wasRestoredFromPageCache&quot;);
</span><ins>+        shouldBeTrue(&quot;receivedErrorEvent&quot;);
</ins><span class="cx">         closeEvent = ev;
</span><span class="cx">         shouldBeFalse(&quot;closeEvent.wasClean&quot;);
</span><span class="cx">         shouldBe(&quot;closeEvent.code&quot;, &quot;1006&quot;);
</span></span></pre></div>
<a id="trunkLayoutTestshttptestswebsockettestshybiresourcespagecachewebsockethtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/http/tests/websocket/tests/hybi/resources/page-cache-websocket.html (0 => 182901)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/http/tests/websocket/tests/hybi/resources/page-cache-websocket.html                                (rev 0)
+++ trunk/LayoutTests/http/tests/websocket/tests/hybi/resources/page-cache-websocket.html        2015-04-16 19:39:50 UTC (rev 182901)
</span><span class="lines">@@ -0,0 +1,8 @@
</span><ins>+&lt;!DOCTYPE html&gt;
+&lt;html&gt;
+&lt;body&gt;
+&lt;script&gt;
+ws = new WebSocket(&quot;ws://127.0.0.1:8880/websocket/tests/hybi/echo&quot;);
+&lt;/script&gt;
+&lt;/body&gt;
+&lt;/html&gt;
</ins></span></pre></div>
<a id="trunkLayoutTestshttptestswebsockettestshybistoponresumeinerrorhandlerexpectedtxtfromrev182900trunkLayoutTestshttptestswebsockettestshybiclosedwhenenteringpagecacheexpectedtxt"></a>
<div class="copfile"><h4>Copied: trunk/LayoutTests/http/tests/websocket/tests/hybi/stop-on-resume-in-error-handler-expected.txt (from rev 182900, trunk/LayoutTests/http/tests/websocket/tests/hybi/closed-when-entering-page-cache-expected.txt) (0 => 182901)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/http/tests/websocket/tests/hybi/stop-on-resume-in-error-handler-expected.txt                                (rev 0)
+++ trunk/LayoutTests/http/tests/websocket/tests/hybi/stop-on-resume-in-error-handler-expected.txt        2015-04-16 19:39:50 UTC (rev 182901)
</span><span class="lines">@@ -0,0 +1,16 @@
</span><ins>+CONSOLE MESSAGE: WebSocket connection to 'ws://127.0.0.1:8880/websocket/tests/hybi/echo' failed: WebSocket is closed due to suspension.
+Tests that WebSocket correctly handles being stopped in the error event handler upon restoring from PageCache.
+
+On success, you will see a series of &quot;PASS&quot; messages, followed by &quot;TEST COMPLETE&quot;.
+
+
+pageshow - not from cache
+pagehide - entering cache
+pageshow - from cache
+PASS Page did enter and was restored from the page cache
+error event fired
+PASS wasRestoredFromPageCache is true
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
</ins></span></pre></div>
<a id="trunkLayoutTestshttptestswebsockettestshybistoponresumeinerrorhandlerhtmlfromrev182900trunkLayoutTestshttptestswebsockettestshybiclosedwhenenteringpagecachehtml"></a>
<div class="copfile"><h4>Copied: trunk/LayoutTests/http/tests/websocket/tests/hybi/stop-on-resume-in-error-handler.html (from rev 182900, trunk/LayoutTests/http/tests/websocket/tests/hybi/closed-when-entering-page-cache.html) (0 => 182901)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/http/tests/websocket/tests/hybi/stop-on-resume-in-error-handler.html                                (rev 0)
+++ trunk/LayoutTests/http/tests/websocket/tests/hybi/stop-on-resume-in-error-handler.html        2015-04-16 19:39:50 UTC (rev 182901)
</span><span class="lines">@@ -0,0 +1,63 @@
</span><ins>+&lt;!DOCTYPE html&gt;
+&lt;html&gt;
+&lt;body&gt;
+&lt;script src=&quot;/js-test-resources/js-test-pre.js&quot;&gt;&lt;/script&gt;
+&lt;script&gt;
+description(&quot;Tests that WebSocket correctly handles being stopped in the error event handler upon restoring from PageCache.&quot;);
+
+var wasRestoredFromPageCache = false;
+
+window.jsTestIsAsync = true;
+if (window.testRunner)
+    testRunner.overridePreference(&quot;WebKitUsesPageCachePreferenceKey&quot;, 1);
+
+window.addEventListener(&quot;pageshow&quot;, function(event) {
+    debug(&quot;pageshow - &quot; + (event.persisted ? &quot;&quot; : &quot;not &quot;) + &quot;from cache&quot;);
+
+    if (event.persisted) {
+        testPassed(&quot;Page did enter and was restored from the page cache&quot;);
+        wasRestoredFromPageCache = true;
+    }
+}, false);
+
+window.addEventListener(&quot;pagehide&quot;, function(event) {
+    debug(&quot;pagehide - &quot; + (event.persisted ? &quot;&quot; : &quot;not &quot;) + &quot;entering cache&quot;);
+    if (!event.persisted) {
+        testFailed(&quot;Page did not enter the page cache.&quot;);
+        finishJSTest();
+    }
+}, false);
+
+var totalLoaded = 0;
+function loaded() {
+    totalLoaded++;
+    if (totalLoaded &lt; 2)
+        return;
+
+    testFrame = document.getElementById(&quot;testFrame&quot;);
+    ws = testFrame.contentWindow.ws;
+    ws.onerror = function(ev) {
+        debug(&quot;error event fired&quot;);
+        shouldBeTrue(&quot;wasRestoredFromPageCache&quot;);
+        testFrame.remove();
+        setTimeout(function() {
+             finishJSTest();
+        }, 500);
+    }
+    ws.onclose = function(ev) {
+        testFailed(&quot;Close event should not have fired.&quot;);
+    }
+
+    setTimeout(function() {
+        // Force a back navigation back to this page.
+        window.location.href = &quot;/navigation/resources/page-cache-helper.html&quot;;
+    }, 0);
+}
+
+window.onload = loaded;
+
+&lt;/script&gt;
+&lt;iframe id=&quot;testFrame&quot; src=&quot;resources/page-cache-websocket.html&quot; onload=&quot;loaded()&quot;&gt;&lt;/iframe&gt;
+&lt;script src=&quot;/js-test-resources/js-test-post.js&quot;&gt;&lt;/script&gt;
+&lt;/body&gt;
+&lt;/html&gt;
</ins></span></pre></div>
<a id="trunkSourceWebCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/ChangeLog (182900 => 182901)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/ChangeLog        2015-04-16 19:20:08 UTC (rev 182900)
+++ trunk/Source/WebCore/ChangeLog        2015-04-16 19:39:50 UTC (rev 182901)
</span><span class="lines">@@ -1,3 +1,38 @@
</span><ins>+2015-04-16  Chris Dumez  &lt;cdumez@apple.com&gt;
+
+        Regression(r182517): WebSocket::suspend() causes error event to be fired
+        https://bugs.webkit.org/show_bug.cgi?id=143806
+        &lt;rdar://problem/20559812&gt;
+
+        Reviewed by Alexey Proskuryakov.
+
+        WebSocket::suspend() causes an error event to be fired after r182517.
+        This is not allowed as firing the event could trigger arbitrary JS
+        execution, which is no longer allowed at this point.
+
+        This patch delays the error event firing until after
+        WebSocket::resume() is called, similarly to what we already do for
+        the close event.
+
+        Also add assertions in WebSocket::suspend() / WebSocket::resume()
+        that will be hit if JS events are fired from within these functions.
+        The pre-existing closed-when-entering-page-cache.html test is hitting
+        one of these assertions without the fix above.
+
+        Tests:
+          - http/tests/websocket/tests/hybi/closed-when-entering-page-cache.html
+          - http/tests/websocket/tests/hybi/stop-on-resume-in-error-handler.html
+
+        * Modules/websockets/WebSocket.cpp:
+        (WebCore::WebSocket::suspend):
+        (WebCore::WebSocket::resume):
+        (WebCore::WebSocket::resumeTimerFired):
+        (WebCore::WebSocket::stop):
+        (WebCore::WebSocket::didReceiveMessageError):
+        (WebCore::WebSocket::didClose):
+        (WebCore::WebSocket::dispatchOrQueueEvent):
+        * Modules/websockets/WebSocket.h:
+
</ins><span class="cx"> 2015-04-15  Roger Fong  &lt;roger_fong@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         Adjustments to button graphics for media controls.
</span></span></pre></div>
<a id="trunkSourceWebCoreModuleswebsocketsWebSocketcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/Modules/websockets/WebSocket.cpp (182900 => 182901)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/Modules/websockets/WebSocket.cpp        2015-04-16 19:20:08 UTC (rev 182900)
+++ trunk/Source/WebCore/Modules/websockets/WebSocket.cpp        2015-04-16 19:39:50 UTC (rev 182901)
</span><span class="lines">@@ -468,13 +468,15 @@
</span><span class="cx"> 
</span><span class="cx"> void WebSocket::suspend(ReasonForSuspension reason)
</span><span class="cx"> {
</span><ins>+    NoEventDispatchAssertion assertNoEventDispatch;
+
</ins><span class="cx">     if (m_resumeTimer.isActive())
</span><span class="cx">         m_resumeTimer.stop();
</span><span class="cx"> 
</span><ins>+    m_shouldDelayEventFiring = true;
+
</ins><span class="cx">     if (m_channel) {
</span><span class="cx">         if (reason == ActiveDOMObject::PageCache) {
</span><del>-            // The page will enter the page cache, close the channel but only fire the close event after resuming.
-            m_shouldDelayCloseEvent = true;
</del><span class="cx">             // This will cause didClose() to be called.
</span><span class="cx">             m_channel-&gt;fail(&quot;WebSocket is closed due to suspension.&quot;);
</span><span class="cx">         } else
</span><span class="lines">@@ -484,18 +486,28 @@
</span><span class="cx"> 
</span><span class="cx"> void WebSocket::resume()
</span><span class="cx"> {
</span><ins>+    NoEventDispatchAssertion assertNoEventDispatch;
+
</ins><span class="cx">     if (m_channel)
</span><span class="cx">         m_channel-&gt;resume();
</span><del>-    else if (m_pendingCloseEvent &amp;&amp; !m_resumeTimer.isActive()) {
-        // Fire the close event in a timer as we are not allowed to execute arbitrary JS from resume().
</del><ins>+    else if (!m_pendingEvents.isEmpty() &amp;&amp; !m_resumeTimer.isActive()) {
+        // Fire the pending events in a timer as we are not allowed to execute arbitrary JS from resume().
</ins><span class="cx">         m_resumeTimer.startOneShot(0);
</span><span class="cx">     }
</span><ins>+
+    m_shouldDelayEventFiring = false;
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> void WebSocket::resumeTimerFired()
</span><span class="cx"> {
</span><del>-    ASSERT(m_pendingCloseEvent);
-    dispatchEvent(WTF::move(m_pendingCloseEvent));
</del><ins>+    Ref&lt;WebSocket&gt; protect(*this);
+
+    ASSERT(!m_pendingEvents.isEmpty());
+
+    // Check m_shouldDelayEventFiring when iterating in case firing an event causes
+    // suspend() to be called.
+    while (!m_pendingEvents.isEmpty() &amp;&amp; !m_shouldDelayEventFiring)
+        dispatchEvent(m_pendingEvents.takeFirst());
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> void WebSocket::stop()
</span><span class="lines">@@ -505,6 +517,7 @@
</span><span class="cx">         m_channel-&gt;disconnect();
</span><span class="cx">     m_channel = 0;
</span><span class="cx">     m_state = CLOSED;
</span><ins>+    m_pendingEvents.clear();
</ins><span class="cx">     ActiveDOMObject::stop();
</span><span class="cx">     if (pending)
</span><span class="cx">         ActiveDOMObject::unsetPendingActivity(this);
</span><span class="lines">@@ -560,7 +573,7 @@
</span><span class="cx">     LOG(Network, &quot;WebSocket %p didReceiveErrorMessage()&quot;, this);
</span><span class="cx">     m_state = CLOSED;
</span><span class="cx">     ASSERT(scriptExecutionContext());
</span><del>-    dispatchEvent(Event::create(eventNames().errorEvent, false, false));
</del><ins>+    dispatchOrQueueEvent(Event::create(eventNames().errorEvent, false, false));
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> void WebSocket::didUpdateBufferedAmount(unsigned long bufferedAmount)
</span><span class="lines">@@ -587,12 +600,7 @@
</span><span class="cx">     m_bufferedAmount = unhandledBufferedAmount;
</span><span class="cx">     ASSERT(scriptExecutionContext());
</span><span class="cx"> 
</span><del>-    RefPtr&lt;CloseEvent&gt; event = CloseEvent::create(wasClean, code, reason);
-    if (m_shouldDelayCloseEvent) {
-        m_pendingCloseEvent = WTF::move(event);
-        m_shouldDelayCloseEvent = false;
-    } else
-        dispatchEvent(event);
</del><ins>+    dispatchOrQueueEvent(CloseEvent::create(wasClean, code, reason));
</ins><span class="cx"> 
</span><span class="cx">     if (m_channel) {
</span><span class="cx">         m_channel-&gt;disconnect();
</span><span class="lines">@@ -616,6 +624,14 @@
</span><span class="cx">     return overhead;
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+void WebSocket::dispatchOrQueueEvent(Ref&lt;Event&gt;&amp;&amp; event)
+{
+    if (m_shouldDelayEventFiring)
+        m_pendingEvents.append(WTF::move(event));
+    else
+        dispatchEvent(WTF::move(event));
+}
+
</ins><span class="cx"> }  // namespace WebCore
</span><span class="cx"> 
</span><span class="cx"> #endif
</span></span></pre></div>
<a id="trunkSourceWebCoreModuleswebsocketsWebSocketh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/Modules/websockets/WebSocket.h (182900 => 182901)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/Modules/websockets/WebSocket.h        2015-04-16 19:20:08 UTC (rev 182900)
+++ trunk/Source/WebCore/Modules/websockets/WebSocket.h        2015-04-16 19:39:50 UTC (rev 182901)
</span><span class="lines">@@ -110,6 +110,7 @@
</span><span class="cx">     explicit WebSocket(ScriptExecutionContext&amp;);
</span><span class="cx"> 
</span><span class="cx">     void resumeTimerFired();
</span><ins>+    void dispatchOrQueueEvent(Ref&lt;Event&gt;&amp;&amp;);
</ins><span class="cx"> 
</span><span class="cx">     // ActiveDOMObject API.
</span><span class="cx">     void contextDestroyed() override;
</span><span class="lines">@@ -140,8 +141,8 @@
</span><span class="cx">     String m_extensions;
</span><span class="cx"> 
</span><span class="cx">     Timer m_resumeTimer;
</span><del>-    bool m_shouldDelayCloseEvent { false };
-    RefPtr&lt;CloseEvent&gt; m_pendingCloseEvent;
</del><ins>+    bool m_shouldDelayEventFiring { false };
+    Deque&lt;Ref&lt;Event&gt;&gt; m_pendingEvents;
</ins><span class="cx"> };
</span><span class="cx"> 
</span><span class="cx"> } // namespace WebCore
</span></span></pre>
</div>
</div>

</body>
</html>