Diff
Modified: trunk/LayoutTests/ChangeLog (240881 => 240882)
--- trunk/LayoutTests/ChangeLog 2019-02-01 23:08:48 UTC (rev 240881)
+++ trunk/LayoutTests/ChangeLog 2019-02-01 23:12:14 UTC (rev 240882)
@@ -1,3 +1,17 @@
+2019-02-01 Wenson Hsieh <[email protected]>
+
+ [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 <[email protected]>
Dispatch pointercancel events when content is panned or zoomed on iOS
Added: trunk/LayoutTests/fast/events/touch/ios/show-modal-alert-during-touch-start-expected.txt (0 => 240882)
--- trunk/LayoutTests/fast/events/touch/ios/show-modal-alert-during-touch-start-expected.txt (rev 0)
+++ trunk/LayoutTests/fast/events/touch/ios/show-modal-alert-during-touch-start-expected.txt 2019-02-01 23:12:14 UTC (rev 240882)
@@ -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.
Added: trunk/LayoutTests/fast/events/touch/ios/show-modal-alert-during-touch-start.html (0 => 240882)
--- trunk/LayoutTests/fast/events/touch/ios/show-modal-alert-during-touch-start.html (rev 0)
+++ trunk/LayoutTests/fast/events/touch/ios/show-modal-alert-during-touch-start.html 2019-02-01 23:12:14 UTC (rev 240882)
@@ -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=""
+<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>
Modified: trunk/Source/WebKit/ChangeLog (240881 => 240882)
--- trunk/Source/WebKit/ChangeLog 2019-02-01 23:08:48 UTC (rev 240881)
+++ trunk/Source/WebKit/ChangeLog 2019-02-01 23:12:14 UTC (rev 240882)
@@ -1,3 +1,94 @@
+2019-02-01 Wenson Hsieh <[email protected]>
+
+ [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 <[email protected]>
Network Process crash when resuming downloads: '-[__NSDictionaryI setObject:forKey:]: unrecognized selector sent to instance %p'
Modified: trunk/Source/WebKit/Shared/ios/InteractionInformationAtPosition.h (240881 => 240882)
--- trunk/Source/WebKit/Shared/ios/InteractionInformationAtPosition.h 2019-02-01 23:08:48 UTC (rev 240881)
+++ trunk/Source/WebKit/Shared/ios/InteractionInformationAtPosition.h 2019-02-01 23:12:14 UTC (rev 240882)
@@ -39,8 +39,16 @@
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 };
Modified: trunk/Source/WebKit/Shared/ios/InteractionInformationAtPosition.mm (240881 => 240882)
--- trunk/Source/WebKit/Shared/ios/InteractionInformationAtPosition.mm 2019-02-01 23:08:48 UTC (rev 240881)
+++ trunk/Source/WebKit/Shared/ios/InteractionInformationAtPosition.mm 2019-02-01 23:12:14 UTC (rev 240882)
@@ -43,6 +43,7 @@
{
encoder << request;
+ encoder << canBeValid;
encoder << nodeAtPositionIsFocusedElement;
#if ENABLE(DATA_INTERACTION)
encoder << hasSelectionAtPosition;
@@ -89,6 +90,9 @@
if (!decoder.decode(result.request))
return false;
+ if (!decoder.decode(result.canBeValid))
+ return false;
+
if (!decoder.decode(result.nodeAtPositionIsFocusedElement))
return false;
Modified: trunk/Source/WebKit/UIProcess/API/C/WKContextConfigurationRef.cpp (240881 => 240882)
--- trunk/Source/WebKit/UIProcess/API/C/WKContextConfigurationRef.cpp 2019-02-01 23:08:48 UTC (rev 240881)
+++ trunk/Source/WebKit/UIProcess/API/C/WKContextConfigurationRef.cpp 2019-02-01 23:12:14 UTC (rev 240882)
@@ -138,6 +138,16 @@
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());
Modified: trunk/Source/WebKit/UIProcess/API/C/WKContextConfigurationRef.h (240881 => 240882)
--- trunk/Source/WebKit/UIProcess/API/C/WKContextConfigurationRef.h 2019-02-01 23:08:48 UTC (rev 240881)
+++ trunk/Source/WebKit/UIProcess/API/C/WKContextConfigurationRef.h 2019-02-01 23:12:14 UTC (rev 240882)
@@ -63,6 +63,9 @@
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);
Modified: trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm (240881 => 240882)
--- trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm 2019-02-01 23:08:48 UTC (rev 240881)
+++ trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm 2019-02-01 23:12:14 UTC (rev 240882)
@@ -1763,7 +1763,8 @@
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 @@
newInfo.mergeCompatibleOptionalInformation(_positionInformation);
_positionInformation = newInfo;
- _hasValidPositionInformation = YES;
+ _hasValidPositionInformation = _positionInformation.canBeValid;
if (_actionSheetAssistant)
[_actionSheetAssistant updateSheetPosition];
[self _invokeAndRemovePendingHandlersValidForCurrentPositionInformation];
Modified: trunk/Source/WebKit/WebProcess/WebCoreSupport/WebChromeClient.cpp (240881 => 240882)
--- trunk/Source/WebKit/WebProcess/WebCoreSupport/WebChromeClient.cpp 2019-02-01 23:08:48 UTC (rev 240881)
+++ trunk/Source/WebKit/WebProcess/WebCoreSupport/WebChromeClient.cpp 2019-02-01 23:12:14 UTC (rev 240882)
@@ -405,7 +405,7 @@
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 @@
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 @@
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 @@
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 @@
}
#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 @@
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 @@
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);
}
Modified: trunk/Source/WebKit/WebProcess/WebPage/WebPage.cpp (240881 => 240882)
--- trunk/Source/WebKit/WebProcess/WebPage/WebPage.cpp 2019-02-01 23:08:48 UTC (rev 240881)
+++ trunk/Source/WebKit/WebProcess/WebPage/WebPage.cpp 2019-02-01 23:12:14 UTC (rev 240882)
@@ -262,6 +262,8 @@
#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 @@
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 @@
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)
Modified: trunk/Source/WebKit/WebProcess/WebPage/WebPage.h (240881 => 240882)
--- trunk/Source/WebKit/WebProcess/WebPage/WebPage.h 2019-02-01 23:08:48 UTC (rev 240881)
+++ trunk/Source/WebKit/WebProcess/WebPage/WebPage.h 2019-02-01 23:12:14 UTC (rev 240882)
@@ -647,7 +647,7 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
Optional<DynamicViewportSizeUpdateID> m_pendingDynamicViewportSizeUpdateID;
double m_lastTransactionPageScaleFactor { 0 };
uint64_t m_lastTransactionIDWithScaleChange { 0 };
+
+ CompletionHandler<void(InteractionInformationAtPosition&&)> m_pendingSynchronousPositionInformationReply;
#endif
WebCore::Timer m_layerVolatilityTimer;
Modified: trunk/Source/WebKit/WebProcess/WebPage/WebPage.messages.in (240881 => 240882)
--- trunk/Source/WebKit/WebProcess/WebPage/WebPage.messages.in 2019-02-01 23:08:48 UTC (rev 240881)
+++ trunk/Source/WebKit/WebProcess/WebPage/WebPage.messages.in 2019-02-01 23:12:14 UTC (rev 240882)
@@ -80,7 +80,7 @@
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 @@
#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)
Modified: trunk/Source/WebKit/WebProcess/WebPage/ios/WebPageIOS.mm (240881 => 240882)
--- trunk/Source/WebKit/WebProcess/WebPage/ios/WebPageIOS.mm 2019-02-01 23:08:48 UTC (rev 240881)
+++ trunk/Source/WebKit/WebProcess/WebPage/ios/WebPageIOS.mm 2019-02-01 23:12:14 UTC (rev 240882)
@@ -2085,8 +2085,19 @@
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 @@
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)
Modified: trunk/Tools/ChangeLog (240881 => 240882)
--- trunk/Tools/ChangeLog 2019-02-01 23:08:48 UTC (rev 240881)
+++ trunk/Tools/ChangeLog 2019-02-01 23:12:14 UTC (rev 240882)
@@ -1,3 +1,45 @@
+2019-02-01 Wenson Hsieh <[email protected]>
+
+ [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 <[email protected]>
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
Modified: trunk/Tools/WebKitTestRunner/InjectedBundle/Bindings/TestRunner.idl (240881 => 240882)
--- trunk/Tools/WebKitTestRunner/InjectedBundle/Bindings/TestRunner.idl 2019-02-01 23:08:48 UTC (rev 240881)
+++ trunk/Tools/WebKitTestRunner/InjectedBundle/Bindings/TestRunner.idl 2019-02-01 23:12:14 UTC (rev 240882)
@@ -329,6 +329,9 @@
// Open panel
void setOpenPanelFiles(object filesArray);
+ // Modal alerts
+ void setShouldDismissJavaScriptAlertsAsynchronously(boolean value);
+
void setWebRTCMDNSICECandidatesEnabled(boolean value);
void setWebRTCUnifiedPlanEnabled(boolean value);
void setCustomUserAgent(DOMString userAgent);
Modified: trunk/Tools/WebKitTestRunner/InjectedBundle/TestRunner.cpp (240881 => 240882)
--- trunk/Tools/WebKitTestRunner/InjectedBundle/TestRunner.cpp 2019-02-01 23:08:48 UTC (rev 240881)
+++ trunk/Tools/WebKitTestRunner/InjectedBundle/TestRunner.cpp 2019-02-01 23:12:14 UTC (rev 240882)
@@ -2715,4 +2715,11 @@
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
Modified: trunk/Tools/WebKitTestRunner/InjectedBundle/TestRunner.h (240881 => 240882)
--- trunk/Tools/WebKitTestRunner/InjectedBundle/TestRunner.h 2019-02-01 23:08:48 UTC (rev 240881)
+++ trunk/Tools/WebKitTestRunner/InjectedBundle/TestRunner.h 2019-02-01 23:12:14 UTC (rev 240882)
@@ -448,6 +448,9 @@
// Open panel
void setOpenPanelFiles(JSValueRef);
+ // Modal alerts
+ void setShouldDismissJavaScriptAlertsAsynchronously(bool);
+
void terminateNetworkProcess();
void terminateServiceWorkerProcess();
Modified: trunk/Tools/WebKitTestRunner/TestController.cpp (240881 => 240882)
--- trunk/Tools/WebKitTestRunner/TestController.cpp 2019-02-01 23:08:48 UTC (rev 240881)
+++ trunk/Tools/WebKitTestRunner/TestController.cpp 2019-02-01 23:12:14 UTC (rev 240882)
@@ -239,6 +239,11 @@
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 @@
0, // runJavaScriptPrompt
0, // mediaSessionMetadataDidChange
createOtherPage,
- 0, // runJavaScriptAlert
+ runJavaScriptAlert,
0, // runJavaScriptConfirm
0, // runJavaScriptPrompt
checkUserMediaPermissionForOrigin,
@@ -442,11 +447,12 @@
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 @@
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 @@
0, // runJavaScriptPrompt
0, // mediaSessionMetadataDidChange
createOtherPage,
- 0, // runJavaScriptAlert
+ runJavaScriptAlert,
0, // runJavaScriptConfirm
0, // runJavaScriptPrompt
checkUserMediaPermissionForOrigin,
@@ -824,8 +830,6 @@
WKPreferencesSetWebSQLDisabled(preferences, false);
- m_serverTrustEvaluationCallbackCallsCount = 0;
-
platformResetPreferencesToConsistentValues();
}
@@ -957,6 +961,8 @@
statisticsResetToConsistentState();
m_didReceiveServerRedirectForProvisionalNavigation = false;
+ m_serverTrustEvaluationCallbackCallsCount = 0;
+ m_shouldDismissJavaScriptAlertsAsynchronously = false;
// Reset main page back to about:blank
m_doneResetting = false;
@@ -1278,6 +1284,8 @@
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 @@
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()
Modified: trunk/Tools/WebKitTestRunner/TestController.h (240881 => 240882)
--- trunk/Tools/WebKitTestRunner/TestController.h 2019-02-01 23:08:48 UTC (rev 240881)
+++ trunk/Tools/WebKitTestRunner/TestController.h 2019-02-01 23:12:14 UTC (rev 240882)
@@ -284,9 +284,12 @@
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 @@
HashMap<uint64_t, AbandonedDocumentInfo> m_abandonedDocumentInfo;
uint64_t m_serverTrustEvaluationCallbackCallsCount { 0 };
+ bool m_shouldDismissJavaScriptAlertsAsynchronously { false };
};
struct TestCommand {
Modified: trunk/Tools/WebKitTestRunner/TestInvocation.cpp (240881 => 240882)
--- trunk/Tools/WebKitTestRunner/TestInvocation.cpp 2019-02-01 23:08:48 UTC (rev 240881)
+++ trunk/Tools/WebKitTestRunner/TestInvocation.cpp 2019-02-01 23:12:14 UTC (rev 240882)
@@ -1539,6 +1539,13 @@
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;
}
Modified: trunk/Tools/WebKitTestRunner/TestOptions.h (240881 => 240882)
--- trunk/Tools/WebKitTestRunner/TestOptions.h 2019-02-01 23:08:48 UTC (rev 240881)
+++ trunk/Tools/WebKitTestRunner/TestOptions.h 2019-02-01 23:12:14 UTC (rev 240882)
@@ -68,6 +68,7 @@
bool enableEditableImages { false };
bool editable { false };
bool enableUndoManagerAPI { false };
+ bool ignoreSynchronousMessagingTimeoutsForTesting { false };
double contentInsetTop { 0 };
@@ -116,7 +117,8 @@
|| enableEditableImages != options.enableEditableImages
|| editable != options.editable
|| enableUndoManagerAPI != options.enableUndoManagerAPI
- || contentInsetTop != options.contentInsetTop)
+ || contentInsetTop != options.contentInsetTop
+ || ignoreSynchronousMessagingTimeoutsForTesting != options.ignoreSynchronousMessagingTimeoutsForTesting)
return false;
if (experimentalFeatures != options.experimentalFeatures)