Title: [269983] trunk
Revision
269983
Author
[email protected]
Date
2020-11-18 13:57:01 -0800 (Wed, 18 Nov 2020)

Log 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):

Modified Paths

Diff

Modified: trunk/Source/WebCore/ChangeLog (269982 => 269983)


--- trunk/Source/WebCore/ChangeLog	2020-11-18 21:47:20 UTC (rev 269982)
+++ trunk/Source/WebCore/ChangeLog	2020-11-18 21:57:01 UTC (rev 269983)
@@ -1,3 +1,26 @@
+2020-11-18  Chris Dumez  <[email protected]>
+
+        [iOS] beforeunload event does not fire in MobileSafari
+        https://bugs.webkit.org/show_bug.cgi?id=219102
+        <rdar://problem/70550655>
+
+        Reviewed by Geoff Garen.
+
+        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):
+
 2020-11-18  Aditya Keerthi  <[email protected]>
 
         [iOS][FCR] Use new glyph for -webkit-list-button

Modified: trunk/Source/WebCore/loader/FrameLoader.cpp (269982 => 269983)


--- trunk/Source/WebCore/loader/FrameLoader.cpp	2020-11-18 21:47:20 UTC (rev 269982)
+++ trunk/Source/WebCore/loader/FrameLoader.cpp	2020-11-18 21:57:01 UTC (rev 269983)
@@ -3234,8 +3234,6 @@
     Page* page = m_frame.page();
     if (!page)
         return true;
-    if (!page->chrome().canRunBeforeUnloadConfirmPanel())
-        return true;
 
     // Store all references to each subframe in advance since beforeunload's event handler may modify frame
     Vector<Ref<Frame>, 16> targetFrames;
@@ -3365,7 +3363,7 @@
     if (!beforeUnloadEvent->defaultPrevented())
         document->defaultEventHandler(beforeUnloadEvent.get());
 
-    if (!shouldAskForNavigationConfirmation(*document, beforeUnloadEvent))
+    if (!chrome.canRunBeforeUnloadConfirmPanel() || !shouldAskForNavigationConfirmation(*document, beforeUnloadEvent))
         return true;
 
     // If the navigating FrameLoader has already shown a beforeunload confirmation panel for the current navigation attempt,

Modified: trunk/Tools/ChangeLog (269982 => 269983)


--- trunk/Tools/ChangeLog	2020-11-18 21:47:20 UTC (rev 269982)
+++ trunk/Tools/ChangeLog	2020-11-18 21:57:01 UTC (rev 269983)
@@ -1,5 +1,21 @@
 2020-11-18  Chris Dumez  <[email protected]>
 
+        [iOS] beforeunload event does not fire in MobileSafari
+        https://bugs.webkit.org/show_bug.cgi?id=219102
+        <rdar://problem/70550655>
+
+        Reviewed by Geoff Garen.
+
+        Add API test coverage.
+
+        * TestWebKitAPI/Tests/WebKit/beforeunload.html:
+        * TestWebKitAPI/Tests/WebKitCocoa/ModalAlerts.mm:
+        (-[UIDelegateWithoutRunBeforeUnload webViewDidClose:]):
+        (-[BeforeUnloadMessageHandler userContentController:didReceiveScriptMessage:]):
+        (TEST):
+
+2020-11-18  Chris Dumez  <[email protected]>
+
         navigator.clipboard is not exposed on *.localhost pages
         https://bugs.webkit.org/show_bug.cgi?id=219020
 

Modified: trunk/Tools/TestWebKitAPI/Tests/WebKit/beforeunload.html (269982 => 269983)


--- trunk/Tools/TestWebKitAPI/Tests/WebKit/beforeunload.html	2020-11-18 21:47:20 UTC (rev 269982)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKit/beforeunload.html	2020-11-18 21:57:01 UTC (rev 269983)
@@ -3,6 +3,8 @@
 <head>
 <script>
 _onbeforeunload_ = (ev) => {
+    if (window.webkit && webkit.messageHandlers.testHandler)
+        webkit.messageHandlers.testHandler.postMessage("beforeunload called");
     ev.preventDefault();
     return "foo";
 }

Modified: trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/ModalAlerts.mm (269982 => 269983)


--- trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/ModalAlerts.mm	2020-11-18 21:47:20 UTC (rev 269982)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/ModalAlerts.mm	2020-11-18 21:57:01 UTC (rev 269983)
@@ -313,3 +313,54 @@
 }
 
 #endif
+
+static bool wasBeforeUnloadEventHandlerCalled = false;
+
+@interface UIDelegateWithoutRunBeforeUnload : NSObject <WKUIDelegate>
+@end
+
+@implementation UIDelegateWithoutRunBeforeUnload
+
+- (void)webViewDidClose:(WKWebView *)webView
+{
+    [webView _close];
+}
+
+@end
+
+@interface BeforeUnloadMessageHandler : NSObject <WKScriptMessageHandler>
+@end
+
+@implementation BeforeUnloadMessageHandler
+- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message
+{
+    NSString *scriptMessage = [message body];
+    EXPECT_WK_STREQ(@"beforeunload called", scriptMessage);
+    wasBeforeUnloadEventHandlerCalled = true;
+}
+@end
+
+TEST(WebKit, BeforeUnloadEventWithoutRunBeforeUnloadConfirmPanelUIDelegate)
+{
+    auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]);
+    auto messageHandler = adoptNS([[BeforeUnloadMessageHandler alloc] init]);
+    [[webViewConfiguration userContentController] addScriptMessageHandler:messageHandler.get() name:@"testHandler"];
+
+    auto uiDelegate = adoptNS([[UIDelegateWithoutRunBeforeUnload alloc] init]);
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]);
+    [webView setUIDelegate:uiDelegate.get()];
+
+    [webView synchronouslyLoadTestPageNamed:@"beforeunload"];
+
+    TestWebKitAPI::Util::spinRunLoop(10);
+
+    [webView _tryClose];
+
+    // The beforeunload event handler should get called even if the client application does not
+    // have a UIDelegate that can show the before unload prompt.
+    TestWebKitAPI::Util::run(&wasBeforeUnloadEventHandlerCalled);
+
+    // The view should get closed.
+    while (![webView _isClosed])
+        TestWebKitAPI::Util::sleep(0.1);
+}
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to