Diff
Modified: trunk/Source/WebKit/ChangeLog (278253 => 278254)
--- trunk/Source/WebKit/ChangeLog 2021-05-30 16:11:40 UTC (rev 278253)
+++ trunk/Source/WebKit/ChangeLog 2021-05-30 19:18:56 UTC (rev 278254)
@@ -1,3 +1,31 @@
+2021-05-30 Wenson Hsieh <[email protected]>
+
+ [iOS] UI process crashes when deallocating WKWebView in a script message handler during an active touch event
+ https://bugs.webkit.org/show_bug.cgi?id=226426
+ rdar://75425319
+
+ Reviewed by Darin Adler.
+
+ It's possible for the UI process to crash upon being notified that asynchronous, active touch events have been
+ handled in the web process via the async IPC replay block in `WebPageProxy::handlePreventableTouchEvent()`. This
+ happens if the client posts a message from the web process to UI process while handling an active touch event
+ and deallocates the WKWebView currently handling the touch event in the script message handler.
+
+ This is because the async replay block inside `WebPageProxy::handlePreventableTouchEvent()` strongly captures a
+ reference to the `WebPageProxy`, thus keeping it alive; however, the `WebPageProxy`'s weak pointer to the page
+ client is nulled out, which causes `WebPageProxy::pageClient()` to crash with a null dereference.
+
+ To fix this, we weakly capture `WebPageProxy` instead and return early if it has already been destroyed by the
+ time we process the completion handler, and also add a null check for `m_pageClient` before attempting to call
+ into it. Note that it's unnecessary to call into `doneDeferringTouch(Start|End)` to unblock any deferred
+ gestures here, because the process of destroying the content view will call `-cleanUpInteraction` and remove all
+ deferring gestures from the view, regardless of whether they're still in Possible state.
+
+ Test: TouchEventTests.DestroyWebViewWhileHandlingTouchEnd
+
+ * UIProcess/WebPageProxy.cpp:
+ (WebKit::WebPageProxy::handlePreventableTouchEvent):
+
2021-05-30 Darin Adler <[email protected]>
Remove WTF::Optional synonym for std::optional, using that class template directly instead
Modified: trunk/Source/WebKit/UIProcess/WebPageProxy.cpp (278253 => 278254)
--- trunk/Source/WebKit/UIProcess/WebPageProxy.cpp 2021-05-30 16:11:40 UTC (rev 278253)
+++ trunk/Source/WebKit/UIProcess/WebPageProxy.cpp 2021-05-30 19:18:56 UTC (rev 278254)
@@ -3118,7 +3118,11 @@
if (isTouchEnd)
++m_handlingPreventableTouchEndCount;
- sendWithAsyncReply(Messages::EventDispatcher::TouchEvent(m_webPageID, event), [this, protectedThis = makeRef(*this), event] (bool handled) {
+ sendWithAsyncReply(Messages::EventDispatcher::TouchEvent(m_webPageID, event), [this, weakThis = makeWeakPtr(*this), event] (bool handled) {
+ auto protectedThis = makeRefPtr(weakThis.get());
+ if (!protectedThis)
+ return;
+
bool didFinishDeferringTouchStart = false;
ASSERT_IMPLIES(event.type() == WebEvent::TouchStart, m_handlingPreventableTouchStartCount);
if (event.type() == WebEvent::TouchStart && m_handlingPreventableTouchStartCount)
@@ -3134,6 +3138,9 @@
m_handledSynchronousTouchEventWhileDispatchingPreventableTouchStart = false;
didReceiveEvent(event.type(), handledOrFailedWithError);
+ if (!m_pageClient)
+ return;
+
pageClient().doneWithTouchEvent(event, handledOrFailedWithError);
if (didFinishDeferringTouchStart)
Modified: trunk/Tools/ChangeLog (278253 => 278254)
--- trunk/Tools/ChangeLog 2021-05-30 16:11:40 UTC (rev 278253)
+++ trunk/Tools/ChangeLog 2021-05-30 19:18:56 UTC (rev 278254)
@@ -1,3 +1,27 @@
+2021-05-30 Wenson Hsieh <[email protected]>
+
+ [iOS] UI process crashes when deallocating WKWebView in a script message handler during an active touch event
+ https://bugs.webkit.org/show_bug.cgi?id=226426
+ rdar://75425319
+
+ Reviewed by Darin Adler.
+
+ Add a new API test that exercises the crash.
+
+ * TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
+ * TestWebKitAPI/Tests/ios/TouchEventTests.mm: Added.
+ (-[TouchEventScriptMessageHandler userContentController:didReceiveScriptMessage:]):
+ (-[WKWebView touchEventGestureRecognizer]):
+ (TestWebKitAPI::updateSimulatedTouchEvent):
+ (TestWebKitAPI::simulatedTouchEvent):
+ (TestWebKitAPI::TEST):
+ * TestWebKitAPI/Tests/ios/active-touch-events.html: Added.
+ * TestWebKitAPI/cocoa/TestWKWebView.h:
+ * TestWebKitAPI/cocoa/TestWKWebView.mm:
+ (-[WKWebView textInputContentView]):
+ (-[TestWKWebView textInputContentView]): Deleted.
+ * TestWebKitAPI/ios/UIKitSPI.h:
+
2021-05-30 Darin Adler <[email protected]>
Remove WTF::Optional synonym for std::optional, using that class template directly instead
Modified: trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj (278253 => 278254)
--- trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj 2021-05-30 16:11:40 UTC (rev 278253)
+++ trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj 2021-05-30 19:18:56 UTC (rev 278254)
@@ -1229,6 +1229,8 @@
F49992C6248DABFD00034167 /* overflow-hidden.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = F49992C5248DABE400034167 /* overflow-hidden.html */; };
F4A32EC41F05F3850047C544 /* dragstart-change-selection-offscreen.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = F4A32EC31F05F3780047C544 /* dragstart-change-selection-offscreen.html */; };
F4A32ECB1F0643370047C544 /* contenteditable-in-iframe.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = F4A32ECA1F0642F40047C544 /* contenteditable-in-iframe.html */; };
+ F4A7CE782662D6E800228685 /* TouchEventTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = F4A7CE772662D6E800228685 /* TouchEventTests.mm */; };
+ F4A7CE7A2662D86C00228685 /* active-touch-events.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = F4A7CE792662D83E00228685 /* active-touch-events.html */; };
F4A9202F1FEE34E900F59590 /* apple-data-url.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = F4A9202E1FEE34C800F59590 /* apple-data-url.html */; };
F4AB578A1F65165400DB0DA1 /* custom-draggable-div.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = F4AB57891F65164B00DB0DA1 /* custom-draggable-div.html */; };
F4AD183825ED791500B1A19F /* FloatQuadTests.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F4AD183725ED791500B1A19F /* FloatQuadTests.cpp */; };
@@ -1353,6 +1355,7 @@
1A9E52C913E65EF4006917F5 /* 18-characters.html in Copy Resources */,
55A81800218102210004A39A /* 400x400-green.png in Copy Resources */,
379028B914FAC24C007E6B43 /* acceptsFirstMouse.html in Copy Resources */,
+ F4A7CE7A2662D86C00228685 /* active-touch-events.html in Copy Resources */,
725C3EF322058A5B007C36FC /* AdditionalSupportedImageTypes.html in Copy Resources */,
1C2B81871C8925A000A5529F /* Ahem.ttf in Copy Resources */,
46C1EA9825758820005E409E /* alert.html in Copy Resources */,
@@ -3069,6 +3072,8 @@
F49992C5248DABE400034167 /* overflow-hidden.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = "overflow-hidden.html"; sourceTree = "<group>"; };
F4A32EC31F05F3780047C544 /* dragstart-change-selection-offscreen.html */ = {isa = PBXFileReference; lastKnownFileType = text.html; path = "dragstart-change-selection-offscreen.html"; sourceTree = "<group>"; };
F4A32ECA1F0642F40047C544 /* contenteditable-in-iframe.html */ = {isa = PBXFileReference; lastKnownFileType = text.html; path = "contenteditable-in-iframe.html"; sourceTree = "<group>"; };
+ F4A7CE772662D6E800228685 /* TouchEventTests.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = TouchEventTests.mm; sourceTree = "<group>"; };
+ F4A7CE792662D83E00228685 /* active-touch-events.html */ = {isa = PBXFileReference; lastKnownFileType = text.html; path = "active-touch-events.html"; sourceTree = "<group>"; };
F4A9202E1FEE34C800F59590 /* apple-data-url.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = "apple-data-url.html"; sourceTree = "<group>"; };
F4AB57891F65164B00DB0DA1 /* custom-draggable-div.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = "custom-draggable-div.html"; sourceTree = "<group>"; };
F4AD183725ED791500B1A19F /* FloatQuadTests.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = FloatQuadTests.cpp; sourceTree = "<group>"; };
@@ -3831,6 +3836,7 @@
F45033F4206BEC95009351CE /* TextAutosizingBoost.mm */,
F494B369263120780060A310 /* TextServicesTests.mm */,
1C24DEEC263001DE00450D07 /* TextStyleFontSize.mm */,
+ F4A7CE772662D6E800228685 /* TouchEventTests.mm */,
F460F6742614DE2F0064F2B6 /* UIFocusTests.mm */,
F46849BD1EEF58E400B937FE /* UIPasteboardTests.mm */,
F402F56B23ECC2FB00865549 /* UIWKInteractionViewProtocol.mm */,
@@ -4184,6 +4190,7 @@
A1C4FB6F1BACCEFA003742D0 /* Resources */ = {
isa = PBXGroup;
children = (
+ F4A7CE792662D83E00228685 /* active-touch-events.html */,
0F16BED72304A1D100B4A167 /* composited.html */,
CEDA12402437C9EA00C28A9E /* editable-region-composited-and-non-composited-overlap.html */,
71E88C4324B533EC00665160 /* img-with-base64-url.html */,
@@ -5761,6 +5768,7 @@
C22FA32B228F8708009D7988 /* TextWidth.mm in Sources */,
7CCE7EDD1A411A9200447C4C /* TimeRanges.cpp in Sources */,
7C83E0BD1D0A650C00FEBCF3 /* TopContentInset.mm in Sources */,
+ F4A7CE782662D6E800228685 /* TouchEventTests.mm in Sources */,
7CCE7ED31A411A7E00447C4C /* TypingStyleCrash.mm in Sources */,
57152B7821DD4E8D000C37CA /* U2fCommandConstructorTest.cpp in Sources */,
5CB40B4E1F4B98D3007DC7B9 /* UIDelegate.mm in Sources */,
Added: trunk/Tools/TestWebKitAPI/Tests/ios/TouchEventTests.mm (0 => 278254)
--- trunk/Tools/TestWebKitAPI/Tests/ios/TouchEventTests.mm (rev 0)
+++ trunk/Tools/TestWebKitAPI/Tests/ios/TouchEventTests.mm 2021-05-30 19:18:56 UTC (rev 278254)
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2021 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import "config.h"
+
+#if ENABLE(IOS_TOUCH_EVENTS)
+
+#import "InstanceMethodSwizzler.h"
+#import "PlatformUtilities.h"
+#import "TestNavigationDelegate.h"
+#import "TestWKWebView.h"
+#import "UIKitSPI.h"
+#import <wtf/RetainPtr.h>
+
+@interface UIView (WKContentView)
+- (void)_webTouchEventsRecognized:(UIWebTouchEventsGestureRecognizer *)gestureRecognizer;
+@end
+
+static WKWebView *globalWebView = nil;
+
+@interface TouchEventScriptMessageHandler : NSObject<WKScriptMessageHandler>
+@end
+
+@implementation TouchEventScriptMessageHandler
+
+- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message
+{
+ if ([message.body isEqualToString:@"touchend"]) {
+ @autoreleasepool {
+ // This @autoreleasepool ensures that the content view is also deallocated upon releasing the web view.
+ [globalWebView removeFromSuperview];
+ [globalWebView release];
+ globalWebView = nil;
+ }
+ }
+}
+
+@end
+
+@interface WKWebView (TouchEventTests)
+@property (nonatomic, readonly) UIWebTouchEventsGestureRecognizer *touchEventGestureRecognizer;
+@end
+
+@implementation WKWebView (TouchEventTests)
+
+- (UIWebTouchEventsGestureRecognizer *)touchEventGestureRecognizer
+{
+ for (UIGestureRecognizer *gestureRecognizer in self.textInputContentView.gestureRecognizers) {
+ if ([gestureRecognizer isKindOfClass:UIWebTouchEventsGestureRecognizer.class])
+ return (UIWebTouchEventsGestureRecognizer *)gestureRecognizer;
+ }
+ return nil;
+}
+
+@end
+
+namespace TestWebKitAPI {
+
+static _UIWebTouchPoint globalTouchPoint { CGPointZero, CGPointZero, 100, UITouchPhaseBegan, 1, 0, 0, 0, UIWebTouchPointTypeDirect };
+static _UIWebTouchEvent globalTouchEvent { UIWebTouchEventTouchBegin, CACurrentMediaTime(), CGPointZero, CGPointZero, 1, 0, false, &globalTouchPoint, 1, true };
+static void updateSimulatedTouchEvent(CGPoint location, UITouchPhase phase)
+{
+ globalTouchPoint.locationInScreenCoordinates = location;
+ globalTouchPoint.locationInDocumentCoordinates = location;
+ globalTouchEvent.locationInScreenCoordinates = location;
+ globalTouchEvent.locationInDocumentCoordinates = location;
+ globalTouchPoint.phase = phase;
+ switch (phase) {
+ case UITouchPhaseBegan:
+ globalTouchEvent.type = UIWebTouchEventTouchBegin;
+ break;
+ case UITouchPhaseMoved:
+ globalTouchEvent.type = UIWebTouchEventTouchChange;
+ break;
+ case UITouchPhaseEnded:
+ globalTouchEvent.type = UIWebTouchEventTouchEnd;
+ break;
+ case UITouchPhaseCancelled:
+ globalTouchEvent.type = UIWebTouchEventTouchCancel;
+ break;
+ default:
+ break;
+ }
+}
+
+static const _UIWebTouchEvent* simulatedTouchEvent(id, SEL)
+{
+ return &globalTouchEvent;
+}
+
+TEST(TouchEventTests, DestroyWebViewWhileHandlingTouchEnd)
+{
+ InstanceMethodSwizzler lastTouchEventSwizzler { UIWebTouchEventsGestureRecognizer.class, @selector(lastTouchEvent), reinterpret_cast<IMP>(simulatedTouchEvent) };
+ @autoreleasepool {
+ auto messageHandler = adoptNS([TouchEventScriptMessageHandler new]);
+ auto controller = adoptNS([[WKUserContentController alloc] init]);
+ [controller addScriptMessageHandler:messageHandler.get() name:@"testHandler"];
+
+ auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
+ [configuration setUserContentController:controller.get()];
+
+ globalWebView = [[WKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500) configuration:configuration.get()];
+ auto hostWindow = adoptNS([[UIWindow alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
+ [hostWindow setHidden:NO];
+ [hostWindow addSubview:globalWebView];
+
+ [globalWebView loadRequest:[NSURLRequest requestWithURL:[NSBundle.mainBundle URLForResource:@"active-touch-events" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]]];
+ [globalWebView _test_waitForDidFinishNavigation];
+
+ updateSimulatedTouchEvent(CGPointMake(100, 100), UITouchPhaseBegan);
+ [[globalWebView textInputContentView] _webTouchEventsRecognized:globalWebView.touchEventGestureRecognizer];
+
+ updateSimulatedTouchEvent(CGPointMake(100, 100), UITouchPhaseEnded);
+ [[globalWebView textInputContentView] _webTouchEventsRecognized:globalWebView.touchEventGestureRecognizer];
+ }
+
+ __block bool done = false;
+ dispatch_async(dispatch_get_main_queue(), ^{
+ done = true;
+ });
+ TestWebKitAPI::Util::run(&done);
+}
+
+} // namespace TestWebKitAPI
+
+#endif // ENABLE(IOS_TOUCH_EVENTS)
Added: trunk/Tools/TestWebKitAPI/Tests/ios/active-touch-events.html (0 => 278254)
--- trunk/Tools/TestWebKitAPI/Tests/ios/active-touch-events.html (rev 0)
+++ trunk/Tools/TestWebKitAPI/Tests/ios/active-touch-events.html 2021-05-30 19:18:56 UTC (rev 278254)
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<html>
+<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
+<head>
+<style>
+body, html {
+ width: 100%;
+ height: 100%;
+ margin: 0;
+}
+
+#target {
+ width: 100%;
+ height: 100%;
+ background: tomato;
+}
+</style>
+</head>
+<body>
+<div id="target"></div>
+<script>
+function handleTouchEvent(event) {
+ webkit.messageHandlers.testHandler.postMessage(event.type);
+}
+
+let target = document.getElementById("target");
+target.addEventListener("touchstart", handleTouchEvent);
+target.addEventListener("touchmove", handleTouchEvent);
+target.addEventListener("touchcancel", handleTouchEvent);
+target.addEventListener("touchend", handleTouchEvent);
+</script>
+</body>
+</html>
Modified: trunk/Tools/TestWebKitAPI/cocoa/TestWKWebView.h (278253 => 278254)
--- trunk/Tools/TestWebKitAPI/cocoa/TestWKWebView.h 2021-05-30 16:11:40 UTC (rev 278253)
+++ trunk/Tools/TestWebKitAPI/cocoa/TestWKWebView.h 2021-05-30 19:18:56 UTC (rev 278254)
@@ -48,6 +48,9 @@
@end
@interface WKWebView (TestWebKitAPI)
+#if PLATFORM(IOS_FAMILY)
+@property (nonatomic, readonly) UIView <UITextInputPrivate, UITextInputInternal, UITextInputMultiDocument, UIWKInteractionViewProtocol, UITextInputTokenizer> *textInputContentView;
+#endif
@property (nonatomic, readonly) NSString *contentsAsString;
@property (nonatomic, readonly) NSArray<NSString *> *tagsInBody;
- (void)loadTestPageNamed:(NSString *)pageName;
@@ -112,7 +115,6 @@
@end
@interface TestWKWebView (IOSOnly)
-@property (nonatomic, readonly) UIView <UITextInputPrivate, UITextInputInternal, UITextInputMultiDocument, UIWKInteractionViewProtocol, UITextInputTokenizer> *textInputContentView;
@property (nonatomic, readonly) RetainPtr<NSArray> selectionRectsAfterPresentationUpdate;
@property (nonatomic, readonly) CGRect caretViewRectInContentCoordinates;
@property (nonatomic, readonly) NSArray<NSValue *> *selectionViewRectsInContentCoordinates;
Modified: trunk/Tools/TestWebKitAPI/cocoa/TestWKWebView.mm (278253 => 278254)
--- trunk/Tools/TestWebKitAPI/cocoa/TestWKWebView.mm 2021-05-30 16:11:40 UTC (rev 278253)
+++ trunk/Tools/TestWebKitAPI/cocoa/TestWKWebView.mm 2021-05-30 19:18:56 UTC (rev 278254)
@@ -131,6 +131,15 @@
return success;
}
+#if PLATFORM(IOS_FAMILY)
+
+- (UIView <UITextInputPrivate, UITextInputMultiDocument> *)textInputContentView
+{
+ return (UIView <UITextInputPrivate, UITextInputMultiDocument> *)[self valueForKey:@"_currentContentView"];
+}
+
+#endif // PLATFORM(IOS_FAMILY)
+
- (NSString *)contentsAsString
{
__block bool done = false;
@@ -685,11 +694,6 @@
}
}
-- (UIView <UITextInputPrivate, UITextInputMultiDocument> *)textInputContentView
-{
- return (UIView <UITextInputPrivate, UITextInputMultiDocument> *)[self valueForKey:@"_currentContentView"];
-}
-
- (RetainPtr<NSArray>)selectionRectsAfterPresentationUpdate
{
RetainPtr<TestWKWebView> retainedSelf = self;
Modified: trunk/Tools/TestWebKitAPI/ios/UIKitSPI.h (278253 => 278254)
--- trunk/Tools/TestWebKitAPI/ios/UIKitSPI.h 2021-05-30 16:11:40 UTC (rev 278253)
+++ trunk/Tools/TestWebKitAPI/ios/UIKitSPI.h 2021-05-30 19:18:56 UTC (rev 278254)
@@ -47,6 +47,7 @@
#import <UIKit/UIViewController_Private.h>
#import <UIKit/UIWKTextInteractionAssistant.h>
#import <UIKit/UIWebFormAccessory.h>
+#import <UIKit/UIWebTouchEventsGestureRecognizer.h>
#import <UIKit/_UINavigationInteractiveTransition.h>
IGNORE_WARNINGS_BEGIN("deprecated-implementations")