diff --git a/ios/web/web_state/ui/crw_context_menu_controller.mm b/ios/web/web_state/ui/crw_context_menu_controller.mm index 362c2226186ecc..dc5331026d81f2 100644 --- a/ios/web/web_state/ui/crw_context_menu_controller.mm +++ b/ios/web/web_state/ui/crw_context_menu_controller.mm @@ -7,6 +7,7 @@ #import #include +#include "base/ios/ios_util.h" #include "base/logging.h" #include "base/mac/foundation_util.h" #include "base/metrics/histogram_macros.h" @@ -54,6 +55,9 @@ @interface CRWContextMenuController () // Returns the x, y offset the content has been scrolled. @property(nonatomic, readonly) CGPoint scrollPosition; +// Returns a gesture recognizers with |fragment| in it's description. +- (UIGestureRecognizer*)gestureRecognizerWithDescriptionFragment: + (NSString*)fragment; // Called when the window has determined there was a long-press and context menu // must be shown. - (void)showContextMenu:(UIGestureRecognizer*)gestureRecognizer; @@ -107,10 +111,46 @@ - (instancetype)initWithWebView:(WKWebView*)webView [_contextMenuRecognizer setAllowableMovement:kLongPressMoveDeltaPixels]; [_contextMenuRecognizer setDelegate:self]; [_webView addGestureRecognizer:_contextMenuRecognizer]; + + if (base::ios::IsRunningOnIOS11OrLater()) { + // WKWebView's default context menu gesture recognizer interferes with + // the detection of a long press by |_contextMenuRecognizer|. WKWebView's + // context menu gesture recognizer should fail if |_contextMenuRecognizer| + // detects a long press. + NSString* fragment = @"action=_longPressRecognized:"; + UIGestureRecognizer* systemContextMenuRecognizer = + [self gestureRecognizerWithDescriptionFragment:fragment]; + if (systemContextMenuRecognizer) { + [systemContextMenuRecognizer + requireGestureRecognizerToFail:_contextMenuRecognizer]; + // requireGestureRecognizerToFail: doesn't retain the recognizer, so it + // is possible for |iRecognizer| to outlive |recognizer| and end up with + // a dangling pointer. Add a retaining associative reference to ensure + // that the lifetimes work out. + // Note that normally using the value as the key wouldn't make any + // sense, but here it's fine since nothing needs to look up the value. + void* associated_object_key = (__bridge void*)_contextMenuRecognizer; + objc_setAssociatedObject(systemContextMenuRecognizer.view, + associated_object_key, _contextMenuRecognizer, + OBJC_ASSOCIATION_RETAIN_NONATOMIC); + } + } } return self; } +- (UIGestureRecognizer*)gestureRecognizerWithDescriptionFragment: + (NSString*)fragment { + for (UIView* view in [[_webView scrollView] subviews]) { + for (UIGestureRecognizer* recognizer in [view gestureRecognizers]) { + if ([recognizer.description rangeOfString:fragment].length) { + return recognizer; + } + } + } + return nil; +} + - (UIScrollView*)webScrollView { return [_webView scrollView]; }