Changeset 240882 in webkit


Ignore:
Timestamp:
Feb 1, 2019 3:12:14 PM (5 years ago)
Author:
Wenson Hsieh
Message:

[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.
Location:
trunk
Files:
2 added
20 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r240875 r240882  
     12019-02-01  Wenson Hsieh  <wenson_hsieh@apple.com>
     2
     3        [iOS] Consistent 1 sec hang when triggering modal alerts while handling synchronous touch events
     4        https://bugs.webkit.org/show_bug.cgi?id=194140
     5        <rdar://problem/47728098>
     6
     7        Reviewed by Tim Horton.
     8
     9        Add a test that induces sync IPC deadlock by presenting a modal alert while handling touch start. This test
     10        forces sync IPC timeouts to be disabled, and passes if we do not time out while handling a touch.
     11
     12        * fast/events/touch/ios/show-modal-alert-during-touch-start-expected.txt: Added.
     13        * fast/events/touch/ios/show-modal-alert-during-touch-start.html: Added.
     14
    1152019-02-01  Antoine Quint  <graouts@apple.com>
    216
  • trunk/Source/WebKit/ChangeLog

    r240881 r240882  
     12019-02-01  Wenson Hsieh  <wenson_hsieh@apple.com>
     2
     3        [iOS] Consistent 1 sec hang when triggering modal alerts while handling synchronous touch events
     4        https://bugs.webkit.org/show_bug.cgi?id=194140
     5        <rdar://problem/47728098>
     6
     7        Reviewed by Tim Horton.
     8
     9        Currently, the UI process hangs when attempting to synchronously present modal UI from the web process while the
     10        UI process is waiting for sync IPC in the web process. While we have logic to generally mitigate IPC deadlock in
     11        this scenario by dispatching the web process' sync IPC immediately with the intention of allowing the web
     12        process to finish processing sync IPC (and consequently unblock the UI process), this fails in the case where
     13        the sync IPC message from the web process to the UI process requires main thread execution for an arbitrary
     14        amount of time (for instance, modal alert dialogs). In this case, we'll end up in a state where we've handled
     15        the web process' sync IPC in the UI process, yet we can't resume execution since the web process is still
     16        blocked.
     17
     18        By far the most egregious scenario in which this manifests is during synchronous gesture recognizer IPC, i.e.
     19        grabbing position information from the UI process, and handling touch events synchronously. Luckily, these are
     20        also cases where (1) we know sync IPC may safely time out, and (2) the presentation of modal UI from the web
     21        process should cause the gesture recognizers to fail anyways. As such, we can mitigate these scenarios in the
     22        web process by responding to the these pending sync IPC messages *before* sending our own sync IPC to the UI
     23        process.
     24
     25        Test: fast/events/touch/ios/show-modal-alert-during-touch-start.html
     26
     27        * Shared/ios/InteractionInformationAtPosition.h:
     28        (WebKit::InteractionInformationAtPosition::invalidInformation):
     29        * Shared/ios/InteractionInformationAtPosition.mm:
     30        (WebKit::InteractionInformationAtPosition::encode const):
     31        (WebKit::InteractionInformationAtPosition::decode):
     32
     33        Add a new flag to indicate whether an interaction information response can be valid. Interaction information
     34        cannot be valid in the case where the interaction information request was interrupted by certain sync IPC
     35        messages from the web process.
     36
     37        * UIProcess/API/C/WKContextConfigurationRef.cpp:
     38        (WKContextConfigurationIgnoreSynchronousMessagingTimeoutsForTesting):
     39        (WKContextConfigurationSetIgnoreSynchronousMessagingTimeoutsForTesting):
     40
     41        Add some testing SPI to ignore sync IPC timeouts, for the purposes of testing. Rather than use the existing
     42        Objective-C SPI in WKWebProcessPoolConfiguration, I decided to add C API plumbing for this flag, so that other
     43        non-Cocoa ports may also support the new layout test option to ignore sync IPC timeouts.
     44
     45        * UIProcess/API/C/WKContextConfigurationRef.h:
     46        * UIProcess/ios/WKContentViewInteraction.mm:
     47        (-[WKContentView ensurePositionInformationIsUpToDate:]):
     48        (-[WKContentView _positionInformationDidChange:]):
     49        * WebProcess/WebCoreSupport/WebChromeClient.cpp:
     50        (WebKit::WebChromeClient::runBeforeUnloadConfirmPanel):
     51        (WebKit::WebChromeClient::runJavaScriptAlert):
     52        (WebKit::WebChromeClient::runJavaScriptConfirm):
     53        (WebKit::WebChromeClient::runJavaScriptPrompt):
     54        (WebKit::WebChromeClient::print):
     55        (WebKit::WebChromeClient::exceededDatabaseQuota):
     56        (WebKit::WebChromeClient::reachedApplicationCacheOriginQuota):
     57
     58        Cancel any pending sync IPC replies prior to sending sync IPC messages to the UI process which may result in
     59        sync IPC deadlock, by using the new helper method, sendSyncWithDelayedReply, instead of just sendSync.
     60
     61        * WebProcess/WebPage/WebPage.cpp:
     62        (WebKit::WebPage::cancelGesturesBlockedOnSynchronousReplies):
     63
     64        Add a helper to cancel pending sync messages coming in from the UI process that are being called from within
     65        gesture recognizer delegate hooks.
     66
     67        (WebKit::WebPage::touchEventSync):
     68        * WebProcess/WebPage/WebPage.h:
     69
     70        Add a new helper, sendSyncWithDelayedReply, to be used when sending a sync IPC message to the UI process that
     71        cannot be immediately completed upon arrival. Importantly, this cancels pending sync replies, and also passes
     72        IPC::SendSyncOption::InformPlatformProcessWillSuspend.
     73
     74        * WebProcess/WebPage/WebPage.messages.in:
     75
     76        Change these from LegacySync to Delayed messages.
     77
     78        * WebProcess/WebPage/ios/WebPageIOS.mm:
     79        (WebKit::WebPage::getPositionInformation):
     80
     81        Make this sync IPC handler (as well as WebPage::touchEventSync) store the IPC reply during the scope of the
     82        method, and invoke the stored reply at the end of the method if it wasn't interrupted due to something calling
     83        cancelGesturesBlockedOnSynchronousReplies().
     84
     85        (WebKit::WebPage::positionInformation):
     86
     87        Refactor getPositionInformation by pulling out the logic for building an InteractionInformationAtPosition into
     88        a separate helper.
     89
     90        (WebKit::WebPage::requestPositionInformation):
     91
    1922019-02-01  David Quesada  <david_quesada@apple.com>
    293
  • trunk/Source/WebKit/Shared/ios/InteractionInformationAtPosition.h

    r239454 r240882  
    4040
    4141struct InteractionInformationAtPosition {
     42    static InteractionInformationAtPosition invalidInformation()
     43    {
     44        InteractionInformationAtPosition response;
     45        response.canBeValid = false;
     46        return response;
     47    }
     48
    4249    InteractionInformationRequest request;
    4350
     51    bool canBeValid { true };
    4452    bool nodeAtPositionIsFocusedElement { false };
    4553#if ENABLE(DATA_INTERACTION)
  • trunk/Source/WebKit/Shared/ios/InteractionInformationAtPosition.mm

    r239454 r240882  
    4444    encoder << request;
    4545
     46    encoder << canBeValid;
    4647    encoder << nodeAtPositionIsFocusedElement;
    4748#if ENABLE(DATA_INTERACTION)
     
    9091        return false;
    9192
     93    if (!decoder.decode(result.canBeValid))
     94        return false;
     95
    9296    if (!decoder.decode(result.nodeAtPositionIsFocusedElement))
    9397        return false;
  • trunk/Source/WebKit/UIProcess/API/C/WKContextConfigurationRef.cpp

    r238820 r240882  
    139139}
    140140
     141bool WKContextConfigurationIgnoreSynchronousMessagingTimeoutsForTesting(WKContextConfigurationRef configuration)
     142{
     143    return toImpl(configuration)->ignoreSynchronousMessagingTimeoutsForTesting();
     144}
     145
     146void WKContextConfigurationSetIgnoreSynchronousMessagingTimeoutsForTesting(WKContextConfigurationRef configuration, bool ignore)
     147{
     148    toImpl(configuration)->setIgnoreSynchronousMessagingTimeoutsForTesting(ignore);
     149}
     150
    141151WKArrayRef WKContextConfigurationCopyOverrideLanguages(WKContextConfigurationRef configuration)
    142152{
  • trunk/Source/WebKit/UIProcess/API/C/WKContextConfigurationRef.h

    r238820 r240882  
    6464WK_EXPORT void WKContextConfigurationSetFullySynchronousModeIsAllowedForTesting(WKContextConfigurationRef configuration, bool allowed);
    6565
     66WK_EXPORT bool WKContextConfigurationIgnoreSynchronousMessagingTimeoutsForTesting(WKContextConfigurationRef configuration);
     67WK_EXPORT void WKContextConfigurationSetIgnoreSynchronousMessagingTimeoutsForTesting(WKContextConfigurationRef configuration, bool ignore);
     68
    6669WK_EXPORT WKArrayRef WKContextConfigurationCopyOverrideLanguages(WKContextConfigurationRef configuration);
    6770WK_EXPORT void WKContextConfigurationSetOverrideLanguages(WKContextConfigurationRef configuration, WKArrayRef overrideLanguages);
  • trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm

    r240875 r240882  
    17641764        return connection->waitForAndDispatchImmediately<Messages::WebPageProxy::DidReceivePositionInformation>(_page->pageID(), 1_s, IPC::WaitForOption::InterruptWaitingIfSyncMessageArrives);
    17651765
    1766     _hasValidPositionInformation = _page->process().sendSync(Messages::WebPage::GetPositionInformation(request), Messages::WebPage::GetPositionInformation::Reply(_positionInformation), _page->pageID(), 1_s);
     1766    bool receivedResponse = _page->process().sendSync(Messages::WebPage::GetPositionInformation(request), Messages::WebPage::GetPositionInformation::Reply(_positionInformation), _page->pageID(), 1_s);
     1767    _hasValidPositionInformation = receivedResponse && _positionInformation.canBeValid;
    17671768   
    17681769    // FIXME: We need to clean up these handlers in the event that we are not able to collect data, or if the WebProcess crashes.
     
    22852286
    22862287    _positionInformation = newInfo;
    2287     _hasValidPositionInformation = YES;
     2288    _hasValidPositionInformation = _positionInformation.canBeValid;
    22882289    if (_actionSheetAssistant)
    22892290        [_actionSheetAssistant updateSheetPosition];
  • trunk/Source/WebKit/WebProcess/WebCoreSupport/WebChromeClient.cpp

    r240199 r240882  
    406406    HangDetectionDisabler hangDetectionDisabler;
    407407
    408     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))
     408    if (!m_page.sendSyncWithDelayedReply(Messages::WebPageProxy::RunBeforeUnloadConfirmPanel(webFrame->frameID(), SecurityOriginData::fromFrame(&frame), message), Messages::WebPageProxy::RunBeforeUnloadConfirmPanel::Reply(shouldClose)))
    409409        return false;
    410410
     
    452452    HangDetectionDisabler hangDetectionDisabler;
    453453
    454     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);
     454    m_page.sendSyncWithDelayedReply(Messages::WebPageProxy::RunJavaScriptAlert(webFrame->frameID(), SecurityOriginData::fromFrame(&frame), alertText), Messages::WebPageProxy::RunJavaScriptAlert::Reply());
    455455}
    456456
     
    469469
    470470    bool result = false;
    471     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))
     471    if (!m_page.sendSyncWithDelayedReply(Messages::WebPageProxy::RunJavaScriptConfirm(webFrame->frameID(), SecurityOriginData::fromFrame(&frame), message), Messages::WebPageProxy::RunJavaScriptConfirm::Reply(result)))
    472472        return false;
    473473
     
    488488    HangDetectionDisabler hangDetectionDisabler;
    489489
    490     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))
     490    if (!m_page.sendSyncWithDelayedReply(Messages::WebPageProxy::RunJavaScriptPrompt(webFrame->frameID(), SecurityOriginData::fromFrame(&frame), message, defaultValue), Messages::WebPageProxy::RunJavaScriptPrompt::Reply(result)))
    491491        return false;
    492492
     
    715715#endif
    716716
    717     m_page.sendSync(Messages::WebPageProxy::PrintFrame(webFrame->frameID()), Messages::WebPageProxy::PrintFrame::Reply(), Seconds::infinity(), IPC::SendSyncOption::InformPlatformProcessWillSuspend);
     717    m_page.sendSyncWithDelayedReply(Messages::WebPageProxy::PrintFrame(webFrame->frameID()), Messages::WebPageProxy::PrintFrame::Reply());
    718718}
    719719
     
    732732    newQuota = m_page.injectedBundleUIClient().didExceedDatabaseQuota(&m_page, securityOrigin.ptr(), databaseName, details.displayName(), currentQuota, currentOriginUsage, details.currentUsage(), details.expectedUsage());
    733733
    734     if (!newQuota) {
    735         WebProcess::singleton().parentProcessConnection()->sendSync(
    736             Messages::WebPageProxy::ExceededDatabaseQuota(webFrame->frameID(), originData.databaseIdentifier(), databaseName, details.displayName(), currentQuota, currentOriginUsage, details.currentUsage(), details.expectedUsage()),
    737             Messages::WebPageProxy::ExceededDatabaseQuota::Reply(newQuota), m_page.pageID(), Seconds::infinity(), IPC::SendSyncOption::InformPlatformProcessWillSuspend);
    738     }
     734    if (!newQuota)
     735        m_page.sendSyncWithDelayedReply(Messages::WebPageProxy::ExceededDatabaseQuota(webFrame->frameID(), originData.databaseIdentifier(), databaseName, details.displayName(), currentQuota, currentOriginUsage, details.currentUsage(), details.expectedUsage()), Messages::WebPageProxy::ExceededDatabaseQuota::Reply(newQuota));
    739736
    740737    tracker.setQuota(originData, newQuota);
     
    758755
    759756    uint64_t newQuota = 0;
    760     WebProcess::singleton().parentProcessConnection()->sendSync(
    761         Messages::WebPageProxy::ReachedApplicationCacheOriginQuota(origin.data().databaseIdentifier(), currentQuota, totalBytesNeeded),
    762         Messages::WebPageProxy::ReachedApplicationCacheOriginQuota::Reply(newQuota), m_page.pageID(), Seconds::infinity(), IPC::SendSyncOption::InformPlatformProcessWillSuspend);
     757    m_page.sendSyncWithDelayedReply(Messages::WebPageProxy::ReachedApplicationCacheOriginQuota(origin.data().databaseIdentifier(), currentQuota, totalBytesNeeded), Messages::WebPageProxy::ReachedApplicationCacheOriginQuota::Reply(newQuota));
    763758
    764759    cacheStorage.storeUpdatedQuotaForOrigin(&origin, newQuota);
  • trunk/Source/WebKit/WebProcess/WebPage/WebPage.cpp

    r240875 r240882  
    263263
    264264#if PLATFORM(IOS_FAMILY)
     265#include "InteractionInformationAtPosition.h"
     266#include "InteractionInformationRequest.h"
    265267#include "RemoteLayerTreeDrawingArea.h"
    266268#include <CoreGraphics/CoreGraphics.h>
     
    27262728}
    27272729
     2730void WebPage::cancelGesturesBlockedOnSynchronousReplies()
     2731{
     2732#if ENABLE(IOS_TOUCH_EVENTS)
     2733    if (auto reply = WTFMove(m_pendingSynchronousTouchEventReply))
     2734        reply(true);
     2735#endif
     2736
     2737#if PLATFORM(IOS_FAMILY)
     2738    if (auto reply = WTFMove(m_pendingSynchronousPositionInformationReply))
     2739        reply(InteractionInformationAtPosition::invalidInformation());
     2740#endif
     2741}
     2742
    27282743#if ENABLE(TOUCH_EVENTS)
    27292744static bool handleTouchEvent(const WebTouchEvent& touchEvent, Page* page)
     
    27472762}
    27482763
    2749 void WebPage::touchEventSync(const WebTouchEvent& touchEvent, bool& handled)
    2750 {
     2764void WebPage::touchEventSync(const WebTouchEvent& touchEvent, CompletionHandler<void(bool)>&& reply)
     2765{
     2766    m_pendingSynchronousTouchEventReply = WTFMove(reply);
     2767
    27512768    EventDispatcher::TouchEventQueue queuedEvents;
    27522769    WebProcess::singleton().eventDispatcher().getQueuedTouchEventsForPage(*this, queuedEvents);
    27532770    dispatchAsynchronousTouchEvents(queuedEvents);
    27542771
     2772    bool handled = true;
    27552773    dispatchTouchEvent(touchEvent, handled);
     2774
     2775    if (auto reply = WTFMove(m_pendingSynchronousTouchEventReply))
     2776        reply(handled);
    27562777}
    27572778
  • trunk/Source/WebKit/WebProcess/WebPage/WebPage.h

    r240875 r240882  
    648648    void requestAutocorrectionContext(CallbackID);
    649649    void getAutocorrectionContext(String& beforeText, String& markedText, String& selectedText, String& afterText, uint64_t& location, uint64_t& length);
    650     void getPositionInformation(const InteractionInformationRequest&, InteractionInformationAtPosition&);
     650    void getPositionInformation(const InteractionInformationRequest&, CompletionHandler<void(InteractionInformationAtPosition&&)>&&);
    651651    void requestPositionInformation(const InteractionInformationRequest&);
    652652    void startInteractionWithElementAtPosition(const WebCore::IntPoint&);
     
    11381138    void didReceiveWebPageMessage(IPC::Connection&, IPC::Decoder&);
    11391139
     1140    template<typename T>
     1141    bool sendSyncWithDelayedReply(T&& message, typename T::Reply&& reply)
     1142    {
     1143        cancelGesturesBlockedOnSynchronousReplies();
     1144        return sendSync(WTFMove(message), WTFMove(reply), m_pageID, Seconds::infinity(), IPC::SendSyncOption::InformPlatformProcessWillSuspend);
     1145    }
     1146
    11401147private:
    11411148    WebPage(uint64_t pageID, WebPageCreationParameters&&);
     
    11791186    WebCore::VisiblePosition visiblePositionInFocusedNodeForPoint(const WebCore::Frame&, const WebCore::IntPoint&, bool isInteractingWithFocusedElement);
    11801187    RefPtr<WebCore::Range> rangeForGranularityAtPoint(WebCore::Frame&, const WebCore::IntPoint&, uint32_t granularity, bool isInteractingWithFocusedElement);
     1188
     1189    void sendPositionInformation(InteractionInformationAtPosition&&);
     1190    InteractionInformationAtPosition positionInformation(const InteractionInformationRequest&);
    11811191#endif
    11821192
     
    12351245
    12361246#if ENABLE(IOS_TOUCH_EVENTS)
    1237     void touchEventSync(const WebTouchEvent&, bool& handled);
     1247    void touchEventSync(const WebTouchEvent&, CompletionHandler<void(bool)>&&);
    12381248    void updatePotentialTapSecurityOrigin(const WebTouchEvent&, bool wasHandled);
    12391249#elif ENABLE(TOUCH_EVENTS)
     
    14941504    bool canShowMIMEType(const String&, const Function<bool(const String&, WebCore::PluginData::AllowedPluginTypes)>& supportsPlugin) const;
    14951505
     1506    void cancelGesturesBlockedOnSynchronousReplies();
     1507
    14961508    uint64_t m_pageID;
    14971509
     
    17001712    bool m_hasPendingBlurNotification { false };
    17011713    bool m_hasPendingEditorStateUpdate { false };
     1714
     1715#if ENABLE(IOS_TOUCH_EVENTS)
     1716    CompletionHandler<void(bool)> m_pendingSynchronousTouchEventReply;
     1717#endif
    17021718   
    17031719#if PLATFORM(IOS_FAMILY)
     
    17451761    double m_lastTransactionPageScaleFactor { 0 };
    17461762    uint64_t m_lastTransactionIDWithScaleChange { 0 };
     1763
     1764    CompletionHandler<void(InteractionInformationAtPosition&&)> m_pendingSynchronousPositionInformationReply;
    17471765#endif
    17481766
  • trunk/Source/WebKit/WebProcess/WebPage/WebPage.messages.in

    r240875 r240882  
    8181    RequestAutocorrectionContext(WebKit::CallbackID callbackID)
    8282    GetAutocorrectionContext() -> (String beforeContext, String markedText, String selectedText, String afterContext, uint64_t location, uint64_t length) LegacySync
    83     GetPositionInformation(struct WebKit::InteractionInformationRequest request) -> (struct WebKit::InteractionInformationAtPosition information) LegacySync
     83    GetPositionInformation(struct WebKit::InteractionInformationRequest request) -> (struct WebKit::InteractionInformationAtPosition information) Delayed
    8484    RequestPositionInformation(struct WebKit::InteractionInformationRequest request)
    8585    StartInteractionWithElementAtPosition(WebCore::IntPoint point)
     
    122122
    123123#if ENABLE(IOS_TOUCH_EVENTS)
    124     TouchEventSync(WebKit::WebTouchEvent event) -> (bool handled) LegacySync
     124    TouchEventSync(WebKit::WebTouchEvent event) -> (bool handled) Delayed
    125125#endif
    126126#if !ENABLE(IOS_TOUCH_EVENTS) && ENABLE(TOUCH_EVENTS)
  • trunk/Source/WebKit/WebProcess/WebPage/ios/WebPageIOS.mm

    r240702 r240882  
    20862086}
    20872087
    2088 void WebPage::getPositionInformation(const InteractionInformationRequest& request, InteractionInformationAtPosition& info)
    2089 {
     2088void WebPage::getPositionInformation(const InteractionInformationRequest& request, CompletionHandler<void(InteractionInformationAtPosition&&)>&& reply)
     2089{
     2090    m_pendingSynchronousPositionInformationReply = WTFMove(reply);
     2091
     2092    auto information = positionInformation(request);
     2093
     2094    if (auto reply = WTFMove(m_pendingSynchronousPositionInformationReply))
     2095        reply(WTFMove(information));
     2096}
     2097
     2098InteractionInformationAtPosition WebPage::positionInformation(const InteractionInformationRequest& request)
     2099{
     2100    InteractionInformationAtPosition info;
    20902101    info.request = request;
    20912102
     
    22632274#endif
    22642275    info.adjustedPointForNodeRespondingToClickEvents = adjustedPoint;
     2276
     2277    return info;
    22652278}
    22662279
    22672280void WebPage::requestPositionInformation(const InteractionInformationRequest& request)
    22682281{
    2269     InteractionInformationAtPosition info;
    2270 
    2271     getPositionInformation(request, info);
    2272     send(Messages::WebPageProxy::DidReceivePositionInformation(info));
     2282    send(Messages::WebPageProxy::DidReceivePositionInformation(positionInformation(request)));
    22732283}
    22742284
  • trunk/Tools/ChangeLog

    r240880 r240882  
     12019-02-01  Wenson Hsieh  <wenson_hsieh@apple.com>
     2
     3        [iOS] Consistent 1 sec hang when triggering modal alerts while handling synchronous touch events
     4        https://bugs.webkit.org/show_bug.cgi?id=194140
     5        <rdar://problem/47728098>
     6
     7        Reviewed by Tim Horton.
     8
     9        * WebKitTestRunner/InjectedBundle/Bindings/TestRunner.idl:
     10        * WebKitTestRunner/InjectedBundle/TestRunner.cpp:
     11        (WTR::TestRunner::setShouldDismissJavaScriptAlertsAsynchronously):
     12
     13        Add a new TestRunner hook to make modal JavaScript alerts dismiss asynchronously. This is used by the new layout
     14        test to induce an IPC deadlock when presenting a modal alert during touch start.
     15
     16        * WebKitTestRunner/InjectedBundle/TestRunner.h:
     17        * WebKitTestRunner/TestController.cpp:
     18        (WTR::runJavaScriptAlert):
     19
     20        Add a client callback function for running JavaScript alerts.
     21
     22        (WTR::TestController::createOtherPage):
     23        (WTR::TestController::generateContextConfiguration const):
     24
     25        Add a test option to disable IPC timeouts for a layout test. This forces the test to reliably time out without
     26        the fix in this patch.
     27
     28        (WTR::TestController::createWebViewWithOptions):
     29
     30        Plumb TestOptions to generateContextConfiguration.
     31
     32        (WTR::TestController::resetPreferencesToConsistentValues):
     33        (WTR::TestController::resetStateToConsistentValues):
     34        (WTR::updateTestOptionsFromTestHeader):
     35        (WTR::TestController::setShouldDismissJavaScriptAlertsAsynchronously):
     36        (WTR::TestController::handleJavaScriptAlert):
     37        * WebKitTestRunner/TestController.h:
     38        * WebKitTestRunner/TestInvocation.cpp:
     39        (WTR::TestInvocation::didReceiveSynchronousMessageFromInjectedBundle):
     40        * WebKitTestRunner/TestOptions.h:
     41        (WTR::TestOptions::hasSameInitializationOptions const):
     42
    1432019-02-01  Chris Dumez  <cdumez@apple.com>
    244
  • trunk/Tools/WebKitTestRunner/InjectedBundle/Bindings/TestRunner.idl

    r240689 r240882  
    330330    void setOpenPanelFiles(object filesArray);
    331331
     332    // Modal alerts
     333    void setShouldDismissJavaScriptAlertsAsynchronously(boolean value);
     334
    332335    void setWebRTCMDNSICECandidatesEnabled(boolean value);
    333336    void setWebRTCUnifiedPlanEnabled(boolean value);
  • trunk/Tools/WebKitTestRunner/InjectedBundle/TestRunner.cpp

    r240689 r240882  
    27162716}
    27172717
     2718void TestRunner::setShouldDismissJavaScriptAlertsAsynchronously(bool shouldDismissAsynchronously)
     2719{
     2720    WKRetainPtr<WKStringRef> messageName(AdoptWK, WKStringCreateWithUTF8CString("ShouldDismissJavaScriptAlertsAsynchronously"));
     2721    WKRetainPtr<WKBooleanRef> messageBody(AdoptWK, WKBooleanCreate(shouldDismissAsynchronously));
     2722    WKBundlePostSynchronousMessage(InjectedBundle::singleton().bundle(), messageName.get(), messageBody.get(), nullptr);
     2723}
     2724
    27182725} // namespace WTR
  • trunk/Tools/WebKitTestRunner/InjectedBundle/TestRunner.h

    r240689 r240882  
    449449    void setOpenPanelFiles(JSValueRef);
    450450
     451    // Modal alerts
     452    void setShouldDismissJavaScriptAlertsAsynchronously(bool);
     453
    451454    void terminateNetworkProcess();
    452455    void terminateServiceWorkerProcess();
  • trunk/Tools/WebKitTestRunner/TestController.cpp

    r240775 r240882  
    238238{
    239239    TestController::singleton().handleUserMediaPermissionRequest(frame, userMediaDocumentOrigin, topLevelDocumentOrigin, permissionRequest);
     240}
     241
     242static void runJavaScriptAlert(WKPageRef page, WKStringRef alertText, WKFrameRef frame, WKSecurityOriginRef securityOrigin, WKPageRunJavaScriptAlertResultListenerRef listener, const void *clientInfo)
     243{
     244    TestController::singleton().handleJavaScriptAlert(listener);
    240245}
    241246
     
    327332        0, // mediaSessionMetadataDidChange
    328333        createOtherPage,
    329         0, // runJavaScriptAlert
     334        runJavaScriptAlert,
    330335        0, // runJavaScriptConfirm
    331336        0, // runJavaScriptPrompt
     
    443448}
    444449
    445 WKRetainPtr<WKContextConfigurationRef> TestController::generateContextConfiguration() const
     450WKRetainPtr<WKContextConfigurationRef> TestController::generateContextConfiguration(const TestOptions& options) const
    446451{
    447452    auto configuration = adoptWK(WKContextConfigurationCreate());
    448453    WKContextConfigurationSetInjectedBundlePath(configuration.get(), injectedBundlePath());
    449454    WKContextConfigurationSetFullySynchronousModeIsAllowedForTesting(configuration.get(), true);
     455    WKContextConfigurationSetIgnoreSynchronousMessagingTimeoutsForTesting(configuration.get(), options.ignoreSynchronousMessagingTimeoutsForTesting);
    450456
    451457    if (const char* dumpRenderTreeTemp = libraryPathForTesting()) {
     
    535541void TestController::createWebViewWithOptions(const TestOptions& options)
    536542{
    537     auto contextConfiguration = generateContextConfiguration();
     543    auto contextConfiguration = generateContextConfiguration(options);
    538544
    539545    WKRetainPtr<WKMutableArrayRef> overrideLanguages = adoptWK(WKMutableArrayCreate());
     
    615621        0, // mediaSessionMetadataDidChange
    616622        createOtherPage,
    617         0, // runJavaScriptAlert
     623        runJavaScriptAlert,
    618624        0, // runJavaScriptConfirm
    619625        0, // runJavaScriptPrompt
     
    825831    WKPreferencesSetWebSQLDisabled(preferences, false);
    826832
    827     m_serverTrustEvaluationCallbackCallsCount = 0;
    828 
    829833    platformResetPreferencesToConsistentValues();
    830834}
     
    958962
    959963    m_didReceiveServerRedirectForProvisionalNavigation = false;
     964    m_serverTrustEvaluationCallbackCallsCount = 0;
     965    m_shouldDismissJavaScriptAlertsAsynchronously = false;
    960966
    961967    // Reset main page back to about:blank
     
    12791285        else if (key == "contentInset.top")
    12801286            testOptions.contentInsetTop = std::stod(value);
     1287        else if (key == "ignoreSynchronousMessagingTimeoutsForTesting")
     1288            testOptions.ignoreSynchronousMessagingTimeoutsForTesting = parseBooleanTestHeaderValue(value);
    12811289        pairStart = pairEnd + 1;
    12821290    }
     
    23312339}
    23322340
     2341void TestController::setShouldDismissJavaScriptAlertsAsynchronously(bool value)
     2342{
     2343    m_shouldDismissJavaScriptAlertsAsynchronously = value;
     2344}
     2345
     2346void TestController::handleJavaScriptAlert(WKPageRunJavaScriptAlertResultListenerRef listener)
     2347{
     2348    if (!m_shouldDismissJavaScriptAlertsAsynchronously) {
     2349        WKPageRunJavaScriptAlertResultListenerCall(listener);
     2350        return;
     2351    }
     2352
     2353    WKRetain(listener);
     2354    callOnMainThread([listener] {
     2355        WKPageRunJavaScriptAlertResultListenerCall(listener);
     2356        WKRelease(listener);
     2357    });
     2358}
     2359
    23332360class OriginSettings : public RefCounted<OriginSettings> {
    23342361public:
  • trunk/Tools/WebKitTestRunner/TestController.h

    r240689 r240882  
    285285    uint64_t serverTrustEvaluationCallbackCallsCount() const { return m_serverTrustEvaluationCallbackCallsCount; }
    286286
     287    void setShouldDismissJavaScriptAlertsAsynchronously(bool);
     288    void handleJavaScriptAlert(WKPageRunJavaScriptAlertResultListenerRef);
     289
    287290private:
    288291    WKRetainPtr<WKPageConfigurationRef> generatePageConfiguration(WKContextConfigurationRef);
    289     WKRetainPtr<WKContextConfigurationRef> generateContextConfiguration() const;
     292    WKRetainPtr<WKContextConfigurationRef> generateContextConfiguration(const TestOptions&) const;
    290293    void initialize(int argc, const char* argv[]);
    291294    void createWebViewWithOptions(const TestOptions&);
     
    540543
    541544    uint64_t m_serverTrustEvaluationCallbackCallsCount { 0 };
     545    bool m_shouldDismissJavaScriptAlertsAsynchronously { false };
    542546};
    543547
  • trunk/Tools/WebKitTestRunner/TestInvocation.cpp

    r240689 r240882  
    15401540    }
    15411541
     1542    if (WKStringIsEqualToUTF8CString(messageName, "ShouldDismissJavaScriptAlertsAsynchronously")) {
     1543        ASSERT(WKGetTypeID(messageBody) == WKBooleanGetTypeID());
     1544        WKBooleanRef value = static_cast<WKBooleanRef>(messageBody);
     1545        TestController::singleton().setShouldDismissJavaScriptAlertsAsynchronously(WKBooleanGetValue(value));
     1546        return nullptr;
     1547    }
     1548
    15421549    ASSERT_NOT_REACHED();
    15431550    return nullptr;
  • trunk/Tools/WebKitTestRunner/TestOptions.h

    r240139 r240882  
    6969    bool editable { false };
    7070    bool enableUndoManagerAPI { false };
     71    bool ignoreSynchronousMessagingTimeoutsForTesting { false };
    7172
    7273    double contentInsetTop { 0 };
     
    117118            || editable != options.editable
    118119            || enableUndoManagerAPI != options.enableUndoManagerAPI
    119             || contentInsetTop != options.contentInsetTop)
     120            || contentInsetTop != options.contentInsetTop
     121            || ignoreSynchronousMessagingTimeoutsForTesting != options.ignoreSynchronousMessagingTimeoutsForTesting)
    120122            return false;
    121123
Note: See TracChangeset for help on using the changeset viewer.