Changeset 269983 in webkit


Ignore:
Timestamp:
Nov 18, 2020 1:57:01 PM (3 years ago)
Author:
Chris Dumez
Message:

[iOS] beforeunload event does not fire in MobileSafari
https://bugs.webkit.org/show_bug.cgi?id=219102
<rdar://problem/70550655>

Reviewed by Geoff Garen.

Source/WebCore:

MobileSafari on iOS does not implement WKUIDelegate's runJavaScriptAlertPanelWithMessage because
it never shows any before unload prompt. When the client does not implement this delegate,
Chrome::canRunBeforeUnloadConfirmPanel() returns false and this was causing
FrameLoader::shouldClose() to return early, before even firing the beforeunload event in each
frame. I updated our logic so that we now fire the beforeunload events no matter what and we
merely do not attempt to show the beforeunload prompt when Chrome::canRunBeforeUnloadConfirmPanel()
return false, similarly to what we do when the document does not have a user gesture.

Note that we already fire the pagehide and unload events on iOS so this is not a significant
change in policy.

  • loader/FrameLoader.cpp:

(WebCore::FrameLoader::shouldClose):
(WebCore::FrameLoader::dispatchBeforeUnloadEvent):

Tools:

Add API test coverage.

  • TestWebKitAPI/Tests/WebKit/beforeunload.html:
  • TestWebKitAPI/Tests/WebKitCocoa/ModalAlerts.mm:

(-[UIDelegateWithoutRunBeforeUnload webViewDidClose:]):
(-[BeforeUnloadMessageHandler userContentController:didReceiveScriptMessage:]):
(TEST):

Location:
trunk
Files:
5 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebCore/ChangeLog

    r269977 r269983  
     12020-11-18  Chris Dumez  <cdumez@apple.com>
     2
     3        [iOS] beforeunload event does not fire in MobileSafari
     4        https://bugs.webkit.org/show_bug.cgi?id=219102
     5        <rdar://problem/70550655>
     6
     7        Reviewed by Geoff Garen.
     8
     9        MobileSafari on iOS does not implement WKUIDelegate's runJavaScriptAlertPanelWithMessage because
     10        it never shows any before unload prompt. When the client does not implement this delegate,
     11        Chrome::canRunBeforeUnloadConfirmPanel() returns false and this was causing
     12        FrameLoader::shouldClose() to return early, before even firing the beforeunload event in each
     13        frame. I updated our logic so that we now fire the beforeunload events no matter what and we
     14        merely do not attempt to show the beforeunload prompt when Chrome::canRunBeforeUnloadConfirmPanel()
     15        return false, similarly to what we do when the document does not have a user gesture.
     16
     17        Note that we already fire the pagehide and unload events on iOS so this is not a significant
     18        change in policy.
     19
     20        * loader/FrameLoader.cpp:
     21        (WebCore::FrameLoader::shouldClose):
     22        (WebCore::FrameLoader::dispatchBeforeUnloadEvent):
     23
    1242020-11-18  Aditya Keerthi  <akeerthi@apple.com>
    225
  • trunk/Source/WebCore/loader/FrameLoader.cpp

    r269953 r269983  
    32353235    if (!page)
    32363236        return true;
    3237     if (!page->chrome().canRunBeforeUnloadConfirmPanel())
    3238         return true;
    32393237
    32403238    // Store all references to each subframe in advance since beforeunload's event handler may modify frame
     
    33663364        document->defaultEventHandler(beforeUnloadEvent.get());
    33673365
    3368     if (!shouldAskForNavigationConfirmation(*document, beforeUnloadEvent))
     3366    if (!chrome.canRunBeforeUnloadConfirmPanel() || !shouldAskForNavigationConfirmation(*document, beforeUnloadEvent))
    33693367        return true;
    33703368
  • trunk/Tools/ChangeLog

    r269960 r269983  
     12020-11-18  Chris Dumez  <cdumez@apple.com>
     2
     3        [iOS] beforeunload event does not fire in MobileSafari
     4        https://bugs.webkit.org/show_bug.cgi?id=219102
     5        <rdar://problem/70550655>
     6
     7        Reviewed by Geoff Garen.
     8
     9        Add API test coverage.
     10
     11        * TestWebKitAPI/Tests/WebKit/beforeunload.html:
     12        * TestWebKitAPI/Tests/WebKitCocoa/ModalAlerts.mm:
     13        (-[UIDelegateWithoutRunBeforeUnload webViewDidClose:]):
     14        (-[BeforeUnloadMessageHandler userContentController:didReceiveScriptMessage:]):
     15        (TEST):
     16
    1172020-11-18  Chris Dumez  <cdumez@apple.com>
    218
  • trunk/Tools/TestWebKitAPI/Tests/WebKit/beforeunload.html

    r254711 r269983  
    44<script>
    55onbeforeunload = (ev) => {
     6    if (window.webkit && webkit.messageHandlers.testHandler)
     7        webkit.messageHandlers.testHandler.postMessage("beforeunload called");
    68    ev.preventDefault();
    79    return "foo";
  • trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/ModalAlerts.mm

    r260366 r269983  
    314314
    315315#endif
     316
     317static bool wasBeforeUnloadEventHandlerCalled = false;
     318
     319@interface UIDelegateWithoutRunBeforeUnload : NSObject <WKUIDelegate>
     320@end
     321
     322@implementation UIDelegateWithoutRunBeforeUnload
     323
     324- (void)webViewDidClose:(WKWebView *)webView
     325{
     326    [webView _close];
     327}
     328
     329@end
     330
     331@interface BeforeUnloadMessageHandler : NSObject <WKScriptMessageHandler>
     332@end
     333
     334@implementation BeforeUnloadMessageHandler
     335- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message
     336{
     337    NSString *scriptMessage = [message body];
     338    EXPECT_WK_STREQ(@"beforeunload called", scriptMessage);
     339    wasBeforeUnloadEventHandlerCalled = true;
     340}
     341@end
     342
     343TEST(WebKit, BeforeUnloadEventWithoutRunBeforeUnloadConfirmPanelUIDelegate)
     344{
     345    auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]);
     346    auto messageHandler = adoptNS([[BeforeUnloadMessageHandler alloc] init]);
     347    [[webViewConfiguration userContentController] addScriptMessageHandler:messageHandler.get() name:@"testHandler"];
     348
     349    auto uiDelegate = adoptNS([[UIDelegateWithoutRunBeforeUnload alloc] init]);
     350    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]);
     351    [webView setUIDelegate:uiDelegate.get()];
     352
     353    [webView synchronouslyLoadTestPageNamed:@"beforeunload"];
     354
     355    TestWebKitAPI::Util::spinRunLoop(10);
     356
     357    [webView _tryClose];
     358
     359    // The beforeunload event handler should get called even if the client application does not
     360    // have a UIDelegate that can show the before unload prompt.
     361    TestWebKitAPI::Util::run(&wasBeforeUnloadEventHandlerCalled);
     362
     363    // The view should get closed.
     364    while (![webView _isClosed])
     365        TestWebKitAPI::Util::sleep(0.1);
     366}
Note: See TracChangeset for help on using the changeset viewer.