- 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)