[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)
commit99a7431d6ff90d7448d3e2f6be00dd963480d817
tree82754943973db290ba06e9d1f2fd37c75fef6245
parent744c1ab9cbf978e5c6787d32aa922966e405ae92
[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.

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