⚠ Archived content — this site is no longer maintained.   Current WebKit documentation is at docs.webkit.org.

Changeset 275562 in webkit


Ignore:
Timestamp:
Apr 6, 2021, 3:29:15 PM (5 years ago)
Author:
Devin Rousso
Message:

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

Reviewed by Wenson Hsieh.

  • UIProcess/ios/WKContentViewInteraction.h:
  • UIProcess/ios/WKContentViewInteraction.mm:

(-[WKContentView cleanUpInteraction]):
(-[WKContentView cleanUpRelatedViews]): Added.
(-[WKContentView _didScroll]):
(-[WKContentView textEffectsWindow]):
(-[WKContentView _createPreviewContainerWithLayerName:]):
(-[WKContentView containerForDropPreviews]):
(-[WKContentView containerForDragPreviews]):
(-[WKContentView containerForContextMenuHintPreviews]):
(-[WKContentView _hideTargetedPreviewContainerViews]):
(-[WKContentView _updateTargetedPreviewViewsContainerViewFrameIfNeeded]): Added.
(-[WKContentView _removeTargetedPreviewViewsContainerViewIfPossible]): Added.
(-[WKContentView _updateTargetedPreviewScrollViewUsingContainerScrollingNodeID:]): Added.
(-[WKContentView cleanUpDragSourceSessionState]):
(-[WKContentView _createTargetedContextMenuHintPreviewForFocusedElement]):
(-[WKContentView _createTargetedContextMenuHintPreviewIfPossible]):
(-[WKContentView _removeContextMenuViewIfPossible]):
(-[WKContentView dropInteraction:concludeDrop:]):
(-[WKContentView overridePositionTrackingViewForTargetedPreviewIfNecessary:containerScrollingNodeID:]): Deleted.
Instead of attaching the contextmenu hint container (_contextMenuHintContainerView) to
the WKWebView (via the _interactionViewsContainerView), attach it to a new container
(_targetedPreviewViewsContainerView) under the UITextEffectsWindow so that if the
contextmenu target is near the edge of the WKWebView then the shadow of the contextmenu
hint will not be clipped by the WKWebView. Whenever any view is scrolled, adjust the
frame of the _targetedPreviewViewsContainerView so that the contextmenu hint moves.

  • UIProcess/ios/WKContentView.mm:

(-[WKContentView didMoveToWindow]):
Call -cleanUpRelatedViews if no longer attached to a window so that the
_targetedPreviewViewsContainerView is not left behind if the WKWebView is moved.

  • UIProcess/RemoteLayerTree/ios/RemoteScrollingCoordinatorProxyIOS.mm:

(WebKit::RemoteScrollingCoordinatorProxy::scrollViewForScrollingNodeID const):

  • UIProcess/RemoteLayerTree/ios/ScrollingTreeFrameScrollingNodeRemoteIOS.h:
  • UIProcess/RemoteLayerTree/ios/ScrollingTreeFrameScrollingNodeRemoteIOS.mm:

(WebKit::ScrollingTreeFrameScrollingNodeRemoteIOS::scrollView const): Added.
ScrollingTreeFrameScrollingNodeRemoteIOS also has a corresponding UIScrollView.

  • Platform/spi/ios/UIKitSPI.h:

Remove no longer needed SPI now that WebKit manages the position of the contextmenu hint.

Location:
trunk/Source/WebKit
Files:
8 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebKit/ChangeLog

    r275560 r275562  
     12021-04-06  Devin Rousso  <drousso@apple.com>
     2
     3        [iOS] contextmenu hints can be clipped by the WKWebView
     4        https://bugs.webkit.org/show_bug.cgi?id=224204
     5        <rdar://problem/75504620>
     6
     7        Reviewed by Wenson Hsieh.
     8
     9        * UIProcess/ios/WKContentViewInteraction.h:
     10        * UIProcess/ios/WKContentViewInteraction.mm:
     11        (-[WKContentView cleanUpInteraction]):
     12        (-[WKContentView cleanUpRelatedViews]): Added.
     13        (-[WKContentView _didScroll]):
     14        (-[WKContentView textEffectsWindow]):
     15        (-[WKContentView _createPreviewContainerWithLayerName:]):
     16        (-[WKContentView containerForDropPreviews]):
     17        (-[WKContentView containerForDragPreviews]):
     18        (-[WKContentView containerForContextMenuHintPreviews]):
     19        (-[WKContentView _hideTargetedPreviewContainerViews]):
     20        (-[WKContentView _updateTargetedPreviewViewsContainerViewFrameIfNeeded]): Added.
     21        (-[WKContentView _removeTargetedPreviewViewsContainerViewIfPossible]): Added.
     22        (-[WKContentView _updateTargetedPreviewScrollViewUsingContainerScrollingNodeID:]): Added.
     23        (-[WKContentView cleanUpDragSourceSessionState]):
     24        (-[WKContentView _createTargetedContextMenuHintPreviewForFocusedElement]):
     25        (-[WKContentView _createTargetedContextMenuHintPreviewIfPossible]):
     26        (-[WKContentView _removeContextMenuViewIfPossible]):
     27        (-[WKContentView dropInteraction:concludeDrop:]):
     28        (-[WKContentView overridePositionTrackingViewForTargetedPreviewIfNecessary:containerScrollingNodeID:]): Deleted.
     29        Instead of attaching the contextmenu hint container (`_contextMenuHintContainerView`) to
     30        the `WKWebView` (via the `_interactionViewsContainerView`), attach it to a new container
     31        (`_targetedPreviewViewsContainerView`) under the `UITextEffectsWindow` so that if the
     32        contextmenu target is near the edge of the `WKWebView` then the shadow of the contextmenu
     33        hint will not be clipped by the `WKWebView`. Whenever any view is scrolled, adjust the
     34        `frame` of the `_targetedPreviewViewsContainerView` so that the contextmenu hint moves.
     35
     36        * UIProcess/ios/WKContentView.mm:
     37        (-[WKContentView didMoveToWindow]):
     38        Call `-cleanUpRelatedViews` if no longer attached to a `window` so that the
     39        `_targetedPreviewViewsContainerView` is not left behind if the `WKWebView` is moved.
     40
     41        * UIProcess/RemoteLayerTree/ios/RemoteScrollingCoordinatorProxyIOS.mm:
     42        (WebKit::RemoteScrollingCoordinatorProxy::scrollViewForScrollingNodeID const):
     43        * UIProcess/RemoteLayerTree/ios/ScrollingTreeFrameScrollingNodeRemoteIOS.h:
     44        * UIProcess/RemoteLayerTree/ios/ScrollingTreeFrameScrollingNodeRemoteIOS.mm:
     45        (WebKit::ScrollingTreeFrameScrollingNodeRemoteIOS::scrollView const): Added.
     46        `ScrollingTreeFrameScrollingNodeRemoteIOS` also has a corresponding `UIScrollView`.
     47
     48        * Platform/spi/ios/UIKitSPI.h:
     49        Remove no longer needed SPI now that WebKit manages the position of the contextmenu hint.
     50
    1512021-04-06  Patrick Angle  <pangle@apple.com>
    252
  • trunk/Source/WebKit/Platform/spi/ios/UIKitSPI.h

    r275532 r275562  
    12191219
    12201220#if USE(UICONTEXTMENU)
    1221 @interface UITargetedPreview ()
    1222 @property (nonatomic, strong, setter=_setOverridePositionTrackingView:) UIView *overridePositionTrackingView;
    1223 @end
    1224 
    12251221@interface UIContextMenuInteraction ()
    12261222@property (nonatomic, readonly) UIGestureRecognizer *gestureRecognizerForFailureRelationships;
  • trunk/Source/WebKit/UIProcess/RemoteLayerTree/ios/RemoteScrollingCoordinatorProxyIOS.mm

    r272610 r275562  
    3232#import "RemoteLayerTreeHost.h"
    3333#import "RemoteLayerTreeNode.h"
     34#import "ScrollingTreeFrameScrollingNodeRemoteIOS.h"
    3435#import "ScrollingTreeOverflowScrollingNodeIOS.h"
    3536#import "WebPageProxy.h"
     
    5758{
    5859    auto* treeNode = m_scrollingTree->nodeForID(nodeID);
    59     if (!is<ScrollingTreeOverflowScrollingNode>(treeNode))
    60         return nil;
    61 
    62     auto* scrollingNode = downcast<ScrollingTreeOverflowScrollingNode>(treeNode);
    63     // All ScrollingTreeOverflowScrollingNodes are ScrollingTreeOverflowScrollingNodeIOS on iOS.
    64     return static_cast<ScrollingTreeOverflowScrollingNodeIOS*>(scrollingNode)->scrollView();
     60
     61    if (is<ScrollingTreeOverflowScrollingNode>(treeNode)) {
     62        auto* overflowScrollingNode = downcast<ScrollingTreeOverflowScrollingNode>(treeNode);
     63
     64        // All ScrollingTreeOverflowScrollingNodes are ScrollingTreeOverflowScrollingNodeIOS on iOS.
     65        return static_cast<ScrollingTreeOverflowScrollingNodeIOS*>(overflowScrollingNode)->scrollView();
     66    }
     67
     68    if (is<ScrollingTreeFrameScrollingNode>(treeNode)) {
     69        auto* frameScrollingNode = downcast<ScrollingTreeFrameScrollingNode>(treeNode);
     70
     71        // All ScrollingTreeFrameScrollingNodes are ScrollingTreeFrameScrollingNodeRemoteIOS on iOS.
     72        return static_cast<ScrollingTreeFrameScrollingNodeRemoteIOS*>(frameScrollingNode)->scrollView();
     73    }
     74
     75    return nil;
    6576}
    6677
  • trunk/Source/WebKit/UIProcess/RemoteLayerTree/ios/ScrollingTreeFrameScrollingNodeRemoteIOS.h

    r242356 r275562  
    3939    virtual ~ScrollingTreeFrameScrollingNodeRemoteIOS();
    4040
     41    UIScrollView *scrollView() const;
     42
    4143private:
    4244    ScrollingTreeFrameScrollingNodeRemoteIOS(WebCore::ScrollingTree&, WebCore::ScrollingNodeType, WebCore::ScrollingNodeID);
  • trunk/Source/WebKit/UIProcess/RemoteLayerTree/ios/ScrollingTreeFrameScrollingNodeRemoteIOS.mm

    r269184 r275562  
    5050ScrollingTreeFrameScrollingNodeRemoteIOS::~ScrollingTreeFrameScrollingNodeRemoteIOS()
    5151{
     52}
     53
     54UIScrollView *ScrollingTreeFrameScrollingNodeRemoteIOS::scrollView() const
     55{
     56    return m_scrollingNodeDelegate ? m_scrollingNodeDelegate->scrollView() : nil;
    5257}
    5358
  • trunk/Source/WebKit/UIProcess/ios/WKContentView.mm

    r275297 r275562  
    337337    if (self.window)
    338338        [self setUpInteraction];
     339    else
     340        [self cleanUpRelatedViews];
    339341}
    340342
  • trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.h

    r275546 r275562  
    299299    RetainPtr<_UIHighlightView> _highlightView;
    300300    RetainPtr<UIView> _interactionViewsContainerView;
     301    RetainPtr<UIView> _targetedPreviewViewsContainerView;
     302    WeakObjCPtr<UIScrollView> _scrollViewForTargetedPreview;
    301303    RetainPtr<UIView> _contextMenuHintContainerView;
    302304    RetainPtr<UIView> _dragPreviewContainerView;
     
    526528- (void)setUpInteraction;
    527529- (void)cleanUpInteraction;
     530- (void)cleanUpRelatedViews;
    528531
    529532- (void)scrollViewWillStartPanOrPinchGesture;
  • trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm

    r275546 r275562  
    10471047    _layerTreeTransactionIdAtLastInteractionStart = { };
    10481048
     1049#if USE(UICONTEXTMENU)
     1050    [self _removeContextMenuViewIfPossible];
     1051#endif // USE(UICONTEXTMENU)
     1052
    10491053#if ENABLE(DRAG_SUPPORT)
    10501054    [existingLocalDragSessionContext(_dragDropInteractionState.dragSession()) cleanUpTemporaryDirectories];
     
    11011105#endif
    11021106
     1107    [std::exchange(_targetedPreviewViewsContainerView, nil) removeFromSuperview];
     1108
    11031109    _hasSetUpInteractions = NO;
    11041110    _suppressSelectionAssistantReasons = { };
     
    11091115
    11101116    _cachedSelectedTextRange = nil;
     1117}
     1118
     1119- (void)cleanUpRelatedViews
     1120{
     1121    [std::exchange(_targetedPreviewViewsContainerView, nil) removeFromSuperview];
    11111122}
    11121123
     
    20332044- (void)_didScroll
    20342045{
     2046    [self _updateTargetedPreviewViewsContainerViewFrameIfNeeded];
     2047
    20352048    [self _cancelLongPressGestureRecognizer];
    20362049    [self _cancelInteraction];
     
    75687581#endif // HAVE(PASTEBOARD_DATA_OWNER)
    75697582
     7583- (UIView *)textEffectsWindow
     7584{
     7585    return [UITextEffectsWindow sharedTextEffectsWindowForWindowScene:self.window.windowScene];
     7586}
     7587
    75707588- (RetainPtr<UIView>)_createPreviewContainerWithLayerName:(NSString *)layerName
    75717589{
     7590    if (!_targetedPreviewViewsContainerView) {
     7591        _targetedPreviewViewsContainerView = adoptNS([[UIView alloc] init]);
     7592        [_targetedPreviewViewsContainerView layer].name = @"Preview Views Container";
     7593        [_targetedPreviewViewsContainerView layer].anchorPoint = CGPointZero;
     7594        [self _updateTargetedPreviewViewsContainerViewFrameIfNeeded];
     7595        [self.textEffectsWindow addSubview:_targetedPreviewViewsContainerView.get()];
     7596    }
     7597
    75727598    auto container = adoptNS([[UIView alloc] init]);
    75737599    [container layer].anchorPoint = CGPointZero;
    75747600    [container layer].name = layerName;
    7575     [_interactionViewsContainerView addSubview:container.get()];
     7601    [_targetedPreviewViewsContainerView addSubview:container.get()];
    75767602    return container;
    75777603}
     
    75817607    if (!_dropPreviewContainerView)
    75827608        _dropPreviewContainerView = [self _createPreviewContainerWithLayerName:@"Drop Preview Container"];
     7609
     7610    [_targetedPreviewViewsContainerView setHidden:NO];
    75837611
    75847612    ASSERT([_dropPreviewContainerView superview]);
     
    75927620        _dragPreviewContainerView = [self _createPreviewContainerWithLayerName:@"Drag Preview Container"];
    75937621
     7622    [_targetedPreviewViewsContainerView setHidden:NO];
     7623
    75947624    ASSERT([_dragPreviewContainerView superview]);
    75957625    [_dragPreviewContainerView setHidden:NO];
     
    76027632        _contextMenuHintContainerView = [self _createPreviewContainerWithLayerName:@"Context Menu Hint Preview Container"];
    76037633
     7634    [_targetedPreviewViewsContainerView setHidden:NO];
     7635
    76047636    ASSERT([_contextMenuHintContainerView superview]);
    76057637    [_contextMenuHintContainerView setHidden:NO];
     
    76097641- (void)_hideTargetedPreviewContainerViews
    76107642{
     7643    [_targetedPreviewViewsContainerView setHidden:YES];
    76117644    [_dropPreviewContainerView setHidden:YES];
    76127645    [_dragPreviewContainerView setHidden:YES];
    76137646    [_contextMenuHintContainerView setHidden:YES];
     7647}
     7648
     7649- (void)_updateTargetedPreviewViewsContainerViewFrameIfNeeded
     7650{
     7651    if (!_targetedPreviewViewsContainerView || [_targetedPreviewViewsContainerView isHidden])
     7652        return;
     7653
     7654    auto scrollView = _scrollViewForTargetedPreview.get() ?: retainPtr(self._scroller);
     7655
     7656    CGRect frame = [_targetedPreviewViewsContainerView frame];
     7657    frame.origin = [scrollView convertPoint:CGPointZero toView:[_targetedPreviewViewsContainerView superview]];
     7658    [_targetedPreviewViewsContainerView setFrame:frame];
     7659}
     7660
     7661- (void)_removeTargetedPreviewViewsContainerViewIfPossible
     7662{
     7663    if (!_targetedPreviewViewsContainerView || [_targetedPreviewViewsContainerView subviews].count)
     7664        return;
     7665
     7666    [std::exchange(_targetedPreviewViewsContainerView, nil) removeFromSuperview];
     7667    _scrollViewForTargetedPreview = nil;
     7668}
     7669
     7670- (void)_updateTargetedPreviewScrollViewUsingContainerScrollingNodeID:(WebCore::ScrollingNodeID)scrollingNodeID
     7671{
     7672    if (scrollingNodeID) {
     7673        if (auto* scrollingCoordinator = _page->scrollingCoordinatorProxy()) {
     7674            if (UIScrollView *scrollViewForScrollingNode = scrollingCoordinator->scrollViewForScrollingNodeID(scrollingNodeID)) {
     7675                _scrollViewForTargetedPreview = scrollViewForScrollingNode;
     7676                return;
     7677            }
     7678        }
     7679    }
     7680
     7681    _scrollViewForTargetedPreview = self.webView.scrollView;
    76147682}
    76157683
     
    79338001
    79348002    [std::exchange(_dragPreviewContainerView, nil) removeFromSuperview];
     8003    [self _removeTargetedPreviewViewsContainerViewIfPossible];
    79358004    [std::exchange(_visibleContentViewSnapshot, nil) removeFromSuperview];
    79368005    [_editDropCaretView remove];
     
    81868255}
    81878256
    8188 - (UIView *)textEffectsWindow
    8189 {
    8190     return [UITextEffectsWindow sharedTextEffectsWindowForWindowScene:self.window.windowScene];
    8191 }
    8192 
    81938257- (NSDictionary *)_autofillContext
    81948258{
     
    82868350}
    82878351
    8288 - (void)overridePositionTrackingViewForTargetedPreviewIfNecessary:(UITargetedPreview *)targetedPreview containerScrollingNodeID:(WebCore::ScrollingNodeID)scrollingNodeID
    8289 {
    8290     if (!scrollingNodeID)
    8291         return;
    8292 
    8293     UIScrollView *positionTrackingView = self.webView.scrollView;
    8294     if (auto* scrollingCoordinator = _page->scrollingCoordinatorProxy())
    8295         positionTrackingView = scrollingCoordinator->scrollViewForScrollingNodeID(scrollingNodeID);
    8296 
    8297     if ([targetedPreview respondsToSelector:@selector(_setOverridePositionTrackingView:)])
    8298         [targetedPreview _setOverridePositionTrackingView:positionTrackingView];
    8299 }
    8300 
    83018352- (UITargetedPreview *)_createTargetedContextMenuHintPreviewForFocusedElement
    83028353{
    83038354    RetainPtr<UITargetedPreview> targetedPreview = createFallbackTargetedPreview(self, self.containerForContextMenuHintPreviews, _focusedElementInformation.interactionRect);
    83048355
    8305     [self overridePositionTrackingViewForTargetedPreviewIfNecessary:targetedPreview.get() containerScrollingNodeID:_focusedElementInformation.containerScrollingNodeID];
     8356    [self _updateTargetedPreviewScrollViewUsingContainerScrollingNodeID:_focusedElementInformation.containerScrollingNodeID];
    83068357
    83078358    _contextMenuInteractionTargetedPreview = WTFMove(targetedPreview);
     
    83268377        targetedPreview = createFallbackTargetedPreview(self, self.containerForContextMenuHintPreviews, _positionInformation.bounds);
    83278378
    8328     [self overridePositionTrackingViewForTargetedPreviewIfNecessary:targetedPreview.get() containerScrollingNodeID:_positionInformation.containerScrollingNodeID];
     8379    [self _updateTargetedPreviewScrollViewUsingContainerScrollingNodeID:_positionInformation.containerScrollingNodeID];
    83298380
    83308381    _contextMenuInteractionTargetedPreview = WTFMove(targetedPreview);
     
    83578408   
    83588409    [std::exchange(_contextMenuHintContainerView, nil) removeFromSuperview];
     8410    [self _removeTargetedPreviewViewsContainerViewIfPossible];
    83598411}
    83608412
     
    88548906{
    88558907    [std::exchange(_dropPreviewContainerView, nil) removeFromSuperview];
     8908    [self _removeTargetedPreviewViewsContainerViewIfPossible];
    88568909    [std::exchange(_visibleContentViewSnapshot, nil) removeFromSuperview];
    88578910    [std::exchange(_unselectedContentSnapshot, nil) removeFromSuperview];
Note: See TracChangeset for help on using the changeset viewer.