Bug 168382 - onbeforeunload event return value coercion is not per-spec
Summary: onbeforeunload event return value coercion is not per-spec
Status: RESOLVED FIXED
Alias: None
Product: WebKit
Classification: Unclassified
Component: Page Loading (show other bugs)
Version: Safari Technology Preview
Hardware: Mac macOS 10.12
: P2 Normal
Assignee: Chris Dumez
URL: https://html.spec.whatwg.org/#the-eve...
Keywords: WebExposed
Depends on:
Blocks:
 
Reported: 2017-02-15 11:32 PST by Domenic Denicola
Modified: 2017-02-24 08:35 PST (History)
10 users (show)

See Also:


Attachments
Patch (23.82 KB, patch)
2017-02-15 19:04 PST, Chris Dumez
no flags Details | Formatted Diff | Diff
Patch (22.52 KB, patch)
2017-02-17 12:51 PST, Chris Dumez
no flags Details | Formatted Diff | Diff
Patch (17.10 KB, patch)
2017-02-17 13:16 PST, Chris Dumez
no flags Details | Formatted Diff | Diff
Patch (16.13 KB, patch)
2017-02-17 13:18 PST, Chris Dumez
no flags Details | Formatted Diff | Diff
Patch (21.48 KB, patch)
2017-02-19 17:58 PST, Chris Dumez
no flags Details | Formatted Diff | Diff
Archive of layout-test-results from ews121 for ios-simulator-wk2 (839.35 KB, application/zip)
2017-02-19 19:16 PST, Build Bot
no flags Details
Patch (21.59 KB, patch)
2017-02-19 20:57 PST, Chris Dumez
no flags Details | Formatted Diff | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Domenic Denicola 2017-02-15 11:32:06 PST
Test: http://w3c-test.org/html/browsers/browsing-the-web/unloading-documents/beforeunload-canceling.html
Spec: https://html.spec.whatwg.org/#the-event-handler-processing-algorithm step 4

The return value of an onbeforeunload event handler is supposed to be coerced to a DOMString?, and then any non-null value should cancel the event. That means anything but null or undefined should cancel the event. Safari Tech Preview seems to not cancel when returning "" or true or 0.
Comment 1 Chris Dumez 2017-02-15 13:46:59 PST
Unfortunately, the test in question is flaky on WebKit. It sometimes starts playing with the frames before their content has loaded, thus throwing exceptions.
Comment 2 Domenic Denicola 2017-02-15 13:48:24 PST
Hmm, that shouldn't be possible, at least, I can't see how this code would cause it:

  async_test(t => {
    const iframe = document.createElement("iframe");
    iframe.onload = t.step_func(() => {
      iframe.contentWindow.runTest(t, testCase);
    });

    iframe.src = "beforeunload-canceling-1.html";
    document.body.appendChild(iframe);
  }, `Returning ${testCase.valueToReturn} with a real iframe unloading${labelAboutReturnValue}`);
Comment 3 Chris Dumez 2017-02-15 13:50:06 PST
(In reply to comment #2)
> Hmm, that shouldn't be possible, at least, I can't see how this code would
> cause it:
> 
>   async_test(t => {
>     const iframe = document.createElement("iframe");
>     iframe.onload = t.step_func(() => {
>       iframe.contentWindow.runTest(t, testCase);
>     });
> 
>     iframe.src = "beforeunload-canceling-1.html";
>     document.body.appendChild(iframe);
>   }, `Returning ${testCase.valueToReturn} with a real iframe
> unloading${labelAboutReturnValue}`);

frame.contentWindow.runTest is not a function. (In 'iframe.contentWindow.runTest(t, testCase)', 'iframe.contentWindow.runTest' is undefined)
http://w3c-test.org/html/browsers/browsing-the-web/unloading-documents/beforeunload-canceling.html:128:35
step@http://w3c-test.org/resources/testharness.js:1409:30
http://w3c-test.org/resources/testharness.js:1433:40
appendChild@[native code]
http://w3c-test.org/html/browsers/browsing-the-web/unloading-documents/beforeunload-canceling.html:132:30
step@http://w3c-test.org/resources/testharness.js:1409:30
async_test@http://w3c-test.org/resources/testharness.js:518:26
global code@http://w3c-test.org/html/browsers/browsing-the-web/unloading-documents/beforeunload-canceling.html:125:13
Comment 4 Chris Dumez 2017-02-15 13:53:09 PST
(In reply to comment #3)
> (In reply to comment #2)
> > Hmm, that shouldn't be possible, at least, I can't see how this code would
> > cause it:
> > 
> >   async_test(t => {
> >     const iframe = document.createElement("iframe");
> >     iframe.onload = t.step_func(() => {
> >       iframe.contentWindow.runTest(t, testCase);
> >     });
> > 
> >     iframe.src = "beforeunload-canceling-1.html";
> >     document.body.appendChild(iframe);
> >   }, `Returning ${testCase.valueToReturn} with a real iframe
> > unloading${labelAboutReturnValue}`);
> 
> frame.contentWindow.runTest is not a function. (In
> 'iframe.contentWindow.runTest(t, testCase)', 'iframe.contentWindow.runTest'
> is undefined)
> http://w3c-test.org/html/browsers/browsing-the-web/unloading-documents/
> beforeunload-canceling.html:128:35
> step@http://w3c-test.org/resources/testharness.js:1409:30
> http://w3c-test.org/resources/testharness.js:1433:40
> appendChild@[native code]
> http://w3c-test.org/html/browsers/browsing-the-web/unloading-documents/
> beforeunload-canceling.html:132:30
> step@http://w3c-test.org/resources/testharness.js:1409:30
> async_test@http://w3c-test.org/resources/testharness.js:518:26
> global
> code@http://w3c-test.org/html/browsers/browsing-the-web/unloading-documents/
> beforeunload-canceling.html:125:13

Maybe the unload handler sometimes gets called for the 'about:blank' load somehow? Or the page really has loaded but runTest has not been defined yet when the load event fires.
Comment 5 Domenic Denicola 2017-02-15 14:01:05 PST
Hmm, those make sense, especially the about:blank one. I unfortunately cannot reproduce on my Mac; would you mind working on a test fix, since you are able to reproduce?
Comment 6 Chris Dumez 2017-02-15 19:04:00 PST
Created attachment 301688 [details]
Patch
Comment 7 Chris Dumez 2017-02-17 11:47:51 PST
ping review? Let me know if you'd like me to split the patch. It is not big but it fixes several related bugs in the same patch.
Comment 8 Daniel Bates 2017-02-17 12:32:44 PST
Comment on attachment 301688 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=301688&action=review

> Source/WebCore/loader/FrameLoader.cpp:3039
> -    String text = document->displayStringModifiedByEncoding(beforeUnloadEvent->returnValue());
> -    return chrome.runBeforeUnloadConfirmPanel(text, m_frame);
> +    // The specification says "The message shown to the user is not customizable, but instead determined by the user agent.
> +    // In particular, the actual value of the returnValue attribute is ignored". Therefore, we ignore the returnValue here
> +    // and pass a null string.
> +    return chrome.runBeforeUnloadConfirmPanel(String(), m_frame);

This breaks public WebKit Legacy API on macOS: <https://developer.apple.com/reference/webkit/webuidelegate/1387844-webview?language=objc>. Is this acceptable?
Comment 9 Chris Dumez 2017-02-17 12:35:07 PST
(In reply to comment #8)
> Comment on attachment 301688 [details]
> Patch
> 
> View in context:
> https://bugs.webkit.org/attachment.cgi?id=301688&action=review
> 
> > Source/WebCore/loader/FrameLoader.cpp:3039
> > -    String text = document->displayStringModifiedByEncoding(beforeUnloadEvent->returnValue());
> > -    return chrome.runBeforeUnloadConfirmPanel(text, m_frame);
> > +    // The specification says "The message shown to the user is not customizable, but instead determined by the user agent.
> > +    // In particular, the actual value of the returnValue attribute is ignored". Therefore, we ignore the returnValue here
> > +    // and pass a null string.
> > +    return chrome.runBeforeUnloadConfirmPanel(String(), m_frame);
> 
> This breaks public WebKit Legacy API on macOS:
> <https://developer.apple.com/reference/webkit/webuidelegate/1387844-
> webview?language=objc>. Is this acceptable?

That is a fair point.

Would you find it acceptable to pass the returnValue to the delegate but not print out its value in the layout tests (as this causes flakiness)?
Comment 10 Daniel Bates 2017-02-17 12:44:07 PST
(In reply to comment #9)
> (In reply to comment #8)
> > Comment on attachment 301688 [details]
> > Patch
> > 
> > View in context:
> > https://bugs.webkit.org/attachment.cgi?id=301688&action=review
> > 
> > > Source/WebCore/loader/FrameLoader.cpp:3039
> > > -    String text = document->displayStringModifiedByEncoding(beforeUnloadEvent->returnValue());
> > > -    return chrome.runBeforeUnloadConfirmPanel(text, m_frame);
> > > +    // The specification says "The message shown to the user is not customizable, but instead determined by the user agent.
> > > +    // In particular, the actual value of the returnValue attribute is ignored". Therefore, we ignore the returnValue here
> > > +    // and pass a null string.
> > > +    return chrome.runBeforeUnloadConfirmPanel(String(), m_frame);
> > 
> > This breaks public WebKit Legacy API on macOS:
> > <https://developer.apple.com/reference/webkit/webuidelegate/1387844-
> > webview?language=objc>. Is this acceptable?
> 
> That is a fair point.
> 
> Would you find it acceptable to pass the returnValue to the delegate but not
> print out its value in the layout tests (as this causes flakiness)?

It would be good to understand why we have flakiness? In particular, why does the order of the confirm delegate callbacks change? I mean, I would have assumed they are blocking calls to the UI delegate to ask for confirmation and inconsistent ordering could affect third-part apps.

Regardless, we need to ensure that we do not regress the behavior of the WebKit Legacy API so long as we support it.
Comment 11 Chris Dumez 2017-02-17 12:51:21 PST
(In reply to comment #10)
> (In reply to comment #9)
> > (In reply to comment #8)
> > > Comment on attachment 301688 [details]
> > > Patch
> > > 
> > > View in context:
> > > https://bugs.webkit.org/attachment.cgi?id=301688&action=review
> > > 
> > > > Source/WebCore/loader/FrameLoader.cpp:3039
> > > > -    String text = document->displayStringModifiedByEncoding(beforeUnloadEvent->returnValue());
> > > > -    return chrome.runBeforeUnloadConfirmPanel(text, m_frame);
> > > > +    // The specification says "The message shown to the user is not customizable, but instead determined by the user agent.
> > > > +    // In particular, the actual value of the returnValue attribute is ignored". Therefore, we ignore the returnValue here
> > > > +    // and pass a null string.
> > > > +    return chrome.runBeforeUnloadConfirmPanel(String(), m_frame);
> > > 
> > > This breaks public WebKit Legacy API on macOS:
> > > <https://developer.apple.com/reference/webkit/webuidelegate/1387844-
> > > webview?language=objc>. Is this acceptable?
> > 
> > That is a fair point.
> > 
> > Would you find it acceptable to pass the returnValue to the delegate but not
> > print out its value in the layout tests (as this causes flakiness)?
> 
> It would be good to understand why we have flakiness? In particular, why
> does the order of the confirm delegate callbacks change? I mean, I would
> have assumed they are blocking calls to the UI delegate to ask for
> confirmation and inconsistent ordering could affect third-part apps.

The web-platform-test uses a lot of frames and relies on their load event firing to run each subtest. The order of the load events is not pre-determined so it makes sense that the CONFIRM messages order can change.

> 
> Regardless, we need to ensure that we do not regress the behavior of the
> WebKit Legacy API so long as we support it.

Agreed, I will keep passing the string.
Comment 12 Chris Dumez 2017-02-17 12:51:59 PST
Created attachment 301970 [details]
Patch
Comment 13 Chris Dumez 2017-02-17 12:56:57 PST
Comment on attachment 301970 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=301970&action=review

> LayoutTests/imported/w3c/web-platform-tests/html/browsers/browsing-the-web/unloading-documents/beforeunload-canceling-expected.txt:1
> +CONFIRM NAVIGATION

If people feel strongly about printing the returnValue. I can also propose a change to the web-platform-test upstream to make the order deterministic.
I personally do not feel those messages bring much value here and the specification says to ignore the message, which is why I went this way.
Comment 14 Daniel Bates 2017-02-17 13:04:57 PST
(In reply to comment #11)
> > 
> > Regardless, we need to ensure that we do not regress the behavior of the
> > WebKit Legacy API so long as we support it.
> 
> Agreed, I will keep passing the string.

When I wrote this comment I was implying that we need a way to ensure that we do not regress the behavior of the WebKit Legacy API webView:runBeforeUnloadConfirmPanelWithMessage:initiatedByFrame:. We should not remove the existing test infrastructure that we have (printing the confirm message in layout tests) that ensures that webView:runBeforeUnloadConfirmPanelWithMessage:initiatedByFrame: is called and is passed the confirmation message without having some replacement testing methodology to catch such regressions in behavior. I can think of at least two options: 1. Replace the absence of DumpRenderTree/WebKitTestRunner emitting the confirmation message with TestWebKitAPI test(s) or 2. Provide a window.testRunner setting to disable output of the confirmation message in DumpRenderTree/WebKitTestRunner and enable this setting in web-platform-tests or other tests as needed. I slightly prefer the latter option as it sounds the flakiness is specific to the web-plaoform-tests based on comment 11.
Comment 15 Daniel Bates 2017-02-17 13:06:35 PST
(In reply to comment #13)
> Comment on attachment 301970 [details]
> Patch
> 
> View in context:
> https://bugs.webkit.org/attachment.cgi?id=301970&action=review
> 
> > LayoutTests/imported/w3c/web-platform-tests/html/browsers/browsing-the-web/unloading-documents/beforeunload-canceling-expected.txt:1
> > +CONFIRM NAVIGATION
> 
> [...]
> I can also propose a
> change to the web-platform-test upstream to make the order deterministic.

Please propose such a change. I do not see any benefit to having such non-determinism. It makes reasoning about test failure difficult.
Comment 16 Daniel Bates 2017-02-17 13:07:13 PST
Comment on attachment 301970 [details]
Patch

We should not remove exiting testing support for webView:runBeforeUnloadConfirmPanelWithMessage:initiatedByFrame: without proposing an alternative way of ensuring we do not regress it.
Comment 17 Chris Dumez 2017-02-17 13:07:19 PST
(In reply to comment #14)
> (In reply to comment #11)
> > > 
> > > Regardless, we need to ensure that we do not regress the behavior of the
> > > WebKit Legacy API so long as we support it.
> > 
> > Agreed, I will keep passing the string.
> 
> When I wrote this comment I was implying that we need a way to ensure that
> we do not regress the behavior of the WebKit Legacy API
> webView:runBeforeUnloadConfirmPanelWithMessage:initiatedByFrame:. We should
> not remove the existing test infrastructure that we have (printing the
> confirm message in layout tests) that ensures that
> webView:runBeforeUnloadConfirmPanelWithMessage:initiatedByFrame: is called
> and is passed the confirmation message without having some replacement
> testing methodology to catch such regressions in behavior. I can think of at
> least two options: 1. Replace the absence of DumpRenderTree/WebKitTestRunner
> emitting the confirmation message with TestWebKitAPI test(s) or 2. Provide a
> window.testRunner setting to disable output of the confirmation message in
> DumpRenderTree/WebKitTestRunner and enable this setting in
> web-platform-tests or other tests as needed. I slightly prefer the latter
> option as it sounds the flakiness is specific to the web-plaoform-tests
> based on comment 11.

Ok, I'll do a PR to fix the web-platform-test.
Comment 18 Chris Dumez 2017-02-17 13:08:13 PST
(In reply to comment #16)
> Comment on attachment 301970 [details]
> Patch
> 
> We should not remove exiting testing support for
> webView:runBeforeUnloadConfirmPanelWithMessage:initiatedByFrame: without
> proposing an alternative way of ensuring we do not regress it.

(In reply to comment #15)
> (In reply to comment #13)
> > Comment on attachment 301970 [details]
> > Patch
> > 
> > View in context:
> > https://bugs.webkit.org/attachment.cgi?id=301970&action=review
> > 
> > > LayoutTests/imported/w3c/web-platform-tests/html/browsers/browsing-the-web/unloading-documents/beforeunload-canceling-expected.txt:1
> > > +CONFIRM NAVIGATION
> > 
> > [...]
> > I can also propose a
> > change to the web-platform-test upstream to make the order deterministic.
> 
> Please propose such a change. I do not see any benefit to having such
> non-determinism. It makes reasoning about test failure difficult.

The benefit is that the test likely runs faster but hopefully they will accept the change upstream.
Comment 19 Chris Dumez 2017-02-17 13:16:58 PST
Created attachment 301977 [details]
Patch
Comment 20 Chris Dumez 2017-02-17 13:18:12 PST
Created attachment 301979 [details]
Patch
Comment 21 Chris Dumez 2017-02-17 13:26:14 PST
(In reply to comment #17)
> (In reply to comment #14)
> > (In reply to comment #11)
> > > > 
> > > > Regardless, we need to ensure that we do not regress the behavior of the
> > > > WebKit Legacy API so long as we support it.
> > > 
> > > Agreed, I will keep passing the string.
> > 
> > When I wrote this comment I was implying that we need a way to ensure that
> > we do not regress the behavior of the WebKit Legacy API
> > webView:runBeforeUnloadConfirmPanelWithMessage:initiatedByFrame:. We should
> > not remove the existing test infrastructure that we have (printing the
> > confirm message in layout tests) that ensures that
> > webView:runBeforeUnloadConfirmPanelWithMessage:initiatedByFrame: is called
> > and is passed the confirmation message without having some replacement
> > testing methodology to catch such regressions in behavior. I can think of at
> > least two options: 1. Replace the absence of DumpRenderTree/WebKitTestRunner
> > emitting the confirmation message with TestWebKitAPI test(s) or 2. Provide a
> > window.testRunner setting to disable output of the confirmation message in
> > DumpRenderTree/WebKitTestRunner and enable this setting in
> > web-platform-tests or other tests as needed. I slightly prefer the latter
> > option as it sounds the flakiness is specific to the web-plaoform-tests
> > based on comment 11.
> 
> Ok, I'll do a PR to fix the web-platform-test.

Done: https://github.com/w3c/web-platform-tests/pull/4920
Comment 22 Darin Adler 2017-02-19 10:16:43 PST
Comment on attachment 301979 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=301979&action=review

Looks great. Some thoughts on how to be even more thorough.

> Source/WebCore/ChangeLog:15
> +          explicitely been set by JS).

Typo here in the word explicitly.

> Source/WebCore/bindings/js/JSEventListener.cpp:166
> +                    beforeUnloadEvent.setReturnValue(retval.toWTFString(exec));

I believe that conversion to string can have an observable side effect, due to the fact that toPrimitive gets called on the value if it’s an object. Would be super-thorough to have a few test cases covering this obscure corner of behavior, detecting via the side effect that the value isn’t even converted to a string at all if the return value is already set to a non-empty string but that whether default is prevented has no effect, and also covering exactly what happens if the conversion to string throws an exception.

Regarding that last point, while this is not new to this patch, I just noticed that the code to call the event handler itself uses the version of JSC::profiledCall which explicitly extracts a thrown exception from the execution state. That code calls clearException to avoid leaving it behind in execution state, and then calls a function named uncaughtExceptionInEventHandler. Any exception from the toWTFString call we are making here is not handled that way. I am concerned about that. Seems unlikely to be 100% correct.

Another thought is that this special code for BeforeUnloadEvent is an unwanted distraction here in this JSEventListener code; it would be nicer to factor this out into a helper function, even though it’s just three lines of code. If it weren’t for the fact that we don’t really want to mix JavaScript binding code into the DOM, it would be nice if it was a virtual function so that this code didn’t even need to have a special case for BeforeUnloadEvent. But it’s probably more important to keep bindings separate from the DOM.

> Source/WebCore/loader/FrameLoader.cpp:3001
> +    // Only ask the user to confirm the navigation if the returnValue attribute of the
> +    // event object is not the empty string, or if the event was canceled.
> +    if (beforeUnloadEvent->returnValue().isEmpty() && !beforeUnloadEvent->defaultPrevented())
>          return true;

I am not thrilled with this comment. The comment says *what* the code does, but the code already says that. The comment does not say *why* it’s the right thing to do, and that’s what our comments should try to say. Maybe something more like:

    // Webpages indicate that confirmation is needed before leaving the page by setting the return
    // value of the event or by preventing default event handling, either by making direct calls on
    // the event or by returning a value from an event handler. If none of this was done, show no dialog.

My suggested comment is likely too long, but the point is that it talks about *why* this code is correct rather than stating what the code does. Another way to do this would be to write a named function called isConfirmationNeeded that takes a BeforeUnloadEvent& and put the comment in there. Then the code would say:

    if (!isConfirmationNeeded(*beforeUnloadEvent))
        return true;

And it would require no comment at all. The comment inside isConfirmationNeeded could be even clearer, then. Something like this:

    // Webpages either return a value from an event handler or make function calls directly on the
    // event to let us know that confirmation is needed; either providing a string or just preventing default.
    return !event.returnValue().isEmpty() || event.defaultPrevented();
Comment 23 Chris Dumez 2017-02-19 17:10:55 PST
Comment on attachment 301979 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=301979&action=review

>> Source/WebCore/ChangeLog:15
>> +          explicitely been set by JS).
> 
> Typo here in the word explicitly.

Will fix. I always make this typo :/

>> Source/WebCore/bindings/js/JSEventListener.cpp:166
>> +                    beforeUnloadEvent.setReturnValue(retval.toWTFString(exec));
> 
> I believe that conversion to string can have an observable side effect, due to the fact that toPrimitive gets called on the value if it’s an object. Would be super-thorough to have a few test cases covering this obscure corner of behavior, detecting via the side effect that the value isn’t even converted to a string at all if the return value is already set to a non-empty string but that whether default is prevented has no effect, and also covering exactly what happens if the conversion to string throws an exception.
> 
> Regarding that last point, while this is not new to this patch, I just noticed that the code to call the event handler itself uses the version of JSC::profiledCall which explicitly extracts a thrown exception from the execution state. That code calls clearException to avoid leaving it behind in execution state, and then calls a function named uncaughtExceptionInEventHandler. Any exception from the toWTFString call we are making here is not handled that way. I am concerned about that. Seems unlikely to be 100% correct.
> 
> Another thought is that this special code for BeforeUnloadEvent is an unwanted distraction here in this JSEventListener code; it would be nicer to factor this out into a helper function, even though it’s just three lines of code. If it weren’t for the fact that we don’t really want to mix JavaScript binding code into the DOM, it would be nice if it was a virtual function so that this code didn’t even need to have a special case for BeforeUnloadEvent. But it’s probably more important to keep bindings separate from the DOM.

Regarding your first point. I believe you put your finger on a very subtle bug in our implementation. Because of the type of onBeforeUnloadEventHandler [1]. We should actually always convert the value to a String, except if the input is null or undefined. This is because the IDL type is a DOMString?.

[1] https://html.spec.whatwg.org/#onbeforeunloadeventhandler

>> Source/WebCore/loader/FrameLoader.cpp:3001
>>          return true;
> 
> I am not thrilled with this comment. The comment says *what* the code does, but the code already says that. The comment does not say *why* it’s the right thing to do, and that’s what our comments should try to say. Maybe something more like:
> 
>     // Webpages indicate that confirmation is needed before leaving the page by setting the return
>     // value of the event or by preventing default event handling, either by making direct calls on
>     // the event or by returning a value from an event handler. If none of this was done, show no dialog.
> 
> My suggested comment is likely too long, but the point is that it talks about *why* this code is correct rather than stating what the code does. Another way to do this would be to write a named function called isConfirmationNeeded that takes a BeforeUnloadEvent& and put the comment in there. Then the code would say:
> 
>     if (!isConfirmationNeeded(*beforeUnloadEvent))
>         return true;
> 
> And it would require no comment at all. The comment inside isConfirmationNeeded could be even clearer, then. Something like this:
> 
>     // Webpages either return a value from an event handler or make function calls directly on the
>     // event to let us know that confirmation is needed; either providing a string or just preventing default.
>     return !event.returnValue().isEmpty() || event.defaultPrevented();

Ok, I will integrate this.
Comment 24 Chris Dumez 2017-02-19 17:58:48 PST
Created attachment 302104 [details]
Patch
Comment 25 Chris Dumez 2017-02-19 18:01:23 PST
Comment on attachment 302104 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=302104&action=review

> Source/WebCore/bindings/js/JSEventListener.cpp:77
> +static void handleBeforeUnloadEventReturnValue(BeforeUnloadEvent& event, const String& returnValue)

Moved the logic to a separate function as suggested.

> Source/WebCore/bindings/js/JSEventListener.cpp:174
> +                handleBeforeUnloadEventReturnValue(downcast<BeforeUnloadEvent>(*event), convert<IDLNullable<IDLDOMString>>(*exec, retval, StringConversionConfiguration::Normal));

Make sure we cover the returnValue to a DOMString? no matter what, since this is observable. I also added a test to cover this.

> Source/WebCore/loader/FrameLoader.cpp:2978
> +static bool shouldAskForNavigationConfirmation(const BeforeUnloadEvent& event)

Moved this logic to a separate function as suggested.

> Source/WebCore/loader/FrameLoader.cpp:2980
> +    // Web pages can request we ask for confirmation before navigating by:

Improved the comment.

> LayoutTests/ChangeLog:14
> +        * fast/events/before-unload-return-string-conversion.html: Added.

New test for corner case found through review.
Comment 26 Build Bot 2017-02-19 19:16:36 PST
Comment on attachment 302104 [details]
Patch

Attachment 302104 [details] did not pass ios-sim-ews (ios-simulator-wk2):
Output: http://webkit-queues.webkit.org/results/3156904

New failing tests:
fast/events/before-unload-return-string-conversion.html
Comment 27 Build Bot 2017-02-19 19:16:42 PST
Created attachment 302112 [details]
Archive of layout-test-results from ews121 for ios-simulator-wk2

The attached test failures were seen while running run-webkit-tests on the ios-sim-ews.
Bot: ews121  Port: ios-simulator-wk2  Platform: Mac OS X 10.11.6
Comment 28 Chris Dumez 2017-02-19 20:57:30 PST
Created attachment 302115 [details]
Patch
Comment 29 WebKit Commit Bot 2017-02-19 22:37:48 PST
Comment on attachment 302115 [details]
Patch

Clearing flags on attachment: 302115

Committed r212625: <http://trac.webkit.org/changeset/212625>
Comment 30 WebKit Commit Bot 2017-02-19 22:37:55 PST
All reviewed patches have been landed.  Closing bug.
Comment 31 Darin Adler 2017-02-24 08:34:54 PST
The final patch looks great, but I have two small follow-up thoughts:

1) I think I would have put the conversion to a string into the helper function. I understand that it is critical to always do it, but I still think it’s easier to focus on the code when it’s in the helper function context rather than at the call site.

2) I’m not sure I understand what happens the the exception when the string conversion raises an exception. As I mentioned above, the actual call to the event handler is careful to clear the exception out of the VM if it occurs, but as far as I can tell the code for string conversion does not seem to be doing that; just lets the exception get set and ignores it but does not clear it out.
Comment 32 Darin Adler 2017-02-24 08:35:06 PST
what happens *to* the exception