Title: [277501] trunk
Revision
277501
Author
[email protected]
Date
2021-05-14 11:56:34 -0700 (Fri, 14 May 2021)

Log Message

[iOS] contextmenu hints can be clipped by the WKWebView
https://bugs.webkit.org/show_bug.cgi?id=224204
<rdar://problem/77089174>

Reviewed by Wenson Hsieh.

Source/WebKit:

r275562 tried to fix this by moving the interaction previews to a separate `UIWindow`, but
this had the unfortunate consequence of placing those previews on top of everything in the
current `UIWindow` (e.g. above the share sheet). As such, WebKit can't use a `UIWindow` for
this as the `WKWebView` has no idea what the view hierarchy above it looks like.

Test: ContextMenu.HintPreviewContainer

* UIProcess/API/Cocoa/WKUIDelegatePrivate.h:
Add new SPI `-[WKUIDelegate _contextMenuHintPreviewContainerViewForWebView:]` that's used as
the container for the `UIPreviewTarget` created for the contextmenu hint preview.

* UIProcess/ios/WKContentViewInteraction.h:
* UIProcess/ios/WKContentViewInteraction.mm:
(-[WKContentView cleanUpInteraction]):
(-[WKContentView cleanUpInteractionPreviewContainers]): Added.
(-[WKContentView _didCommitLoadForMainFrame]):
(-[WKContentView _createPreviewContainerWithLayerName:]):
(-[WKContentView containerForDropPreviews]):
(-[WKContentView _removeContainerForDropPreviews]): Added.
(-[WKContentView containerForDragPreviews]):
(-[WKContentView _removeContainerForDragPreviews]): Added.
(-[WKContentView containerForContextMenuHintPreviews]):
(-[WKContentView _removeContainerForContextMenuHintPreviews]): Added.
(-[WKContentView cleanUpDragSourceSessionState]):
(-[WKContentView _removeContextMenuViewIfPossible]):
(-[WKContentView dropInteraction:concludeDrop:]):
(-[WKContentView _hideTargetedPreviewContainerViews]): Deleted.
Clean up and centralize logic around creating and removing interaction preview containers.

* UIProcess/ios/WKContentView.mm:
(-[WKContentView didMoveToWindow]):
If the `WKWebView` is removed from it's parent, make sure to remove the contextmenu hint
preview as well as it could be outside the view hierarchy of the `WKWebView` from the SPI.

Tools:

* TestWebKitAPI/Tests/WebKitCocoa/ContextMenus.mm:
(-[TestContextMenuHintPreviewContainerUIDelegate webView:contextMenuWillPresentForElement:]): Added.
(-[TestContextMenuHintPreviewContainerUIDelegate _contextMenuHintPreviewContainerViewForWebView:]): Added.
(TEST.ContextMenu.HintPreviewContainer): Added.

Modified Paths

Diff

Modified: trunk/Source/WebKit/ChangeLog (277500 => 277501)


--- trunk/Source/WebKit/ChangeLog	2021-05-14 18:34:33 UTC (rev 277500)
+++ trunk/Source/WebKit/ChangeLog	2021-05-14 18:56:34 UTC (rev 277501)
@@ -1,3 +1,45 @@
+2021-05-14  Devin Rousso  <[email protected]>
+
+        [iOS] contextmenu hints can be clipped by the WKWebView
+        https://bugs.webkit.org/show_bug.cgi?id=224204
+        <rdar://problem/77089174>
+
+        Reviewed by Wenson Hsieh.
+
+        r275562 tried to fix this by moving the interaction previews to a separate `UIWindow`, but
+        this had the unfortunate consequence of placing those previews on top of everything in the
+        current `UIWindow` (e.g. above the share sheet). As such, WebKit can't use a `UIWindow` for
+        this as the `WKWebView` has no idea what the view hierarchy above it looks like.
+
+        Test: ContextMenu.HintPreviewContainer
+
+        * UIProcess/API/Cocoa/WKUIDelegatePrivate.h:
+        Add new SPI `-[WKUIDelegate _contextMenuHintPreviewContainerViewForWebView:]` that's used as
+        the container for the `UIPreviewTarget` created for the contextmenu hint preview.
+
+        * UIProcess/ios/WKContentViewInteraction.h:
+        * UIProcess/ios/WKContentViewInteraction.mm:
+        (-[WKContentView cleanUpInteraction]):
+        (-[WKContentView cleanUpInteractionPreviewContainers]): Added.
+        (-[WKContentView _didCommitLoadForMainFrame]):
+        (-[WKContentView _createPreviewContainerWithLayerName:]):
+        (-[WKContentView containerForDropPreviews]):
+        (-[WKContentView _removeContainerForDropPreviews]): Added.
+        (-[WKContentView containerForDragPreviews]):
+        (-[WKContentView _removeContainerForDragPreviews]): Added.
+        (-[WKContentView containerForContextMenuHintPreviews]):
+        (-[WKContentView _removeContainerForContextMenuHintPreviews]): Added.
+        (-[WKContentView cleanUpDragSourceSessionState]):
+        (-[WKContentView _removeContextMenuViewIfPossible]):
+        (-[WKContentView dropInteraction:concludeDrop:]):
+        (-[WKContentView _hideTargetedPreviewContainerViews]): Deleted.
+        Clean up and centralize logic around creating and removing interaction preview containers.
+
+        * UIProcess/ios/WKContentView.mm:
+        (-[WKContentView didMoveToWindow]):
+        If the `WKWebView` is removed from it's parent, make sure to remove the contextmenu hint
+        preview as well as it could be outside the view hierarchy of the `WKWebView` from the SPI.
+
 2021-05-14  Chris Dumez  <[email protected]>
 
         Introduce FileSystem::updateFileModificationTime()

Modified: trunk/Source/WebKit/UIProcess/API/Cocoa/WKUIDelegatePrivate.h (277500 => 277501)


--- trunk/Source/WebKit/UIProcess/API/Cocoa/WKUIDelegatePrivate.h	2021-05-14 18:34:33 UTC (rev 277500)
+++ trunk/Source/WebKit/UIProcess/API/Cocoa/WKUIDelegatePrivate.h	2021-05-14 18:56:34 UTC (rev 277501)
@@ -191,6 +191,7 @@
 - (NSArray *)_attachmentListForWebView:(WKWebView *)webView sourceIsManaged:(BOOL*)sourceIsManaged WK_API_AVAILABLE(ios(10.3));
 - (NSUInteger)_webView:(WKWebView *)webView indexIntoAttachmentListForElement:(_WKActivatedElementInfo *)element WK_API_AVAILABLE(ios(10.3));
 - (UIEdgeInsets)_webView:(WKWebView *)webView finalObscuredInsetsForScrollView:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset WK_API_AVAILABLE(ios(9.0));
+- (UIView *)_contextMenuHintPreviewContainerViewForWebView:(WKWebView *)webView WK_API_AVAILABLE(ios(WK_IOS_TBA));
 - (UIViewController *)_webView:(WKWebView *)webView previewViewControllerForURL:(NSURL *)url defaultActions:(NSArray<_WKElementAction *> *)actions elementInfo:(_WKActivatedElementInfo *)elementInfo WK_API_DEPRECATED_WITH_REPLACEMENT("webView:contextMenuConfigurationForElement:completionHandler:", ios(9.0, 13.0));
 - (UIViewController *)_webView:(WKWebView *)webView previewViewControllerForAnimatedImageAtURL:(NSURL *)url defaultActions:(NSArray<_WKElementAction *> *)actions elementInfo:(_WKActivatedElementInfo *)elementInfo imageSize:(CGSize)imageSize WK_API_DEPRECATED_WITH_REPLACEMENT("webView:contextMenuConfigurationForElement:completionHandler:", ios(9.0, 13.0));
 - (UIViewController *)_presentingViewControllerForWebView:(WKWebView *)webView WK_API_AVAILABLE(ios(10.0));

Modified: trunk/Source/WebKit/UIProcess/ios/WKContentView.mm (277500 => 277501)


--- trunk/Source/WebKit/UIProcess/ios/WKContentView.mm	2021-05-14 18:34:33 UTC (rev 277500)
+++ trunk/Source/WebKit/UIProcess/ios/WKContentView.mm	2021-05-14 18:56:34 UTC (rev 277501)
@@ -349,6 +349,8 @@
 
     if (self.window)
         [self setUpInteraction];
+    else
+        [self cleanUpInteractionPreviewContainers];
 }
 
 ALLOW_DEPRECATED_DECLARATIONS_BEGIN

Modified: trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.h (277500 => 277501)


--- trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.h	2021-05-14 18:34:33 UTC (rev 277500)
+++ trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.h	2021-05-14 18:56:34 UTC (rev 277501)
@@ -531,6 +531,7 @@
 
 - (void)setUpInteraction;
 - (void)cleanUpInteraction;
+- (void)cleanUpInteractionPreviewContainers;
 
 - (void)scrollViewWillStartPanOrPinchGesture;
 
@@ -579,7 +580,6 @@
 - (void)_elementDidFocus:(const WebKit::FocusedElementInformation&)information userIsInteracting:(BOOL)userIsInteracting blurPreviousNode:(BOOL)blurPreviousNode activityStateChanges:(OptionSet<WebCore::ActivityState::Flag>)activityStateChanges userObject:(NSObject <NSSecureCoding> *)userObject;
 - (void)_updateInputContextAfterBlurringAndRefocusingElement;
 - (void)_elementDidBlur;
-- (void)_hideTargetedPreviewContainerViews;
 - (void)_didUpdateInputMode:(WebCore::InputMode)mode;
 - (void)_didUpdateEditorState;
 - (void)_hardwareKeyboardAvailabilityChanged;

Modified: trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm (277500 => 277501)


--- trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm	2021-05-14 18:34:33 UTC (rev 277500)
+++ trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm	2021-05-14 18:56:34 UTC (rev 277501)
@@ -1058,6 +1058,10 @@
 
     _layerTreeTransactionIdAtLastInteractionStart = { };
 
+#if USE(UICONTEXTMENU)
+    [self _removeContextMenuViewIfPossible];
+#endif // USE(UICONTEXTMENU)
+
 #if ENABLE(DRAG_SUPPORT)
     [existingLocalDragSessionContext(_dragDropInteractionState.dragSession()) cleanUpTemporaryDirectories];
     [self teardownDragAndDropInteractions];
@@ -1112,6 +1116,10 @@
     [self _tearDownImageExtraction];
 #endif
 
+    [self _removeContainerForContextMenuHintPreviews];
+    [self _removeContainerForDragPreviews];
+    [self _removeContainerForDropPreviews];
+
     _hasSetUpInteractions = NO;
     _suppressSelectionAssistantReasons = { };
 
@@ -1122,6 +1130,11 @@
     _cachedSelectedTextRange = nil;
 }
 
+- (void)cleanUpInteractionPreviewContainers
+{
+    [self _removeContainerForContextMenuHintPreviews];
+}
+
 - (void)_cancelPendingKeyEventHandler
 {
     if (!_page)
@@ -4562,7 +4575,9 @@
 #endif
     [self _elementDidBlur];
     [self _cancelLongPressGestureRecognizer];
-    [self _hideTargetedPreviewContainerViews];
+    [self _removeContainerForContextMenuHintPreviews];
+    [self _removeContainerForDragPreviews];
+    [self _removeContainerForDropPreviews];
     [_webView _didCommitLoadForMainFrame];
 
     _textInteractionDidChangeFocusedElement = NO;
@@ -7633,45 +7648,75 @@
     auto container = adoptNS([[UIView alloc] init]);
     [container layer].anchorPoint = CGPointZero;
     [container layer].name = layerName;
-    [_interactionViewsContainerView addSubview:container.get()];
     return container;
 }
 
 - (UIView *)containerForDropPreviews
 {
-    if (!_dropPreviewContainerView)
+    if (!_dropPreviewContainerView) {
         _dropPreviewContainerView = [self _createPreviewContainerWithLayerName:@"Drop Preview Container"];
+        [_interactionViewsContainerView addSubview:_dropPreviewContainerView.get()];
+    }
 
     ASSERT([_dropPreviewContainerView superview]);
-    [_dropPreviewContainerView setHidden:NO];
     return _dropPreviewContainerView.get();
 }
 
+- (void)_removeContainerForDropPreviews
+{
+    if (!_dropPreviewContainerView)
+        return;
+
+    [std::exchange(_dropPreviewContainerView, nil) removeFromSuperview];
+}
+
 - (UIView *)containerForDragPreviews
 {
-    if (!_dragPreviewContainerView)
+    if (!_dragPreviewContainerView) {
         _dragPreviewContainerView = [self _createPreviewContainerWithLayerName:@"Drag Preview Container"];
+        [_interactionViewsContainerView addSubview:_dragPreviewContainerView.get()];
+    }
 
     ASSERT([_dragPreviewContainerView superview]);
-    [_dragPreviewContainerView setHidden:NO];
     return _dragPreviewContainerView.get();
 }
 
+- (void)_removeContainerForDragPreviews
+{
+    if (!_dragPreviewContainerView)
+        return;
+
+    [std::exchange(_dragPreviewContainerView, nil) removeFromSuperview];
+}
+
 - (UIView *)containerForContextMenuHintPreviews
 {
-    if (!_contextMenuHintContainerView)
+    if (!_contextMenuHintContainerView) {
         _contextMenuHintContainerView = [self _createPreviewContainerWithLayerName:@"Context Menu Hint Preview Container"];
 
+        RetainPtr<UIView> containerView;
+
+        if (auto uiDelegate = static_cast<id<WKUIDelegatePrivate>>(self.webView.UIDelegate)) {
+            if ([uiDelegate respondsToSelector:@selector(_contextMenuHintPreviewContainerViewForWebView:)])
+                containerView = [uiDelegate _contextMenuHintPreviewContainerViewForWebView:self.webView];
+        }
+
+        if (!containerView)
+            containerView = _interactionViewsContainerView;
+
+        [containerView addSubview:_contextMenuHintContainerView.get()];
+    }
+
     ASSERT([_contextMenuHintContainerView superview]);
-    [_contextMenuHintContainerView setHidden:NO];
     return _contextMenuHintContainerView.get();
 }
 
-- (void)_hideTargetedPreviewContainerViews
+- (void)_removeContainerForContextMenuHintPreviews
 {
-    [_dropPreviewContainerView setHidden:YES];
-    [_dragPreviewContainerView setHidden:YES];
-    [_contextMenuHintContainerView setHidden:YES];
+    if (!_contextMenuHintContainerView)
+        return;
+
+    [std::exchange(_contextMenuHintContainerView, nil) removeFromSuperview];
 }
 
 #pragma mark - WKDeferringGestureRecognizerDelegate
@@ -7992,7 +8037,7 @@
     [[WebItemProviderPasteboard sharedInstance] clearRegistrationLists];
     [self _restoreCalloutBarIfNeeded];
 
-    [std::exchange(_dragPreviewContainerView, nil) removeFromSuperview];
+    [self _removeContainerForDragPreviews];
     [std::exchange(_visibleContentViewSnapshot, nil) removeFromSuperview];
     [_editDropCaretView remove];
     _editDropCaretView = nil;
@@ -8431,7 +8476,7 @@
     if ([self selectControl])
         return;
     
-    [std::exchange(_contextMenuHintContainerView, nil) removeFromSuperview];
+    [self _removeContainerForContextMenuHintPreviews];
 }
 
 #endif // USE(UICONTEXTMENU)
@@ -8928,7 +8973,7 @@
 
 - (void)dropInteraction:(UIDropInteraction *)interaction concludeDrop:(id <UIDropSession>)session
 {
-    [std::exchange(_dropPreviewContainerView, nil) removeFromSuperview];
+    [self _removeContainerForDropPreviews];
     [std::exchange(_visibleContentViewSnapshot, nil) removeFromSuperview];
     [std::exchange(_unselectedContentSnapshot, nil) removeFromSuperview];
     _dragDropInteractionState.clearAllDelayedItemPreviewProviders();

Modified: trunk/Tools/ChangeLog (277500 => 277501)


--- trunk/Tools/ChangeLog	2021-05-14 18:34:33 UTC (rev 277500)
+++ trunk/Tools/ChangeLog	2021-05-14 18:56:34 UTC (rev 277501)
@@ -1,3 +1,16 @@
+2021-05-14  Devin Rousso  <[email protected]>
+
+        [iOS] contextmenu hints can be clipped by the WKWebView
+        https://bugs.webkit.org/show_bug.cgi?id=224204
+        <rdar://problem/77089174>
+
+        Reviewed by Wenson Hsieh.
+
+        * TestWebKitAPI/Tests/WebKitCocoa/ContextMenus.mm:
+        (-[TestContextMenuHintPreviewContainerUIDelegate webView:contextMenuWillPresentForElement:]): Added.
+        (-[TestContextMenuHintPreviewContainerUIDelegate _contextMenuHintPreviewContainerViewForWebView:]): Added.
+        (TEST.ContextMenu.HintPreviewContainer): Added.
+
 2021-05-14  Chris Dumez  <[email protected]>
 
         Introduce FileSystem::updateFileModificationTime()

Modified: trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/ContextMenus.mm (277500 => 277501)


--- trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/ContextMenus.mm	2021-05-14 18:34:33 UTC (rev 277500)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/ContextMenus.mm	2021-05-14 18:56:34 UTC (rev 277501)
@@ -41,6 +41,7 @@
 static bool previewingViewControllerCalled;
 static bool previewActionItemsCalled;
 static bool didEndCalled;
+static bool didAskForHintPreviewContainer;
 static bool alternateURLRequested;
 static RetainPtr<NSURL> linkURL;
 
@@ -352,4 +353,35 @@
     EXPECT_TRUE(willPresentCalled);
 }
 
+@interface TestContextMenuHintPreviewContainerUIDelegate : NSObject <WKUIDelegate>
+@end
+
+@implementation TestContextMenuHintPreviewContainerUIDelegate
+
+- (void)webView:(WKWebView *)webView contextMenuWillPresentForElement:(WKContextMenuElementInfo *)elementInfo
+{
+    willPresentCalled = true;
+}
+
+- (UIView *)_contextMenuHintPreviewContainerViewForWebView:(WKWebView *)webView
+{
+    didAskForHintPreviewContainer = true;
+    return nil;
+}
+
+@end
+
+// FIXME: Re-enable this test once rdar://59610140 is resolved
+TEST(ContextMenu, DISABLED_HintPreviewContainer)
+{
+    auto driver = contextMenuWebViewDriver([TestContextMenuHintPreviewContainerUIDelegate class]);
+    [driver begin:^(BOOL result) {
+        EXPECT_TRUE(result);
+        [driver clickDown];
+        [driver clickUp];
+    }];
+    TestWebKitAPI::Util::run(&willPresentCalled);
+    EXPECT_TRUE(didAskForHintPreviewContainer);
+}
+
 #endif // USE(UICONTEXTMENU)
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to