[iOS] Consistent 1 sec hang when triggering modal alerts while handling synchronous...
authorwenson_hsieh@apple.com <wenson_hsieh@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 1 Feb 2019 23:12:14 +0000 (23:12 +0000)
committerwenson_hsieh@apple.com <wenson_hsieh@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 1 Feb 2019 23:12:14 +0000 (23:12 +0000)
https://bugs.webkit.org/show_bug.cgi?id=194140
<rdar://problem/47728098>

Reviewed by Tim Horton.

Source/WebKit:

Currently, the UI process hangs when attempting to synchronously present modal UI from the web process while the
UI process is waiting for sync IPC in the web process. While we have logic to generally mitigate IPC deadlock in
this scenario by dispatching the web process' sync IPC immediately with the intention of allowing the web
process to finish processing sync IPC (and consequently unblock the UI process), this fails in the case where
the sync IPC message from the web process to the UI process requires main thread execution for an arbitrary
amount of time (for instance, modal alert dialogs). In this case, we'll end up in a state where we've handled
the web process' sync IPC in the UI process, yet we can't resume execution since the web process is still
blocked.

By far the most egregious scenario in which this manifests is during synchronous gesture recognizer IPC, i.e.
grabbing position information from the UI process, and handling touch events synchronously. Luckily, these are
also cases where (1) we know sync IPC may safely time out, and (2) the presentation of modal UI from the web
process should cause the gesture recognizers to fail anyways. As such, we can mitigate these scenarios in the
web process by responding to the these pending sync IPC messages *before* sending our own sync IPC to the UI
process.

Test: fast/events/touch/ios/show-modal-alert-during-touch-start.html

* Shared/ios/InteractionInformationAtPosition.h:
(WebKit::InteractionInformationAtPosition::invalidInformation):
* Shared/ios/InteractionInformationAtPosition.mm:
(WebKit::InteractionInformationAtPosition::encode const):
(WebKit::InteractionInformationAtPosition::decode):

Add a new flag to indicate whether an interaction information response can be valid. Interaction information
cannot be valid in the case where the interaction information request was interrupted by certain sync IPC
messages from the web process.

* UIProcess/API/C/WKContextConfigurationRef.cpp:
(WKContextConfigurationIgnoreSynchronousMessagingTimeoutsForTesting):
(WKContextConfigurationSetIgnoreSynchronousMessagingTimeoutsForTesting):

Add some testing SPI to ignore sync IPC timeouts, for the purposes of testing. Rather than use the existing
Objective-C SPI in WKWebProcessPoolConfiguration, I decided to add C API plumbing for this flag, so that other
non-Cocoa ports may also support the new layout test option to ignore sync IPC timeouts.

* UIProcess/API/C/WKContextConfigurationRef.h:
* UIProcess/ios/WKContentViewInteraction.mm:
(-[WKContentView ensurePositionInformationIsUpToDate:]):
(-[WKContentView _positionInformationDidChange:]):
* WebProcess/WebCoreSupport/WebChromeClient.cpp:
(WebKit::WebChromeClient::runBeforeUnloadConfirmPanel):
(WebKit::WebChromeClient::runJavaScriptAlert):
(WebKit::WebChromeClient::runJavaScriptConfirm):
(WebKit::WebChromeClient::runJavaScriptPrompt):
(WebKit::WebChromeClient::print):
(WebKit::WebChromeClient::exceededDatabaseQuota):
(WebKit::WebChromeClient::reachedApplicationCacheOriginQuota):

Cancel any pending sync IPC replies prior to sending sync IPC messages to the UI process which may result in
sync IPC deadlock, by using the new helper method, sendSyncWithDelayedReply, instead of just sendSync.

* WebProcess/WebPage/WebPage.cpp:
(WebKit::WebPage::cancelGesturesBlockedOnSynchronousReplies):

Add a helper to cancel pending sync messages coming in from the UI process that are being called from within
gesture recognizer delegate hooks.

(WebKit::WebPage::touchEventSync):
* WebProcess/WebPage/WebPage.h:

Add a new helper, sendSyncWithDelayedReply, to be used when sending a sync IPC message to the UI process that
cannot be immediately completed upon arrival. Importantly, this cancels pending sync replies, and also passes
IPC::SendSyncOption::InformPlatformProcessWillSuspend.

* WebProcess/WebPage/WebPage.messages.in:

Change these from LegacySync to Delayed messages.

* WebProcess/WebPage/ios/WebPageIOS.mm:
(WebKit::WebPage::getPositionInformation):

Make this sync IPC handler (as well as WebPage::touchEventSync) store the IPC reply during the scope of the
method, and invoke the stored reply at the end of the method if it wasn't interrupted due to something calling
cancelGesturesBlockedOnSynchronousReplies().

(WebKit::WebPage::positionInformation):

Refactor getPositionInformation by pulling out the logic for building an InteractionInformationAtPosition into
a separate helper.

(WebKit::WebPage::requestPositionInformation):

Tools:

* WebKitTestRunner/InjectedBundle/Bindings/TestRunner.idl:
* WebKitTestRunner/InjectedBundle/TestRunner.cpp:
(WTR::TestRunner::setShouldDismissJavaScriptAlertsAsynchronously):

Add a new TestRunner hook to make modal JavaScript alerts dismiss asynchronously. This is used by the new layout
test to induce an IPC deadlock when presenting a modal alert during touch start.

* WebKitTestRunner/InjectedBundle/TestRunner.h:
* WebKitTestRunner/TestController.cpp:
(WTR::runJavaScriptAlert):

Add a client callback function for running JavaScript alerts.

(WTR::TestController::createOtherPage):
(WTR::TestController::generateContextConfiguration const):

Add a test option to disable IPC timeouts for a layout test. This forces the test to reliably time out without
the fix in this patch.

(WTR::TestController::createWebViewWithOptions):

Plumb TestOptions to generateContextConfiguration.

(WTR::TestController::resetPreferencesToConsistentValues):
(WTR::TestController::resetStateToConsistentValues):
(WTR::updateTestOptionsFromTestHeader):
(WTR::TestController::setShouldDismissJavaScriptAlertsAsynchronously):
(WTR::TestController::handleJavaScriptAlert):
* WebKitTestRunner/TestController.h:
* WebKitTestRunner/TestInvocation.cpp:
(WTR::TestInvocation::didReceiveSynchronousMessageFromInjectedBundle):
* WebKitTestRunner/TestOptions.h:
(WTR::TestOptions::hasSameInitializationOptions const):

LayoutTests:

Add a test that induces sync IPC deadlock by presenting a modal alert while handling touch start. This test
forces sync IPC timeouts to be disabled, and passes if we do not time out while handling a touch.

* fast/events/touch/ios/show-modal-alert-during-touch-start-expected.txt: Added.
* fast/events/touch/ios/show-modal-alert-during-touch-start.html: Added.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@240882 268f45cc-cd09-0410-ab3c-d52691b4dbfc

22 files changed:
LayoutTests/ChangeLog
LayoutTests/fast/events/touch/ios/show-modal-alert-during-touch-start-expected.txt [new file with mode: 0644]
LayoutTests/fast/events/touch/ios/show-modal-alert-during-touch-start.html [new file with mode: 0644]
Source/WebKit/ChangeLog
Source/WebKit/Shared/ios/InteractionInformationAtPosition.h
Source/WebKit/Shared/ios/InteractionInformationAtPosition.mm
Source/WebKit/UIProcess/API/C/WKContextConfigurationRef.cpp
Source/WebKit/UIProcess/API/C/WKContextConfigurationRef.h
Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm
Source/WebKit/WebProcess/WebCoreSupport/WebChromeClient.cpp
Source/WebKit/WebProcess/WebPage/WebPage.cpp
Source/WebKit/WebProcess/WebPage/WebPage.h
Source/WebKit/WebProcess/WebPage/WebPage.messages.in
Source/WebKit/WebProcess/WebPage/ios/WebPageIOS.mm
Tools/ChangeLog
Tools/WebKitTestRunner/InjectedBundle/Bindings/TestRunner.idl
Tools/WebKitTestRunner/InjectedBundle/TestRunner.cpp
Tools/WebKitTestRunner/InjectedBundle/TestRunner.h
Tools/WebKitTestRunner/TestController.cpp
Tools/WebKitTestRunner/TestController.h
Tools/WebKitTestRunner/TestInvocation.cpp
Tools/WebKitTestRunner/TestOptions.h

index 395610b..6061b6f 100644 (file)
@@ -1,3 +1,17 @@
+2019-02-01  Wenson Hsieh  <wenson_hsieh@apple.com>
+
+        [iOS] Consistent 1 sec hang when triggering modal alerts while handling synchronous touch events
+        https://bugs.webkit.org/show_bug.cgi?id=194140
+        <rdar://problem/47728098>
+
+        Reviewed by Tim Horton.
+
+        Add a test that induces sync IPC deadlock by presenting a modal alert while handling touch start. This test
+        forces sync IPC timeouts to be disabled, and passes if we do not time out while handling a touch.
+
+        * fast/events/touch/ios/show-modal-alert-during-touch-start-expected.txt: Added.
+        * fast/events/touch/ios/show-modal-alert-during-touch-start.html: Added.
+
 2019-02-01  Antoine Quint  <graouts@apple.com>
 
         Dispatch pointercancel events when content is panned or zoomed on iOS
diff --git a/LayoutTests/fast/events/touch/ios/show-modal-alert-during-touch-start-expected.txt b/LayoutTests/fast/events/touch/ios/show-modal-alert-during-touch-start-expected.txt
new file mode 100644 (file)
index 0000000..681709a
--- /dev/null
@@ -0,0 +1,3 @@
+ALERT: This is a modal alert.
+Tap to show an alert
+This test verifies that presenting a modal alert while handling a synchronous touch event doesn't cause the application process to hang. To verify manually, tap the red area above, and check that the JavaScript alert is presented with no significant delay.
diff --git a/LayoutTests/fast/events/touch/ios/show-modal-alert-during-touch-start.html b/LayoutTests/fast/events/touch/ios/show-modal-alert-during-touch-start.html
new file mode 100644 (file)
index 0000000..9b3abc0
--- /dev/null
@@ -0,0 +1,38 @@
+<!DOCTYPE html> <!-- webkit-test-runner [ useFlexibleViewport=true, ignoreSynchronousMessagingTimeoutsForTesting=true ] -->
+<html>
+<head>
+<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
+<script src="../../../../resources/ui-helper.js"></script>
+<style>
+body, html {
+    width: 100%;
+    height: 100%;
+    margin: 0;
+}
+
+#target {
+    background-color: tomato;
+    color: white;
+    width: 100%;
+    height: 200px;
+    text-align: center;
+    font-size: 20px;
+}
+</style>
+</head>
+<body>
+<div id="target">Tap to show an alert</div>
+<p>This test verifies that presenting a modal alert while handling a synchronous touch event doesn't cause the application process to hang. To verify manually, tap the red area above, and check that the JavaScript alert is presented with no significant delay.</p>
+<script>
+if (window.testRunner) {
+    testRunner.setShouldDismissJavaScriptAlertsAsynchronously(true);
+    testRunner.waitUntilDone();
+    testRunner.dumpAsText();
+}
+
+target.addEventListener("touchstart", () => alert("This is a modal alert."));
+target.addEventListener("touchend", () => testRunner.notifyDone());
+addEventListener("load", async () => await UIHelper.activateAt(100, 100));
+</script>
+</body>
+</html>
index 122728a..b743a8c 100644 (file)
@@ -1,3 +1,94 @@
+2019-02-01  Wenson Hsieh  <wenson_hsieh@apple.com>
+
+        [iOS] Consistent 1 sec hang when triggering modal alerts while handling synchronous touch events
+        https://bugs.webkit.org/show_bug.cgi?id=194140
+        <rdar://problem/47728098>
+
+        Reviewed by Tim Horton.
+
+        Currently, the UI process hangs when attempting to synchronously present modal UI from the web process while the
+        UI process is waiting for sync IPC in the web process. While we have logic to generally mitigate IPC deadlock in
+        this scenario by dispatching the web process' sync IPC immediately with the intention of allowing the web
+        process to finish processing sync IPC (and consequently unblock the UI process), this fails in the case where
+        the sync IPC message from the web process to the UI process requires main thread execution for an arbitrary
+        amount of time (for instance, modal alert dialogs). In this case, we'll end up in a state where we've handled
+        the web process' sync IPC in the UI process, yet we can't resume execution since the web process is still
+        blocked.
+
+        By far the most egregious scenario in which this manifests is during synchronous gesture recognizer IPC, i.e.
+        grabbing position information from the UI process, and handling touch events synchronously. Luckily, these are
+        also cases where (1) we know sync IPC may safely time out, and (2) the presentation of modal UI from the web
+        process should cause the gesture recognizers to fail anyways. As such, we can mitigate these scenarios in the
+        web process by responding to the these pending sync IPC messages *before* sending our own sync IPC to the UI
+        process.
+
+        Test: fast/events/touch/ios/show-modal-alert-during-touch-start.html
+
+        * Shared/ios/InteractionInformationAtPosition.h:
+        (WebKit::InteractionInformationAtPosition::invalidInformation):
+        * Shared/ios/InteractionInformationAtPosition.mm:
+        (WebKit::InteractionInformationAtPosition::encode const):
+        (WebKit::InteractionInformationAtPosition::decode):
+
+        Add a new flag to indicate whether an interaction information response can be valid. Interaction information
+        cannot be valid in the case where the interaction information request was interrupted by certain sync IPC
+        messages from the web process.
+
+        * UIProcess/API/C/WKContextConfigurationRef.cpp:
+        (WKContextConfigurationIgnoreSynchronousMessagingTimeoutsForTesting):
+        (WKContextConfigurationSetIgnoreSynchronousMessagingTimeoutsForTesting):
+
+        Add some testing SPI to ignore sync IPC timeouts, for the purposes of testing. Rather than use the existing
+        Objective-C SPI in WKWebProcessPoolConfiguration, I decided to add C API plumbing for this flag, so that other
+        non-Cocoa ports may also support the new layout test option to ignore sync IPC timeouts.
+
+        * UIProcess/API/C/WKContextConfigurationRef.h:
+        * UIProcess/ios/WKContentViewInteraction.mm:
+        (-[WKContentView ensurePositionInformationIsUpToDate:]):
+        (-[WKContentView _positionInformationDidChange:]):
+        * WebProcess/WebCoreSupport/WebChromeClient.cpp:
+        (WebKit::WebChromeClient::runBeforeUnloadConfirmPanel):
+        (WebKit::WebChromeClient::runJavaScriptAlert):
+        (WebKit::WebChromeClient::runJavaScriptConfirm):
+        (WebKit::WebChromeClient::runJavaScriptPrompt):
+        (WebKit::WebChromeClient::print):
+        (WebKit::WebChromeClient::exceededDatabaseQuota):
+        (WebKit::WebChromeClient::reachedApplicationCacheOriginQuota):
+
+        Cancel any pending sync IPC replies prior to sending sync IPC messages to the UI process which may result in
+        sync IPC deadlock, by using the new helper method, sendSyncWithDelayedReply, instead of just sendSync.
+
+        * WebProcess/WebPage/WebPage.cpp:
+        (WebKit::WebPage::cancelGesturesBlockedOnSynchronousReplies):
+
+        Add a helper to cancel pending sync messages coming in from the UI process that are being called from within
+        gesture recognizer delegate hooks.
+
+        (WebKit::WebPage::touchEventSync):
+        * WebProcess/WebPage/WebPage.h:
+
+        Add a new helper, sendSyncWithDelayedReply, to be used when sending a sync IPC message to the UI process that
+        cannot be immediately completed upon arrival. Importantly, this cancels pending sync replies, and also passes
+        IPC::SendSyncOption::InformPlatformProcessWillSuspend.
+
+        * WebProcess/WebPage/WebPage.messages.in:
+
+        Change these from LegacySync to Delayed messages.
+
+        * WebProcess/WebPage/ios/WebPageIOS.mm:
+        (WebKit::WebPage::getPositionInformation):
+
+        Make this sync IPC handler (as well as WebPage::touchEventSync) store the IPC reply during the scope of the
+        method, and invoke the stored reply at the end of the method if it wasn't interrupted due to something calling
+        cancelGesturesBlockedOnSynchronousReplies().
+
+        (WebKit::WebPage::positionInformation):
+
+        Refactor getPositionInformation by pulling out the logic for building an InteractionInformationAtPosition into
+        a separate helper.
+
+        (WebKit::WebPage::requestPositionInformation):
+
 2019-02-01  David Quesada  <david_quesada@apple.com>
 
         Network Process crash when resuming downloads: '-[__NSDictionaryI setObject:forKey:]: unrecognized selector sent to instance %p'
index 71bce80..cdb454e 100644 (file)
 namespace WebKit {
 
 struct InteractionInformationAtPosition {
+    static InteractionInformationAtPosition invalidInformation()
+    {
+        InteractionInformationAtPosition response;
+        response.canBeValid = false;
+        return response;
+    }
+
     InteractionInformationRequest request;
 
+    bool canBeValid { true };
     bool nodeAtPositionIsFocusedElement { false };
 #if ENABLE(DATA_INTERACTION)
     bool hasSelectionAtPosition { false };
index acda819..73f3df8 100644 (file)
@@ -43,6 +43,7 @@ void InteractionInformationAtPosition::encode(IPC::Encoder& encoder) const
 {
     encoder << request;
 
+    encoder << canBeValid;
     encoder << nodeAtPositionIsFocusedElement;
 #if ENABLE(DATA_INTERACTION)
     encoder << hasSelectionAtPosition;
@@ -89,6 +90,9 @@ bool InteractionInformationAtPosition::decode(IPC::Decoder& decoder, Interaction
     if (!decoder.decode(result.request))
         return false;
 
+    if (!decoder.decode(result.canBeValid))
+        return false;
+
     if (!decoder.decode(result.nodeAtPositionIsFocusedElement))
         return false;
 
index 86d4129..338c0dd 100644 (file)
@@ -138,6 +138,16 @@ void WKContextConfigurationSetFullySynchronousModeIsAllowedForTesting(WKContextC
     toImpl(configuration)->setFullySynchronousModeIsAllowedForTesting(allowed);
 }
 
+bool WKContextConfigurationIgnoreSynchronousMessagingTimeoutsForTesting(WKContextConfigurationRef configuration)
+{
+    return toImpl(configuration)->ignoreSynchronousMessagingTimeoutsForTesting();
+}
+
+void WKContextConfigurationSetIgnoreSynchronousMessagingTimeoutsForTesting(WKContextConfigurationRef configuration, bool ignore)
+{
+    toImpl(configuration)->setIgnoreSynchronousMessagingTimeoutsForTesting(ignore);
+}
+
 WKArrayRef WKContextConfigurationCopyOverrideLanguages(WKContextConfigurationRef configuration)
 {
     return toAPI(&API::Array::createStringArray(toImpl(configuration)->overrideLanguages()).leakRef());
index 3269417..ad3e7a1 100644 (file)
@@ -63,6 +63,9 @@ WK_EXPORT void WKContextConfigurationSetResourceLoadStatisticsDirectory(WKContex
 WK_EXPORT bool WKContextConfigurationFullySynchronousModeIsAllowedForTesting(WKContextConfigurationRef configuration);
 WK_EXPORT void WKContextConfigurationSetFullySynchronousModeIsAllowedForTesting(WKContextConfigurationRef configuration, bool allowed);
 
+WK_EXPORT bool WKContextConfigurationIgnoreSynchronousMessagingTimeoutsForTesting(WKContextConfigurationRef configuration);
+WK_EXPORT void WKContextConfigurationSetIgnoreSynchronousMessagingTimeoutsForTesting(WKContextConfigurationRef configuration, bool ignore);
+
 WK_EXPORT WKArrayRef WKContextConfigurationCopyOverrideLanguages(WKContextConfigurationRef configuration);
 WK_EXPORT void WKContextConfigurationSetOverrideLanguages(WKContextConfigurationRef configuration, WKArrayRef overrideLanguages);
 
index 7a98642..2122840 100644 (file)
@@ -1763,7 +1763,8 @@ static inline bool isSamePair(UIGestureRecognizer *a, UIGestureRecognizer *b, UI
     if ([self _hasValidOutstandingPositionInformationRequest:request])
         return connection->waitForAndDispatchImmediately<Messages::WebPageProxy::DidReceivePositionInformation>(_page->pageID(), 1_s, IPC::WaitForOption::InterruptWaitingIfSyncMessageArrives);
 
-    _hasValidPositionInformation = _page->process().sendSync(Messages::WebPage::GetPositionInformation(request), Messages::WebPage::GetPositionInformation::Reply(_positionInformation), _page->pageID(), 1_s);
+    bool receivedResponse = _page->process().sendSync(Messages::WebPage::GetPositionInformation(request), Messages::WebPage::GetPositionInformation::Reply(_positionInformation), _page->pageID(), 1_s);
+    _hasValidPositionInformation = receivedResponse && _positionInformation.canBeValid;
     
     // FIXME: We need to clean up these handlers in the event that we are not able to collect data, or if the WebProcess crashes.
     if (_hasValidPositionInformation)
@@ -2284,7 +2285,7 @@ static void cancelPotentialTapIfNecessary(WKContentView* contentView)
     newInfo.mergeCompatibleOptionalInformation(_positionInformation);
 
     _positionInformation = newInfo;
-    _hasValidPositionInformation = YES;
+    _hasValidPositionInformation = _positionInformation.canBeValid;
     if (_actionSheetAssistant)
         [_actionSheetAssistant updateSheetPosition];
     [self _invokeAndRemovePendingHandlersValidForCurrentPositionInformation];
index 6c8245f..9a68415 100644 (file)
@@ -405,7 +405,7 @@ bool WebChromeClient::runBeforeUnloadConfirmPanel(const String& message, Frame&
 
     HangDetectionDisabler hangDetectionDisabler;
 
-    if (!WebProcess::singleton().parentProcessConnection()->sendSync(Messages::WebPageProxy::RunBeforeUnloadConfirmPanel(webFrame->frameID(), SecurityOriginData::fromFrame(&frame), message), Messages::WebPageProxy::RunBeforeUnloadConfirmPanel::Reply(shouldClose), m_page.pageID(), Seconds::infinity(), IPC::SendSyncOption::InformPlatformProcessWillSuspend))
+    if (!m_page.sendSyncWithDelayedReply(Messages::WebPageProxy::RunBeforeUnloadConfirmPanel(webFrame->frameID(), SecurityOriginData::fromFrame(&frame), message), Messages::WebPageProxy::RunBeforeUnloadConfirmPanel::Reply(shouldClose)))
         return false;
 
     return shouldClose;
@@ -451,7 +451,7 @@ void WebChromeClient::runJavaScriptAlert(Frame& frame, const String& alertText)
 
     HangDetectionDisabler hangDetectionDisabler;
 
-    WebProcess::singleton().parentProcessConnection()->sendSync(Messages::WebPageProxy::RunJavaScriptAlert(webFrame->frameID(), SecurityOriginData::fromFrame(&frame), alertText), Messages::WebPageProxy::RunJavaScriptAlert::Reply(), m_page.pageID(), Seconds::infinity(), IPC::SendSyncOption::InformPlatformProcessWillSuspend);
+    m_page.sendSyncWithDelayedReply(Messages::WebPageProxy::RunJavaScriptAlert(webFrame->frameID(), SecurityOriginData::fromFrame(&frame), alertText), Messages::WebPageProxy::RunJavaScriptAlert::Reply());
 }
 
 bool WebChromeClient::runJavaScriptConfirm(Frame& frame, const String& message)
@@ -468,7 +468,7 @@ bool WebChromeClient::runJavaScriptConfirm(Frame& frame, const String& message)
     HangDetectionDisabler hangDetectionDisabler;
 
     bool result = false;
-    if (!WebProcess::singleton().parentProcessConnection()->sendSync(Messages::WebPageProxy::RunJavaScriptConfirm(webFrame->frameID(), SecurityOriginData::fromFrame(&frame), message), Messages::WebPageProxy::RunJavaScriptConfirm::Reply(result), m_page.pageID(), Seconds::infinity(), IPC::SendSyncOption::InformPlatformProcessWillSuspend))
+    if (!m_page.sendSyncWithDelayedReply(Messages::WebPageProxy::RunJavaScriptConfirm(webFrame->frameID(), SecurityOriginData::fromFrame(&frame), message), Messages::WebPageProxy::RunJavaScriptConfirm::Reply(result)))
         return false;
 
     return result;
@@ -487,7 +487,7 @@ bool WebChromeClient::runJavaScriptPrompt(Frame& frame, const String& message, c
 
     HangDetectionDisabler hangDetectionDisabler;
 
-    if (!WebProcess::singleton().parentProcessConnection()->sendSync(Messages::WebPageProxy::RunJavaScriptPrompt(webFrame->frameID(), SecurityOriginData::fromFrame(&frame), message, defaultValue), Messages::WebPageProxy::RunJavaScriptPrompt::Reply(result), m_page.pageID(), Seconds::infinity(), IPC::SendSyncOption::InformPlatformProcessWillSuspend))
+    if (!m_page.sendSyncWithDelayedReply(Messages::WebPageProxy::RunJavaScriptPrompt(webFrame->frameID(), SecurityOriginData::fromFrame(&frame), message, defaultValue), Messages::WebPageProxy::RunJavaScriptPrompt::Reply(result)))
         return false;
 
     return !result.isNull();
@@ -714,7 +714,7 @@ void WebChromeClient::print(Frame& frame)
     }
 #endif
 
-    m_page.sendSync(Messages::WebPageProxy::PrintFrame(webFrame->frameID()), Messages::WebPageProxy::PrintFrame::Reply(), Seconds::infinity(), IPC::SendSyncOption::InformPlatformProcessWillSuspend);
+    m_page.sendSyncWithDelayedReply(Messages::WebPageProxy::PrintFrame(webFrame->frameID()), Messages::WebPageProxy::PrintFrame::Reply());
 }
 
 void WebChromeClient::exceededDatabaseQuota(Frame& frame, const String& databaseName, DatabaseDetails details)
@@ -731,11 +731,8 @@ void WebChromeClient::exceededDatabaseQuota(Frame& frame, const String& database
     auto securityOrigin = API::SecurityOrigin::create(SecurityOriginData::fromDatabaseIdentifier(originData.databaseIdentifier())->securityOrigin());
     newQuota = m_page.injectedBundleUIClient().didExceedDatabaseQuota(&m_page, securityOrigin.ptr(), databaseName, details.displayName(), currentQuota, currentOriginUsage, details.currentUsage(), details.expectedUsage());
 
-    if (!newQuota) {
-        WebProcess::singleton().parentProcessConnection()->sendSync(
-            Messages::WebPageProxy::ExceededDatabaseQuota(webFrame->frameID(), originData.databaseIdentifier(), databaseName, details.displayName(), currentQuota, currentOriginUsage, details.currentUsage(), details.expectedUsage()),
-            Messages::WebPageProxy::ExceededDatabaseQuota::Reply(newQuota), m_page.pageID(), Seconds::infinity(), IPC::SendSyncOption::InformPlatformProcessWillSuspend);
-    }
+    if (!newQuota)
+        m_page.sendSyncWithDelayedReply(Messages::WebPageProxy::ExceededDatabaseQuota(webFrame->frameID(), originData.databaseIdentifier(), databaseName, details.displayName(), currentQuota, currentOriginUsage, details.currentUsage(), details.expectedUsage()), Messages::WebPageProxy::ExceededDatabaseQuota::Reply(newQuota));
 
     tracker.setQuota(originData, newQuota);
 }
@@ -757,9 +754,7 @@ void WebChromeClient::reachedApplicationCacheOriginQuota(SecurityOrigin& origin,
         return;
 
     uint64_t newQuota = 0;
-    WebProcess::singleton().parentProcessConnection()->sendSync(
-        Messages::WebPageProxy::ReachedApplicationCacheOriginQuota(origin.data().databaseIdentifier(), currentQuota, totalBytesNeeded),
-        Messages::WebPageProxy::ReachedApplicationCacheOriginQuota::Reply(newQuota), m_page.pageID(), Seconds::infinity(), IPC::SendSyncOption::InformPlatformProcessWillSuspend);
+    m_page.sendSyncWithDelayedReply(Messages::WebPageProxy::ReachedApplicationCacheOriginQuota(origin.data().databaseIdentifier(), currentQuota, totalBytesNeeded), Messages::WebPageProxy::ReachedApplicationCacheOriginQuota::Reply(newQuota));
 
     cacheStorage.storeUpdatedQuotaForOrigin(&origin, newQuota);
 }
index 2a5e4c6..8526740 100644 (file)
 #endif
 
 #if PLATFORM(IOS_FAMILY)
+#include "InteractionInformationAtPosition.h"
+#include "InteractionInformationRequest.h"
 #include "RemoteLayerTreeDrawingArea.h"
 #include <CoreGraphics/CoreGraphics.h>
 #include <WebCore/Icon.h>
@@ -2725,6 +2727,19 @@ void WebPage::requestFontAttributesAtSelectionStart(CallbackID callbackID)
     send(Messages::WebPageProxy::FontAttributesCallback(attributes, callbackID));
 }
 
+void WebPage::cancelGesturesBlockedOnSynchronousReplies()
+{
+#if ENABLE(IOS_TOUCH_EVENTS)
+    if (auto reply = WTFMove(m_pendingSynchronousTouchEventReply))
+        reply(true);
+#endif
+
+#if PLATFORM(IOS_FAMILY)
+    if (auto reply = WTFMove(m_pendingSynchronousPositionInformationReply))
+        reply(InteractionInformationAtPosition::invalidInformation());
+#endif
+}
+
 #if ENABLE(TOUCH_EVENTS)
 static bool handleTouchEvent(const WebTouchEvent& touchEvent, Page* page)
 {
@@ -2746,13 +2761,19 @@ void WebPage::dispatchTouchEvent(const WebTouchEvent& touchEvent, bool& handled)
     updatePotentialTapSecurityOrigin(touchEvent, handled);
 }
 
-void WebPage::touchEventSync(const WebTouchEvent& touchEvent, bool& handled)
+void WebPage::touchEventSync(const WebTouchEvent& touchEvent, CompletionHandler<void(bool)>&& reply)
 {
+    m_pendingSynchronousTouchEventReply = WTFMove(reply);
+
     EventDispatcher::TouchEventQueue queuedEvents;
     WebProcess::singleton().eventDispatcher().getQueuedTouchEventsForPage(*this, queuedEvents);
     dispatchAsynchronousTouchEvents(queuedEvents);
 
+    bool handled = true;
     dispatchTouchEvent(touchEvent, handled);
+
+    if (auto reply = WTFMove(m_pendingSynchronousTouchEventReply))
+        reply(handled);
 }
 
 void WebPage::updatePotentialTapSecurityOrigin(const WebTouchEvent& touchEvent, bool wasHandled)
index 22d05d5..6eaa68f 100644 (file)
@@ -647,7 +647,7 @@ public:
     void syncApplyAutocorrection(const String& correction, const String& originalText, bool& correctionApplied);
     void requestAutocorrectionContext(CallbackID);
     void getAutocorrectionContext(String& beforeText, String& markedText, String& selectedText, String& afterText, uint64_t& location, uint64_t& length);
-    void getPositionInformation(const InteractionInformationRequest&, InteractionInformationAtPosition&);
+    void getPositionInformation(const InteractionInformationRequest&, CompletionHandler<void(InteractionInformationAtPosition&&)>&&);
     void requestPositionInformation(const InteractionInformationRequest&);
     void startInteractionWithElementAtPosition(const WebCore::IntPoint&);
     void stopInteraction();
@@ -1137,6 +1137,13 @@ public:
 
     void didReceiveWebPageMessage(IPC::Connection&, IPC::Decoder&);
 
+    template<typename T>
+    bool sendSyncWithDelayedReply(T&& message, typename T::Reply&& reply)
+    {
+        cancelGesturesBlockedOnSynchronousReplies();
+        return sendSync(WTFMove(message), WTFMove(reply), m_pageID, Seconds::infinity(), IPC::SendSyncOption::InformPlatformProcessWillSuspend);
+    }
+
 private:
     WebPage(uint64_t pageID, WebPageCreationParameters&&);
 
@@ -1178,6 +1185,9 @@ private:
     void resetTextAutosizing();
     WebCore::VisiblePosition visiblePositionInFocusedNodeForPoint(const WebCore::Frame&, const WebCore::IntPoint&, bool isInteractingWithFocusedElement);
     RefPtr<WebCore::Range> rangeForGranularityAtPoint(WebCore::Frame&, const WebCore::IntPoint&, uint32_t granularity, bool isInteractingWithFocusedElement);
+
+    void sendPositionInformation(InteractionInformationAtPosition&&);
+    InteractionInformationAtPosition positionInformation(const InteractionInformationRequest&);
 #endif
 
 #if PLATFORM(IOS_FAMILY) && ENABLE(DATA_INTERACTION)
@@ -1234,7 +1244,7 @@ private:
     void keyEvent(const WebKeyboardEvent&);
 
 #if ENABLE(IOS_TOUCH_EVENTS)
-    void touchEventSync(const WebTouchEvent&, bool& handled);
+    void touchEventSync(const WebTouchEvent&, CompletionHandler<void(bool)>&&);
     void updatePotentialTapSecurityOrigin(const WebTouchEvent&, bool wasHandled);
 #elif ENABLE(TOUCH_EVENTS)
     void touchEvent(const WebTouchEvent&);
@@ -1493,6 +1503,8 @@ private:
 
     bool canShowMIMEType(const String&, const Function<bool(const String&, WebCore::PluginData::AllowedPluginTypes)>& supportsPlugin) const;
 
+    void cancelGesturesBlockedOnSynchronousReplies();
+
     uint64_t m_pageID;
 
     std::unique_ptr<WebCore::Page> m_page;
@@ -1699,6 +1711,10 @@ private:
     RefPtr<WebCore::Element> m_focusedElement;
     bool m_hasPendingBlurNotification { false };
     bool m_hasPendingEditorStateUpdate { false };
+
+#if ENABLE(IOS_TOUCH_EVENTS)
+    CompletionHandler<void(bool)> m_pendingSynchronousTouchEventReply;
+#endif
     
 #if PLATFORM(IOS_FAMILY)
     RefPtr<WebCore::Range> m_currentWordRange;
@@ -1744,6 +1760,8 @@ private:
     Optional<DynamicViewportSizeUpdateID> m_pendingDynamicViewportSizeUpdateID;
     double m_lastTransactionPageScaleFactor { 0 };
     uint64_t m_lastTransactionIDWithScaleChange { 0 };
+
+    CompletionHandler<void(InteractionInformationAtPosition&&)> m_pendingSynchronousPositionInformationReply;
 #endif
 
     WebCore::Timer m_layerVolatilityTimer;
index fc1ce33..a9cc91d 100644 (file)
@@ -80,7 +80,7 @@ messages -> WebPage LegacyReceiver {
     SyncApplyAutocorrection(String correction, String originalText) -> (bool autocorrectionApplied) LegacySync
     RequestAutocorrectionContext(WebKit::CallbackID callbackID)
     GetAutocorrectionContext() -> (String beforeContext, String markedText, String selectedText, String afterContext, uint64_t location, uint64_t length) LegacySync
-    GetPositionInformation(struct WebKit::InteractionInformationRequest request) -> (struct WebKit::InteractionInformationAtPosition information) LegacySync
+    GetPositionInformation(struct WebKit::InteractionInformationRequest request) -> (struct WebKit::InteractionInformationAtPosition information) Delayed
     RequestPositionInformation(struct WebKit::InteractionInformationRequest request)
     StartInteractionWithElementAtPosition(WebCore::IntPoint point)
     StopInteraction()
@@ -121,7 +121,7 @@ messages -> WebPage LegacyReceiver {
 #endif
 
 #if ENABLE(IOS_TOUCH_EVENTS)
-    TouchEventSync(WebKit::WebTouchEvent event) -> (bool handled) LegacySync
+    TouchEventSync(WebKit::WebTouchEvent event) -> (bool handled) Delayed
 #endif
 #if !ENABLE(IOS_TOUCH_EVENTS) && ENABLE(TOUCH_EVENTS)
     TouchEvent(WebKit::WebTouchEvent event)
index d894467..5d1b1dd 100644 (file)
@@ -2085,8 +2085,19 @@ static inline bool isAssistableElement(Element& node)
     return node.isContentEditable();
 }
 
-void WebPage::getPositionInformation(const InteractionInformationRequest& request, InteractionInformationAtPosition& info)
+void WebPage::getPositionInformation(const InteractionInformationRequest& request, CompletionHandler<void(InteractionInformationAtPosition&&)>&& reply)
 {
+    m_pendingSynchronousPositionInformationReply = WTFMove(reply);
+
+    auto information = positionInformation(request);
+
+    if (auto reply = WTFMove(m_pendingSynchronousPositionInformationReply))
+        reply(WTFMove(information));
+}
+
+InteractionInformationAtPosition WebPage::positionInformation(const InteractionInformationRequest& request)
+{
+    InteractionInformationAtPosition info;
     info.request = request;
 
     FloatPoint adjustedPoint;
@@ -2262,14 +2273,13 @@ void WebPage::getPositionInformation(const InteractionInformationRequest& reques
     info.hasSelectionAtPosition = m_page->hasSelectionAtPosition(adjustedPoint);
 #endif
     info.adjustedPointForNodeRespondingToClickEvents = adjustedPoint;
+
+    return info;
 }
 
 void WebPage::requestPositionInformation(const InteractionInformationRequest& request)
 {
-    InteractionInformationAtPosition info;
-
-    getPositionInformation(request, info);
-    send(Messages::WebPageProxy::DidReceivePositionInformation(info));
+    send(Messages::WebPageProxy::DidReceivePositionInformation(positionInformation(request)));
 }
 
 void WebPage::startInteractionWithElementAtPosition(const WebCore::IntPoint& point)
index 4bc80bd..ef3cf8f 100644 (file)
@@ -1,3 +1,45 @@
+2019-02-01  Wenson Hsieh  <wenson_hsieh@apple.com>
+
+        [iOS] Consistent 1 sec hang when triggering modal alerts while handling synchronous touch events
+        https://bugs.webkit.org/show_bug.cgi?id=194140
+        <rdar://problem/47728098>
+
+        Reviewed by Tim Horton.
+
+        * WebKitTestRunner/InjectedBundle/Bindings/TestRunner.idl:
+        * WebKitTestRunner/InjectedBundle/TestRunner.cpp:
+        (WTR::TestRunner::setShouldDismissJavaScriptAlertsAsynchronously):
+
+        Add a new TestRunner hook to make modal JavaScript alerts dismiss asynchronously. This is used by the new layout
+        test to induce an IPC deadlock when presenting a modal alert during touch start.
+
+        * WebKitTestRunner/InjectedBundle/TestRunner.h:
+        * WebKitTestRunner/TestController.cpp:
+        (WTR::runJavaScriptAlert):
+
+        Add a client callback function for running JavaScript alerts.
+
+        (WTR::TestController::createOtherPage):
+        (WTR::TestController::generateContextConfiguration const):
+
+        Add a test option to disable IPC timeouts for a layout test. This forces the test to reliably time out without
+        the fix in this patch.
+
+        (WTR::TestController::createWebViewWithOptions):
+
+        Plumb TestOptions to generateContextConfiguration.
+
+        (WTR::TestController::resetPreferencesToConsistentValues):
+        (WTR::TestController::resetStateToConsistentValues):
+        (WTR::updateTestOptionsFromTestHeader):
+        (WTR::TestController::setShouldDismissJavaScriptAlertsAsynchronously):
+        (WTR::TestController::handleJavaScriptAlert):
+        * WebKitTestRunner/TestController.h:
+        * WebKitTestRunner/TestInvocation.cpp:
+        (WTR::TestInvocation::didReceiveSynchronousMessageFromInjectedBundle):
+        * WebKitTestRunner/TestOptions.h:
+        (WTR::TestOptions::hasSameInitializationOptions const):
+
 2019-02-01  Chris Dumez  <cdumez@apple.com>
 
         REGRESSION: Flaky ASSERTION FAILED: m_uncommittedState.state == State::Committed on http/tests/cookies/same-site/fetch-after-top-level-navigation-initiated-from-iframe-in-cross-origin-page.html
index f671a0a..e4f2cf8 100644 (file)
@@ -329,6 +329,9 @@ interface TestRunner {
     // Open panel
     void setOpenPanelFiles(object filesArray);
 
+    // Modal alerts
+    void setShouldDismissJavaScriptAlertsAsynchronously(boolean value);
+
     void setWebRTCMDNSICECandidatesEnabled(boolean value);
     void setWebRTCUnifiedPlanEnabled(boolean value);
     void setCustomUserAgent(DOMString userAgent);
index a659d16..0ec92eb 100644 (file)
@@ -2715,4 +2715,11 @@ unsigned long TestRunner::serverTrustEvaluationCallbackCallsCount()
     return WKUInt64GetValue(adoptWK(static_cast<WKUInt64Ref>(returnData)).get());
 }
 
+void TestRunner::setShouldDismissJavaScriptAlertsAsynchronously(bool shouldDismissAsynchronously)
+{
+    WKRetainPtr<WKStringRef> messageName(AdoptWK, WKStringCreateWithUTF8CString("ShouldDismissJavaScriptAlertsAsynchronously"));
+    WKRetainPtr<WKBooleanRef> messageBody(AdoptWK, WKBooleanCreate(shouldDismissAsynchronously));
+    WKBundlePostSynchronousMessage(InjectedBundle::singleton().bundle(), messageName.get(), messageBody.get(), nullptr);
+}
+
 } // namespace WTR
index 94a9a15..372c9f0 100644 (file)
@@ -448,6 +448,9 @@ public:
     // Open panel
     void setOpenPanelFiles(JSValueRef);
 
+    // Modal alerts
+    void setShouldDismissJavaScriptAlertsAsynchronously(bool);
+
     void terminateNetworkProcess();
     void terminateServiceWorkerProcess();
 
index 6c7e97a..8072b36 100644 (file)
@@ -239,6 +239,11 @@ static void decidePolicyForUserMediaPermissionRequest(WKPageRef, WKFrameRef fram
     TestController::singleton().handleUserMediaPermissionRequest(frame, userMediaDocumentOrigin, topLevelDocumentOrigin, permissionRequest);
 }
 
+static void runJavaScriptAlert(WKPageRef page, WKStringRef alertText, WKFrameRef frame, WKSecurityOriginRef securityOrigin, WKPageRunJavaScriptAlertResultListenerRef listener, const void *clientInfo)
+{
+    TestController::singleton().handleJavaScriptAlert(listener);
+}
+
 static void checkUserMediaPermissionForOrigin(WKPageRef, WKFrameRef frame, WKSecurityOriginRef userMediaDocumentOrigin, WKSecurityOriginRef topLevelDocumentOrigin, WKUserMediaPermissionCheckRef checkRequest, const void*)
 {
     TestController::singleton().handleCheckOfUserMediaPermissionForOrigin(frame, userMediaDocumentOrigin, topLevelDocumentOrigin, checkRequest);
@@ -326,7 +331,7 @@ WKPageRef TestController::createOtherPage(PlatformWebView* parentView, WKPageCon
         0, // runJavaScriptPrompt
         0, // mediaSessionMetadataDidChange
         createOtherPage,
-        0, // runJavaScriptAlert
+        runJavaScriptAlert,
         0, // runJavaScriptConfirm
         0, // runJavaScriptPrompt
         checkUserMediaPermissionForOrigin,
@@ -442,11 +447,12 @@ void TestController::initialize(int argc, const char* argv[])
     m_pageGroup.adopt(WKPageGroupCreateWithIdentifier(pageGroupIdentifier.get()));
 }
 
-WKRetainPtr<WKContextConfigurationRef> TestController::generateContextConfiguration() const
+WKRetainPtr<WKContextConfigurationRef> TestController::generateContextConfiguration(const TestOptions& options) const
 {
     auto configuration = adoptWK(WKContextConfigurationCreate());
     WKContextConfigurationSetInjectedBundlePath(configuration.get(), injectedBundlePath());
     WKContextConfigurationSetFullySynchronousModeIsAllowedForTesting(configuration.get(), true);
+    WKContextConfigurationSetIgnoreSynchronousMessagingTimeoutsForTesting(configuration.get(), options.ignoreSynchronousMessagingTimeoutsForTesting);
 
     if (const char* dumpRenderTreeTemp = libraryPathForTesting()) {
         String temporaryFolder = String::fromUTF8(dumpRenderTreeTemp);
@@ -534,7 +540,7 @@ WKRetainPtr<WKPageConfigurationRef> TestController::generatePageConfiguration(WK
 
 void TestController::createWebViewWithOptions(const TestOptions& options)
 {
-    auto contextConfiguration = generateContextConfiguration();
+    auto contextConfiguration = generateContextConfiguration(options);
 
     WKRetainPtr<WKMutableArrayRef> overrideLanguages = adoptWK(WKMutableArrayCreate());
     for (auto& language : options.overrideLanguages)
@@ -614,7 +620,7 @@ void TestController::createWebViewWithOptions(const TestOptions& options)
         0, // runJavaScriptPrompt
         0, // mediaSessionMetadataDidChange
         createOtherPage,
-        0, // runJavaScriptAlert
+        runJavaScriptAlert,
         0, // runJavaScriptConfirm
         0, // runJavaScriptPrompt
         checkUserMediaPermissionForOrigin,
@@ -824,8 +830,6 @@ void TestController::resetPreferencesToConsistentValues(const TestOptions& optio
 
     WKPreferencesSetWebSQLDisabled(preferences, false);
 
-    m_serverTrustEvaluationCallbackCallsCount = 0;
-
     platformResetPreferencesToConsistentValues();
 }
 
@@ -957,6 +961,8 @@ bool TestController::resetStateToConsistentValues(const TestOptions& options, Re
     statisticsResetToConsistentState();
 
     m_didReceiveServerRedirectForProvisionalNavigation = false;
+    m_serverTrustEvaluationCallbackCallsCount = 0;
+    m_shouldDismissJavaScriptAlertsAsynchronously = false;
 
     // Reset main page back to about:blank
     m_doneResetting = false;
@@ -1278,6 +1284,8 @@ static void updateTestOptionsFromTestHeader(TestOptions& testOptions, const std:
             testOptions.enableUndoManagerAPI = parseBooleanTestHeaderValue(value);
         else if (key == "contentInset.top")
             testOptions.contentInsetTop = std::stod(value);
+        else if (key == "ignoreSynchronousMessagingTimeoutsForTesting")
+            testOptions.ignoreSynchronousMessagingTimeoutsForTesting = parseBooleanTestHeaderValue(value);
         pairStart = pairEnd + 1;
     }
 }
@@ -2330,6 +2338,25 @@ void TestController::resetUserMediaPermission()
     m_isUserMediaPermissionSet = false;
 }
 
+void TestController::setShouldDismissJavaScriptAlertsAsynchronously(bool value)
+{
+    m_shouldDismissJavaScriptAlertsAsynchronously = value;
+}
+
+void TestController::handleJavaScriptAlert(WKPageRunJavaScriptAlertResultListenerRef listener)
+{
+    if (!m_shouldDismissJavaScriptAlertsAsynchronously) {
+        WKPageRunJavaScriptAlertResultListenerCall(listener);
+        return;
+    }
+
+    WKRetain(listener);
+    callOnMainThread([listener] {
+        WKPageRunJavaScriptAlertResultListenerCall(listener);
+        WKRelease(listener);
+    });
+}
+
 class OriginSettings : public RefCounted<OriginSettings> {
 public:
     explicit OriginSettings()
index 25ec904..e939171 100644 (file)
@@ -284,9 +284,12 @@ public:
     bool canDoServerTrustEvaluationInNetworkProcess() const;
     uint64_t serverTrustEvaluationCallbackCallsCount() const { return m_serverTrustEvaluationCallbackCallsCount; }
 
+    void setShouldDismissJavaScriptAlertsAsynchronously(bool);
+    void handleJavaScriptAlert(WKPageRunJavaScriptAlertResultListenerRef);
+
 private:
     WKRetainPtr<WKPageConfigurationRef> generatePageConfiguration(WKContextConfigurationRef);
-    WKRetainPtr<WKContextConfigurationRef> generateContextConfiguration() const;
+    WKRetainPtr<WKContextConfigurationRef> generateContextConfiguration(const TestOptions&) const;
     void initialize(int argc, const char* argv[]);
     void createWebViewWithOptions(const TestOptions&);
     void run();
@@ -539,6 +542,7 @@ private:
     HashMap<uint64_t, AbandonedDocumentInfo> m_abandonedDocumentInfo;
 
     uint64_t m_serverTrustEvaluationCallbackCallsCount { 0 };
+    bool m_shouldDismissJavaScriptAlertsAsynchronously { false };
 };
 
 struct TestCommand {
index 429ce29..89656fb 100644 (file)
@@ -1539,6 +1539,13 @@ WKRetainPtr<WKTypeRef> TestInvocation::didReceiveSynchronousMessageFromInjectedB
         return result;
     }
 
+    if (WKStringIsEqualToUTF8CString(messageName, "ShouldDismissJavaScriptAlertsAsynchronously")) {
+        ASSERT(WKGetTypeID(messageBody) == WKBooleanGetTypeID());
+        WKBooleanRef value = static_cast<WKBooleanRef>(messageBody);
+        TestController::singleton().setShouldDismissJavaScriptAlertsAsynchronously(WKBooleanGetValue(value));
+        return nullptr;
+    }
+
     ASSERT_NOT_REACHED();
     return nullptr;
 }
index 614ce7b..f91dd50 100644 (file)
@@ -68,6 +68,7 @@ struct TestOptions {
     bool enableEditableImages { false };
     bool editable { false };
     bool enableUndoManagerAPI { false };
+    bool ignoreSynchronousMessagingTimeoutsForTesting { false };
 
     double contentInsetTop { 0 };
 
@@ -116,7 +117,8 @@ struct TestOptions {
             || enableEditableImages != options.enableEditableImages
             || editable != options.editable
             || enableUndoManagerAPI != options.enableUndoManagerAPI
-            || contentInsetTop != options.contentInsetTop)
+            || contentInsetTop != options.contentInsetTop
+            || ignoreSynchronousMessagingTimeoutsForTesting != options.ignoreSynchronousMessagingTimeoutsForTesting)
             return false;
 
         if (experimentalFeatures != options.experimentalFeatures)