<!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>[212625] 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/212625">212625</a></dd>
<dt>Author</dt> <dd>cdumez@apple.com</dd>
<dt>Date</dt> <dd>2017-02-19 22:36:04 -0800 (Sun, 19 Feb 2017)</dd>
</dl>

<h3>Log Message</h3>
<pre>onbeforeunload event return value coercion is not per-spec
https://bugs.webkit.org/show_bug.cgi?id=168382

Reviewed by Darin Adler.

LayoutTests/imported/w3c:

Import test coverage from web-platform-tests. We were failing half the checks
before this patch.

* resources/resource-files.json:
* web-platform-tests/html/browsers/browsing-the-web/unloading-documents/MANIFEST:
* web-platform-tests/html/browsers/browsing-the-web/unloading-documents/beforeunload-canceling-1.html: Added.
* web-platform-tests/html/browsers/browsing-the-web/unloading-documents/beforeunload-canceling-expected.txt: Added.
* web-platform-tests/html/browsers/browsing-the-web/unloading-documents/beforeunload-canceling.html: Added.
* web-platform-tests/html/browsers/browsing-the-web/unloading-documents/w3c-import.log:

Source/WebCore:

Update handling of value returned by onbeforeunload event listeners
to match Firefox and the specification:
- https://html.spec.whatwg.org/#the-event-handler-processing-algorithm (step 4)

Namely, the following changes were made:
- Only set the event's returnValue attribute to the returned value if the attribute
  value is the empty string (so as to not override the attribute value if it has
  explicitly been set by JS).
- Cancel the event when the return value is not null by calling preventDefault().

Additionally, the following changes were made:
- Ask the user to confirm the navigation if the event was canceled, not just if the
  returnValue attribute was set to a non-empty string.
as per:
- https://html.spec.whatwg.org/#prompt-to-unload-a-document (step 8)

Tests: fast/events/before-unload-return-string-conversion.html
       imported/w3c/web-platform-tests/html/browsers/browsing-the-web/unloading-documents/beforeunload-canceling.html

* bindings/js/JSEventListener.cpp:
(WebCore::handleBeforeUnloadEventReturnValue):
(WebCore::JSEventListener::handleEvent):
* loader/FrameLoader.cpp:
(WebCore::shouldAskForNavigationConfirmation):
(WebCore::FrameLoader::dispatchBeforeUnloadEvent):

LayoutTests:

Add test case to check that the value returned by a beforeunload event handler
is already converted to a string, even if the returnValue attribute is also
set on the BeforeUnloadEvent. The existing code did not handle this properly
and it has been fixed in this patch.

* fast/events/before-unload-return-string-conversion-expected.txt: Added.
* fast/events/before-unload-return-string-conversion.html: Added.
* fast/events/resources/before-unload-return-string-conversion-frame.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="#trunkLayoutTestsimportedw3cresourcesresourcefilesjson">trunk/LayoutTests/imported/w3c/resources/resource-files.json</a></li>
<li><a href="#trunkLayoutTestsimportedw3cwebplatformtestshtmlbrowsersbrowsingthewebunloadingdocumentsMANIFEST">trunk/LayoutTests/imported/w3c/web-platform-tests/html/browsers/browsing-the-web/unloading-documents/MANIFEST</a></li>
<li><a href="#trunkLayoutTestsimportedw3cwebplatformtestshtmlbrowsersbrowsingthewebunloadingdocumentsw3cimportlog">trunk/LayoutTests/imported/w3c/web-platform-tests/html/browsers/browsing-the-web/unloading-documents/w3c-import.log</a></li>
<li><a href="#trunkSourceWebCoreChangeLog">trunk/Source/WebCore/ChangeLog</a></li>
<li><a href="#trunkSourceWebCorebindingsjsJSEventListenercpp">trunk/Source/WebCore/bindings/js/JSEventListener.cpp</a></li>
<li><a href="#trunkSourceWebCoreloaderFrameLoadercpp">trunk/Source/WebCore/loader/FrameLoader.cpp</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsfasteventsbeforeunloadreturnstringconversionexpectedtxt">trunk/LayoutTests/fast/events/before-unload-return-string-conversion-expected.txt</a></li>
<li><a href="#trunkLayoutTestsfasteventsbeforeunloadreturnstringconversionhtml">trunk/LayoutTests/fast/events/before-unload-return-string-conversion.html</a></li>
<li><a href="#trunkLayoutTestsfasteventsresourcesbeforeunloadreturnstringconversionframehtml">trunk/LayoutTests/fast/events/resources/before-unload-return-string-conversion-frame.html</a></li>
<li><a href="#trunkLayoutTestsimportedw3cwebplatformtestshtmlbrowsersbrowsingthewebunloadingdocumentsbeforeunloadcanceling1html">trunk/LayoutTests/imported/w3c/web-platform-tests/html/browsers/browsing-the-web/unloading-documents/beforeunload-canceling-1.html</a></li>
<li><a href="#trunkLayoutTestsimportedw3cwebplatformtestshtmlbrowsersbrowsingthewebunloadingdocumentsbeforeunloadcancelingexpectedtxt">trunk/LayoutTests/imported/w3c/web-platform-tests/html/browsers/browsing-the-web/unloading-documents/beforeunload-canceling-expected.txt</a></li>
<li><a href="#trunkLayoutTestsimportedw3cwebplatformtestshtmlbrowsersbrowsingthewebunloadingdocumentsbeforeunloadcancelinghtml">trunk/LayoutTests/imported/w3c/web-platform-tests/html/browsers/browsing-the-web/unloading-documents/beforeunload-canceling.html</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkLayoutTestsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/ChangeLog (212624 => 212625)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/ChangeLog        2017-02-20 06:34:15 UTC (rev 212624)
+++ trunk/LayoutTests/ChangeLog        2017-02-20 06:36:04 UTC (rev 212625)
</span><span class="lines">@@ -1,3 +1,19 @@
</span><ins>+2017-02-19  Chris Dumez  &lt;cdumez@apple.com&gt;
+
+        onbeforeunload event return value coercion is not per-spec
+        https://bugs.webkit.org/show_bug.cgi?id=168382
+
+        Reviewed by Darin Adler.
+
+        Add test case to check that the value returned by a beforeunload event handler
+        is already converted to a string, even if the returnValue attribute is also
+        set on the BeforeUnloadEvent. The existing code did not handle this properly
+        and it has been fixed in this patch.
+
+        * fast/events/before-unload-return-string-conversion-expected.txt: Added.
+        * fast/events/before-unload-return-string-conversion.html: Added.
+        * fast/events/resources/before-unload-return-string-conversion-frame.html: Added.
+
</ins><span class="cx"> 2017-02-18  Ryosuke Niwa  &lt;rniwa@webkit.org&gt;
</span><span class="cx"> 
</span><span class="cx">         REGRESSION(r212218): Assertion failures in and after parserRemoveChild
</span></span></pre></div>
<a id="trunkLayoutTestsfasteventsbeforeunloadreturnstringconversionexpectedtxt"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/fast/events/before-unload-return-string-conversion-expected.txt (0 => 212625)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/fast/events/before-unload-return-string-conversion-expected.txt                                (rev 0)
+++ trunk/LayoutTests/fast/events/before-unload-return-string-conversion-expected.txt        2017-02-20 06:36:04 UTC (rev 212625)
</span><span class="lines">@@ -0,0 +1,13 @@
</span><ins>+CONFIRM NAVIGATION: PASS
+Tests that the value returned from a beforeunload event handler gets converted to a string, even if the returnValue property was already set.
+
+On success, you will see a series of &quot;PASS&quot; messages, followed by &quot;TEST COMPLETE&quot;.
+
+
+PASS event.defaultPrevented is true
+PASS event.returnValue is &quot;PASS&quot;
+PASS toStringCalled is true
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
</ins></span></pre></div>
<a id="trunkLayoutTestsfasteventsbeforeunloadreturnstringconversionhtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/fast/events/before-unload-return-string-conversion.html (0 => 212625)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/fast/events/before-unload-return-string-conversion.html                                (rev 0)
+++ trunk/LayoutTests/fast/events/before-unload-return-string-conversion.html        2017-02-20 06:36:04 UTC (rev 212625)
</span><span class="lines">@@ -0,0 +1,19 @@
</span><ins>+&lt;!DOCTYPE html&gt;
+&lt;html&gt;
+&lt;body&gt;
+&lt;script src=&quot;../../resources/js-test-pre.js&quot;&gt;&lt;/script&gt;
+&lt;script&gt;
+description(&quot;Tests that the value returned from a beforeunload event handler gets converted to a string, even if the returnValue property was already set.&quot;);
+jsTestIsAsync = true;
+
+const iframe = document.createElement(&quot;iframe&quot;);
+iframe.onload = function() {
+    iframe.onload = null;
+    iframe.contentWindow.runTest();
+};
+
+iframe.src = &quot;resources/before-unload-return-string-conversion-frame.html&quot;;
+document.body.appendChild(iframe);
+&lt;/script&gt;
+&lt;script src=&quot;../../resources/js-test-post.js&quot;&gt;&lt;/script&gt;
+&lt;/body&gt;
</ins></span></pre></div>
<a id="trunkLayoutTestsfasteventsresourcesbeforeunloadreturnstringconversionframehtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/fast/events/resources/before-unload-return-string-conversion-frame.html (0 => 212625)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/fast/events/resources/before-unload-return-string-conversion-frame.html                                (rev 0)
+++ trunk/LayoutTests/fast/events/resources/before-unload-return-string-conversion-frame.html        2017-02-20 06:36:04 UTC (rev 212625)
</span><span class="lines">@@ -0,0 +1,25 @@
</span><ins>+&lt;!DOCTYPE html&gt;
+&lt;script&gt;
+
+parent.toStringCalled = false;
+
+window.runTest = function() {
+    window.onbeforeunload = function(e) {
+        e.returnValue = &quot;PASS&quot;;
+        return { toString: function () { parent.toStringCalled = true; return &quot;FAIL&quot;; } };
+    }
+
+    const listener = function(e) {
+        parent.event = e;
+        parent.shouldBeTrue(&quot;event.defaultPrevented&quot;);
+        parent.shouldBeEqualToString(&quot;event.returnValue&quot;, &quot;PASS&quot;);
+        parent.shouldBeTrue(&quot;toStringCalled&quot;);
+        parent.setTimeout(function() {
+            parent.finishJSTest();
+        }, 0);
+    }
+
+    window.addEventListener(&quot;beforeunload&quot;, listener);
+    window.location.href = &quot;about:blank&quot;;
+}
+&lt;/script&gt;
</ins></span></pre></div>
<a id="trunkLayoutTestsimportedw3cChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/imported/w3c/ChangeLog (212624 => 212625)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/imported/w3c/ChangeLog        2017-02-20 06:34:15 UTC (rev 212624)
+++ trunk/LayoutTests/imported/w3c/ChangeLog        2017-02-20 06:36:04 UTC (rev 212625)
</span><span class="lines">@@ -1,3 +1,20 @@
</span><ins>+2017-02-19  Chris Dumez  &lt;cdumez@apple.com&gt;
+
+        onbeforeunload event return value coercion is not per-spec
+        https://bugs.webkit.org/show_bug.cgi?id=168382
+
+        Reviewed by Darin Adler.
+
+        Import test coverage from web-platform-tests. We were failing half the checks
+        before this patch.
+
+        * resources/resource-files.json:
+        * web-platform-tests/html/browsers/browsing-the-web/unloading-documents/MANIFEST:
+        * web-platform-tests/html/browsers/browsing-the-web/unloading-documents/beforeunload-canceling-1.html: Added.
+        * web-platform-tests/html/browsers/browsing-the-web/unloading-documents/beforeunload-canceling-expected.txt: Added.
+        * web-platform-tests/html/browsers/browsing-the-web/unloading-documents/beforeunload-canceling.html: Added.
+        * web-platform-tests/html/browsers/browsing-the-web/unloading-documents/w3c-import.log:
+
</ins><span class="cx"> 2017-02-17  Javier Fernandez  &lt;jfernandez@igalia.com&gt;
</span><span class="cx"> 
</span><span class="cx">         [GTK] Unreviewed test gardening
</span></span></pre></div>
<a id="trunkLayoutTestsimportedw3cresourcesresourcefilesjson"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/imported/w3c/resources/resource-files.json (212624 => 212625)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/imported/w3c/resources/resource-files.json        2017-02-20 06:34:15 UTC (rev 212624)
+++ trunk/LayoutTests/imported/w3c/resources/resource-files.json        2017-02-20 06:36:04 UTC (rev 212625)
</span><span class="lines">@@ -66,6 +66,7 @@
</span><span class="cx">         &quot;web-platform-tests/html/browsers/browsing-the-web/navigating-across-documents/navigation_unload_data_url-1.html&quot;,
</span><span class="cx">         &quot;web-platform-tests/html/browsers/browsing-the-web/navigating-across-documents/navigation_unload_same_origin-1.html&quot;,
</span><span class="cx">         &quot;web-platform-tests/html/browsers/browsing-the-web/unloading-documents/base.html&quot;,
</span><ins>+        &quot;web-platform-tests/html/browsers/browsing-the-web/unloading-documents/beforeunload-canceling-1.html&quot;,
</ins><span class="cx">         &quot;web-platform-tests/html/browsers/browsing-the-web/unloading-documents/beforeunload-on-history-back-1.html&quot;,
</span><span class="cx">         &quot;web-platform-tests/html/browsers/browsing-the-web/unloading-documents/beforeunload-on-navigation-of-parent-1.html&quot;,
</span><span class="cx">         &quot;web-platform-tests/html/browsers/browsing-the-web/unloading-documents/beforeunload-on-navigation-of-parent-2.html&quot;,
</span><span class="lines">@@ -254,4 +255,4 @@
</span><span class="cx">         &quot;web-platform-tests/test_keys_wdspec.html&quot;,
</span><span class="cx">         &quot;web-platform-tests/upgrade-insecure-requests/support/post-origin-to-parent.html&quot;
</span><span class="cx">     ]
</span><del>-}
</del><span class="cx">\ No newline at end of file
</span><ins>+}
</ins></span></pre></div>
<a id="trunkLayoutTestsimportedw3cwebplatformtestshtmlbrowsersbrowsingthewebunloadingdocumentsMANIFEST"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/imported/w3c/web-platform-tests/html/browsers/browsing-the-web/unloading-documents/MANIFEST (212624 => 212625)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/imported/w3c/web-platform-tests/html/browsers/browsing-the-web/unloading-documents/MANIFEST        2017-02-20 06:34:15 UTC (rev 212624)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/html/browsers/browsing-the-web/unloading-documents/MANIFEST        2017-02-20 06:36:04 UTC (rev 212625)
</span><span class="lines">@@ -19,6 +19,7 @@
</span><span class="cx"> support 005b.html
</span><span class="cx"> 005.html
</span><span class="cx"> base.html
</span><ins>+support beforeunload-canceling-1.html
</ins><span class="cx"> support beforeunload-on-history-back-1.html
</span><span class="cx"> beforeunload-on-history-back.html
</span><span class="cx"> support beforeunload-on-navigation-of-parent-1.html
</span></span></pre></div>
<a id="trunkLayoutTestsimportedw3cwebplatformtestshtmlbrowsersbrowsingthewebunloadingdocumentsbeforeunloadcanceling1html"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/imported/w3c/web-platform-tests/html/browsers/browsing-the-web/unloading-documents/beforeunload-canceling-1.html (0 => 212625)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/imported/w3c/web-platform-tests/html/browsers/browsing-the-web/unloading-documents/beforeunload-canceling-1.html                                (rev 0)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/html/browsers/browsing-the-web/unloading-documents/beforeunload-canceling-1.html        2017-02-20 06:36:04 UTC (rev 212625)
</span><span class="lines">@@ -0,0 +1,31 @@
</span><ins>+&lt;!DOCTYPE html&gt;
+&lt;meta charset=&quot;utf-8&quot;&gt;
+&lt;title&gt;Support page for beforeunload-canceling.html&lt;/title&gt;
+
+&lt;h1&gt;If this goes away, it navigated&lt;/h1&gt;
+
+&lt;script&gt;
+&quot;use strict&quot;;
+
+window.runTest = (t, { valueToReturn, expectCancelation, setReturnValue, expectedReturnValue }) =&gt; {
+  window.onbeforeunload = t.step_func(e =&gt; {
+    if (setReturnValue !== undefined) {
+      e.returnValue = setReturnValue;
+    }
+
+    return valueToReturn;
+  });
+
+  const listener = t.step_func(e =&gt; {
+    top.assert_equals(e.defaultPrevented, expectCancelation, &quot;canceled&quot;);
+    top.assert_equals(e.returnValue, expectedReturnValue, &quot;returnValue&quot;);
+    window.onbeforeunload = null;
+
+    t.done();
+  });
+
+  window.addEventListener(&quot;beforeunload&quot;, listener);
+
+  window.location.href = &quot;about:blank&quot;;
+};
+&lt;/script&gt;
</ins></span></pre></div>
<a id="trunkLayoutTestsimportedw3cwebplatformtestshtmlbrowsersbrowsingthewebunloadingdocumentsbeforeunloadcancelingexpectedtxt"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/imported/w3c/web-platform-tests/html/browsers/browsing-the-web/unloading-documents/beforeunload-canceling-expected.txt (0 => 212625)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/imported/w3c/web-platform-tests/html/browsers/browsing-the-web/unloading-documents/beforeunload-canceling-expected.txt                                (rev 0)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/html/browsers/browsing-the-web/unloading-documents/beforeunload-canceling-expected.txt        2017-02-20 06:36:04 UTC (rev 212625)
</span><span class="lines">@@ -0,0 +1,26 @@
</span><ins>+CONFIRM NAVIGATION: 
+CONFIRM NAVIGATION: false
+CONFIRM NAVIGATION: true
+CONFIRM NAVIGATION: 0
+CONFIRM NAVIGATION: foo
+CONFIRM NAVIGATION: foo
+CONFIRM NAVIGATION: foo
+CONFIRM NAVIGATION: foo
+CONFIRM NAVIGATION: foo
+CONFIRM NAVIGATION: foo
+
+PASS Returning a string must not cancel the event: CustomEvent, non-cancelable 
+PASS Returning a string must not cancel the event: CustomEvent, cancelable 
+PASS Returning null with a real iframe unloading 
+PASS Returning undefined with a real iframe unloading 
+PASS Returning  with a real iframe unloading 
+PASS Returning false with a real iframe unloading 
+PASS Returning true with a real iframe unloading 
+PASS Returning 0 with a real iframe unloading 
+PASS Returning null with a real iframe unloading; setting returnValue to foo 
+PASS Returning undefined with a real iframe unloading; setting returnValue to foo 
+PASS Returning  with a real iframe unloading; setting returnValue to foo 
+PASS Returning false with a real iframe unloading; setting returnValue to foo 
+PASS Returning true with a real iframe unloading; setting returnValue to foo 
+PASS Returning 0 with a real iframe unloading; setting returnValue to foo 

</ins></span></pre></div>
<a id="trunkLayoutTestsimportedw3cwebplatformtestshtmlbrowsersbrowsingthewebunloadingdocumentsbeforeunloadcancelinghtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/imported/w3c/web-platform-tests/html/browsers/browsing-the-web/unloading-documents/beforeunload-canceling.html (0 => 212625)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/imported/w3c/web-platform-tests/html/browsers/browsing-the-web/unloading-documents/beforeunload-canceling.html                                (rev 0)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/html/browsers/browsing-the-web/unloading-documents/beforeunload-canceling.html        2017-02-20 06:36:04 UTC (rev 212625)
</span><span class="lines">@@ -0,0 +1,142 @@
</span><ins>+&lt;!DOCTYPE html&gt;
+&lt;meta charset=&quot;utf-8&quot;&gt;
+&lt;title&gt;beforeunload return value cancelation behavior&lt;/title&gt;
+&lt;link rel=&quot;help&quot; href=&quot;https://html.spec.whatwg.org/multipage/webappapis.html#the-event-handler-processing-algorithm&quot;&gt;
+&lt;link rel=&quot;author&quot; title=&quot;Domenic Denicola&quot; href=&quot;mailto:d@domenic.me&quot;&gt;
+&lt;script src=&quot;/resources/testharness.js&quot;&gt;&lt;/script&gt;
+&lt;script src=&quot;/resources/testharnessreport.js&quot;&gt;&lt;/script&gt;
+
+&lt;div id=&quot;log&quot;&gt;&lt;/div&gt;
+
+&lt;script&gt;
+&quot;use strict&quot;;
+
+async_test(t =&gt; {
+  let onbeforeunloadHappened = false;
+  window.onbeforeunload = t.step_func(() =&gt; {
+    onbeforeunloadHappened = true;
+    return &quot;cancel me&quot;;
+  });
+
+  const listener = t.step_func(e =&gt; {
+    assert_true(onbeforeunloadHappened, &quot;CustomEvent must be able to trigger the event handler&quot;);
+    assert_false(e.defaultPrevented, &quot;The event must not have been canceled&quot;);
+    window.onbeforeunload = null;
+    t.done();
+  });
+
+  window.addEventListener(&quot;beforeunload&quot;, listener);
+
+  window.dispatchEvent(new CustomEvent(&quot;beforeunload&quot;));
+}, &quot;Returning a string must not cancel the event: CustomEvent, non-cancelable&quot;);
+
+async_test(t =&gt; {
+  let onbeforeunloadHappened = false;
+  window.onbeforeunload = t.step_func(() =&gt; {
+    onbeforeunloadHappened = true;
+    return &quot;cancel me&quot;;
+  });
+
+  const listener = t.step_func(e =&gt; {
+    assert_true(onbeforeunloadHappened, &quot;CustomEvent must be able to trigger the event handler&quot;);
+    assert_false(e.defaultPrevented, &quot;The event must not have been canceled&quot;);
+    window.onbeforeunload = null;
+    t.done();
+  });
+
+  window.addEventListener(&quot;beforeunload&quot;, listener);
+
+  window.dispatchEvent(new CustomEvent(&quot;beforeunload&quot;, { cancelable: true }));
+}, &quot;Returning a string must not cancel the event: CustomEvent, cancelable&quot;);
+
+const testCases = [
+  {
+    valueToReturn: null,
+    expectCancelation: false,
+    expectedReturnValue: &quot;&quot;
+  },
+  {
+    valueToReturn: undefined,
+    expectCancelation: false,
+    expectedReturnValue: &quot;&quot;
+  },
+  {
+    valueToReturn: &quot;&quot;,
+    expectCancelation: true,
+    expectedReturnValue: &quot;&quot;
+  },
+  {
+    valueToReturn: false,
+    expectCancelation: true,
+    expectedReturnValue: &quot;false&quot;
+  },
+  {
+    valueToReturn: true,
+    expectCancelation: true,
+    expectedReturnValue: &quot;true&quot;
+  },
+  {
+    valueToReturn: 0,
+    expectCancelation: true,
+    expectedReturnValue: &quot;0&quot;
+  },
+  {
+    valueToReturn: null,
+    expectCancelation: false,
+    setReturnValue: &quot;foo&quot;,
+    expectedReturnValue: &quot;foo&quot;
+  },
+  {
+    valueToReturn: undefined,
+    expectCancelation: false,
+    setReturnValue: &quot;foo&quot;,
+    expectedReturnValue: &quot;foo&quot;
+  },
+  {
+    valueToReturn: &quot;&quot;,
+    expectCancelation: true,
+    setReturnValue: &quot;foo&quot;,
+    expectedReturnValue: &quot;foo&quot;
+  },
+  {
+    valueToReturn: false,
+    expectCancelation: true,
+    setReturnValue: &quot;foo&quot;,
+    expectedReturnValue: &quot;foo&quot;
+  },
+  {
+    valueToReturn: true,
+    expectCancelation: true,
+    setReturnValue: &quot;foo&quot;,
+    expectedReturnValue: &quot;foo&quot;
+  },
+  {
+    valueToReturn: 0,
+    expectCancelation: true,
+    setReturnValue: &quot;foo&quot;,
+    expectedReturnValue: &quot;foo&quot;
+  }
+];
+
+var testCaseIndex = 0;
+function runNextTest() {
+  const testCase = testCases[testCaseIndex];
+
+  const labelAboutReturnValue = testCase.setReturnValue === undefined ? &quot;&quot; :
+    `; setting returnValue to ${testCase.setReturnValue}`;
+
+  async_test(t =&gt; {
+    const iframe = document.createElement(&quot;iframe&quot;);
+    iframe.onload = t.step_func(() =&gt; {
+      iframe.contentWindow.runTest(t, testCase);
+      if (++testCaseIndex &lt; testCases.length)
+        runNextTest();
+    });
+
+    iframe.src = &quot;beforeunload-canceling-1.html&quot;;
+    document.body.appendChild(iframe);
+  }, `Returning ${testCase.valueToReturn} with a real iframe unloading${labelAboutReturnValue}`);
+}
+
+runNextTest();
+&lt;/script&gt;
</ins></span></pre></div>
<a id="trunkLayoutTestsimportedw3cwebplatformtestshtmlbrowsersbrowsingthewebunloadingdocumentsw3cimportlog"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/imported/w3c/web-platform-tests/html/browsers/browsing-the-web/unloading-documents/w3c-import.log (212624 => 212625)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/imported/w3c/web-platform-tests/html/browsers/browsing-the-web/unloading-documents/w3c-import.log        2017-02-20 06:34:15 UTC (rev 212624)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/html/browsers/browsing-the-web/unloading-documents/w3c-import.log        2017-02-20 06:36:04 UTC (rev 212625)
</span><span class="lines">@@ -21,6 +21,8 @@
</span><span class="cx"> /LayoutTests/imported/w3c/web-platform-tests/html/browsers/browsing-the-web/unloading-documents/004.html
</span><span class="cx"> /LayoutTests/imported/w3c/web-platform-tests/html/browsers/browsing-the-web/unloading-documents/005.html
</span><span class="cx"> /LayoutTests/imported/w3c/web-platform-tests/html/browsers/browsing-the-web/unloading-documents/base.html
</span><ins>+/LayoutTests/imported/w3c/web-platform-tests/html/browsers/browsing-the-web/unloading-documents/beforeunload-canceling-1.html
+/LayoutTests/imported/w3c/web-platform-tests/html/browsers/browsing-the-web/unloading-documents/beforeunload-canceling.html
</ins><span class="cx"> /LayoutTests/imported/w3c/web-platform-tests/html/browsers/browsing-the-web/unloading-documents/beforeunload-on-history-back-1.html
</span><span class="cx"> /LayoutTests/imported/w3c/web-platform-tests/html/browsers/browsing-the-web/unloading-documents/beforeunload-on-history-back.html
</span><span class="cx"> /LayoutTests/imported/w3c/web-platform-tests/html/browsers/browsing-the-web/unloading-documents/beforeunload-on-navigation-of-parent-1.html
</span></span></pre></div>
<a id="trunkSourceWebCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/ChangeLog (212624 => 212625)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/ChangeLog        2017-02-20 06:34:15 UTC (rev 212624)
+++ trunk/Source/WebCore/ChangeLog        2017-02-20 06:36:04 UTC (rev 212625)
</span><span class="lines">@@ -1,3 +1,36 @@
</span><ins>+2017-02-19  Chris Dumez  &lt;cdumez@apple.com&gt;
+
+        onbeforeunload event return value coercion is not per-spec
+        https://bugs.webkit.org/show_bug.cgi?id=168382
+
+        Reviewed by Darin Adler.
+
+        Update handling of value returned by onbeforeunload event listeners
+        to match Firefox and the specification:
+        - https://html.spec.whatwg.org/#the-event-handler-processing-algorithm (step 4)
+
+        Namely, the following changes were made:
+        - Only set the event's returnValue attribute to the returned value if the attribute
+          value is the empty string (so as to not override the attribute value if it has
+          explicitly been set by JS).
+        - Cancel the event when the return value is not null by calling preventDefault().
+
+        Additionally, the following changes were made:
+        - Ask the user to confirm the navigation if the event was canceled, not just if the
+          returnValue attribute was set to a non-empty string.
+        as per:
+        - https://html.spec.whatwg.org/#prompt-to-unload-a-document (step 8)
+
+        Tests: fast/events/before-unload-return-string-conversion.html
+               imported/w3c/web-platform-tests/html/browsers/browsing-the-web/unloading-documents/beforeunload-canceling.html
+
+        * bindings/js/JSEventListener.cpp:
+        (WebCore::handleBeforeUnloadEventReturnValue):
+        (WebCore::JSEventListener::handleEvent):
+        * loader/FrameLoader.cpp:
+        (WebCore::shouldAskForNavigationConfirmation):
+        (WebCore::FrameLoader::dispatchBeforeUnloadEvent):
+
</ins><span class="cx"> 2017-02-19  Carlos Garcia Campos  &lt;cgarcia@igalia.com&gt;
</span><span class="cx"> 
</span><span class="cx">         [SOUP] Call SoupNetworkSession::setShouldIgnoreTLSErrors when testRunner.setAllowsAnySSLCertificate() is called
</span></span></pre></div>
<a id="trunkSourceWebCorebindingsjsJSEventListenercpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/bindings/js/JSEventListener.cpp (212624 => 212625)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/bindings/js/JSEventListener.cpp        2017-02-20 06:34:15 UTC (rev 212624)
+++ trunk/Source/WebCore/bindings/js/JSEventListener.cpp        2017-02-20 06:36:04 UTC (rev 212625)
</span><span class="lines">@@ -25,6 +25,7 @@
</span><span class="cx"> #include &quot;Event.h&quot;
</span><span class="cx"> #include &quot;Frame.h&quot;
</span><span class="cx"> #include &quot;HTMLElement.h&quot;
</span><ins>+#include &quot;JSDOMConvert.h&quot;
</ins><span class="cx"> #include &quot;JSDocument.h&quot;
</span><span class="cx"> #include &quot;JSEvent.h&quot;
</span><span class="cx"> #include &quot;JSEventTarget.h&quot;
</span><span class="lines">@@ -73,6 +74,16 @@
</span><span class="cx">     visitor.append(m_jsFunction);
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+static void handleBeforeUnloadEventReturnValue(BeforeUnloadEvent&amp; event, const String&amp; returnValue)
+{
+    if (returnValue.isNull())
+        return;
+
+    event.preventDefault();
+    if (event.returnValue().isEmpty())
+        event.setReturnValue(returnValue);
+}
+
</ins><span class="cx"> void JSEventListener::handleEvent(ScriptExecutionContext* scriptExecutionContext, Event* event)
</span><span class="cx"> {
</span><span class="cx">     ASSERT(scriptExecutionContext);
</span><span class="lines">@@ -159,8 +170,9 @@
</span><span class="cx">             event-&gt;target()-&gt;uncaughtExceptionInEventHandler();
</span><span class="cx">             reportException(exec, exception);
</span><span class="cx">         } else {
</span><del>-            if (!retval.isUndefinedOrNull() &amp;&amp; is&lt;BeforeUnloadEvent&gt;(*event))
-                downcast&lt;BeforeUnloadEvent&gt;(*event).setReturnValue(retval.toWTFString(exec));
</del><ins>+            if (is&lt;BeforeUnloadEvent&gt;(*event))
+                handleBeforeUnloadEventReturnValue(downcast&lt;BeforeUnloadEvent&gt;(*event), convert&lt;IDLNullable&lt;IDLDOMString&gt;&gt;(*exec, retval, StringConversionConfiguration::Normal));
+
</ins><span class="cx">             if (m_isAttribute) {
</span><span class="cx">                 if (retval.isFalse())
</span><span class="cx">                     event-&gt;preventDefault();
</span></span></pre></div>
<a id="trunkSourceWebCoreloaderFrameLoadercpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/loader/FrameLoader.cpp (212624 => 212625)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/loader/FrameLoader.cpp        2017-02-20 06:34:15 UTC (rev 212624)
+++ trunk/Source/WebCore/loader/FrameLoader.cpp        2017-02-20 06:36:04 UTC (rev 212625)
</span><span class="lines">@@ -2975,6 +2975,16 @@
</span><span class="cx">         m_frame.document()-&gt;removeAllEventListeners();
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+static bool shouldAskForNavigationConfirmation(const BeforeUnloadEvent&amp; event)
+{
+    // Web pages can request we ask for confirmation before navigating by:
+    // - Cancelling the BeforeUnloadEvent (modern way)
+    // - Setting the returnValue attribute on the BeforeUnloadEvent to a non-empty string.
+    // - Returning a non-empty string from the event handler, which is then set as returnValue
+    //   attribute on the BeforeUnloadEvent.
+    return event.defaultPrevented() || !event.returnValue().isEmpty();
+}
+
</ins><span class="cx"> bool FrameLoader::dispatchBeforeUnloadEvent(Chrome&amp; chrome, FrameLoader* frameLoaderBeingNavigated)
</span><span class="cx"> {
</span><span class="cx">     DOMWindow* domWindow = m_frame.document()-&gt;domWindow();
</span><span class="lines">@@ -2998,7 +3008,8 @@
</span><span class="cx"> 
</span><span class="cx">     if (!beforeUnloadEvent-&gt;defaultPrevented())
</span><span class="cx">         document-&gt;defaultEventHandler(beforeUnloadEvent.get());
</span><del>-    if (beforeUnloadEvent-&gt;returnValue().isNull())
</del><ins>+
+    if (!shouldAskForNavigationConfirmation(beforeUnloadEvent))
</ins><span class="cx">         return true;
</span><span class="cx"> 
</span><span class="cx">     // If the navigating FrameLoader has already shown a beforeunload confirmation panel for the current navigation attempt,
</span></span></pre>
</div>
</div>

</body>
</html>