Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2017 The Chromium Authors. All rights reserved. | 1 // Copyright 2017 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #import "ios/web/web_state/ui/crw_context_menu_controller.h" | 5 #import "ios/web/web_state/ui/crw_context_menu_controller.h" |
| 6 | 6 |
| 7 #import <objc/runtime.h> | 7 #import <objc/runtime.h> |
| 8 #include <stddef.h> | 8 #include <stddef.h> |
| 9 | 9 |
| 10 #import "base/ios/weak_nsobject.h" | |
| 11 #include "base/logging.h" | 10 #include "base/logging.h" |
| 12 #include "base/mac/foundation_util.h" | 11 #include "base/mac/foundation_util.h" |
| 13 #include "base/metrics/histogram.h" | 12 #include "base/metrics/histogram.h" |
| 14 #include "base/strings/sys_string_conversions.h" | 13 #include "base/strings/sys_string_conversions.h" |
| 15 #include "components/url_formatter/url_formatter.h" | |
| 16 #include "ios/web/public/referrer_util.h" | |
| 17 #import "ios/web/public/web_state/context_menu_params.h" | 14 #import "ios/web/public/web_state/context_menu_params.h" |
| 18 #import "ios/web/public/web_state/js/crw_js_injection_evaluator.h" | 15 #import "ios/web/public/web_state/js/crw_js_injection_evaluator.h" |
| 19 #import "ios/web/public/web_state/ui/crw_context_menu_delegate.h" | 16 #import "ios/web/public/web_state/ui/crw_context_menu_delegate.h" |
| 20 | 17 |
| 18 #if !defined(__has_feature) || !__has_feature(objc_arc) | |
| 19 #error "This file requires ARC support." | |
| 20 #endif | |
| 21 | |
| 21 namespace { | 22 namespace { |
| 22 // The long press detection duration must be shorter than the WKWebView's | 23 // The long press detection duration must be shorter than the WKWebView's |
| 23 // long click gesture recognizer's minimum duration. That is 0.55s. | 24 // long click gesture recognizer's minimum duration. That is 0.55s. |
| 24 // If our detection duration is shorter, our gesture recognizer will fire | 25 // If our detection duration is shorter, our gesture recognizer will fire |
| 25 // first, and if it fails the long click gesture (processed simultaneously) | 26 // first, and if it fails the long click gesture (processed simultaneously) |
| 26 // still is able to complete. | 27 // still is able to complete. |
| 27 const NSTimeInterval kLongPressDurationSeconds = 0.55 - 0.1; | 28 const NSTimeInterval kLongPressDurationSeconds = 0.55 - 0.1; |
| 28 | 29 |
| 29 // If there is a movement bigger than |kLongPressMoveDeltaPixels|, the context | 30 // If there is a movement bigger than |kLongPressMoveDeltaPixels|, the context |
| 30 // menu will not be triggered. | 31 // menu will not be triggered. |
| 31 const CGFloat kLongPressMoveDeltaPixels = 10.0; | 32 const CGFloat kLongPressMoveDeltaPixels = 10.0; |
| 32 | 33 |
| 33 // Cancels touch events for the given gesture recognizer. | 34 // Cancels touch events for the given gesture recognizer. |
| 34 void CancelTouches(UIGestureRecognizer* gesture_recognizer) { | 35 void CancelTouches(UIGestureRecognizer* gesture_recognizer) { |
| 35 if (gesture_recognizer.enabled) { | 36 if (gesture_recognizer.enabled) { |
| 36 gesture_recognizer.enabled = NO; | 37 gesture_recognizer.enabled = NO; |
| 37 gesture_recognizer.enabled = YES; | 38 gesture_recognizer.enabled = YES; |
| 38 } | 39 } |
| 39 } | 40 } |
| 40 } // namespace | 41 } // namespace |
| 41 | 42 |
| 42 @interface CRWContextMenuController ()<UIGestureRecognizerDelegate> | 43 @interface CRWContextMenuController ()<UIGestureRecognizerDelegate> |
| 43 | 44 |
| 44 // Sets the specified recognizer to take priority over any recognizers in the | |
| 45 // view that have a description containing the specified text fragment. | |
| 46 + (void)requireGestureRecognizerToFail:(UIGestureRecognizer*)recognizer | |
| 47 inView:(UIView*)view | |
| 48 containingDescription:(NSString*)fragment; | |
| 49 | |
| 50 // The |webView|. | 45 // The |webView|. |
| 51 @property(nonatomic, readonly) WKWebView* webView; | 46 @property(nonatomic, readonly) WKWebView* webView; |
| 52 // The delegate that allow execute javascript. | 47 // The delegate that allow execute javascript. |
| 53 @property(nonatomic, readonly) id<CRWJSInjectionEvaluator> injectionEvaluator; | 48 @property(nonatomic, readonly) id<CRWJSInjectionEvaluator> injectionEvaluator; |
| 54 // The scroll view of |webView|. | 49 // The scroll view of |webView|. |
| 55 @property(nonatomic, readonly) id<CRWContextMenuDelegate> delegate; | 50 @property(nonatomic, readonly) id<CRWContextMenuDelegate> delegate; |
| 56 // Returns the x, y offset the content has been scrolled. | 51 // Returns the x, y offset the content has been scrolled. |
| 57 @property(nonatomic, readonly) CGPoint scrollPosition; | 52 @property(nonatomic, readonly) CGPoint scrollPosition; |
| 58 | 53 |
| 59 // Called when the window has determined there was a long-press and context menu | 54 // Called when the window has determined there was a long-press and context menu |
| 60 // must be shown. | 55 // must be shown. |
| 61 - (void)showContextMenu:(UIGestureRecognizer*)gestureRecognizer; | 56 - (void)showContextMenu:(UIGestureRecognizer*)gestureRecognizer; |
| 62 // Extracts context menu information from the given DOM element. | |
| 63 - (web::ContextMenuParams)contextMenuParamsForElement:(NSDictionary*)element; | |
| 64 // Cancels all touch events in the web view (long presses, tapping, scrolling). | 57 // Cancels all touch events in the web view (long presses, tapping, scrolling). |
| 65 - (void)cancelAllTouches; | 58 - (void)cancelAllTouches; |
| 66 // Asynchronously fetches full width of the rendered web page. | 59 // Asynchronously fetches full width of the rendered web page. |
| 67 - (void)fetchWebPageWidthWithCompletionHandler:(void (^)(CGFloat))handler; | 60 - (void)fetchWebPageWidthWithCompletionHandler:(void (^)(CGFloat))handler; |
| 68 // Asynchronously fetches information about DOM element for the given point (in | 61 // Asynchronously fetches information about DOM element for the given point (in |
| 69 // UIView coordinates). |handler| can not be nil. See |_DOMElementForLastTouch| | 62 // UIView coordinates). |handler| can not be nil. See |_DOMElementForLastTouch| |
| 70 // for element format description. | 63 // for element format description. |
| 71 - (void)fetchDOMElementAtPoint:(CGPoint)point | 64 - (void)fetchDOMElementAtPoint:(CGPoint)point |
| 72 completionHandler:(void (^)(NSDictionary*))handler; | 65 completionHandler:(void (^)(NSDictionary*))handler; |
| 73 // Sets the value of |_DOMElementForLastTouch|. | 66 // Sets the value of |_DOMElementForLastTouch|. |
| 74 - (void)setDOMElementForLastTouch:(NSDictionary*)element; | 67 - (void)setDOMElementForLastTouch:(NSDictionary*)element; |
| 75 // Forwards the execution of |script| to |javaScriptDelegate| and if it is nil, | 68 // Forwards the execution of |script| to |javaScriptDelegate| and if it is nil, |
| 76 // to |webView|. | 69 // to |webView|. |
| 77 - (void)executeJavaScript:(NSString*)script | 70 - (void)executeJavaScript:(NSString*)script |
| 78 completionHandler:(web::JavaScriptResultBlock)completionHandler; | 71 completionHandler:(web::JavaScriptResultBlock)completionHandler; |
| 79 @end | 72 @end |
| 80 | 73 |
| 81 @implementation CRWContextMenuController { | 74 @implementation CRWContextMenuController { |
| 82 base::WeakNSProtocol<id<CRWContextMenuDelegate>> _delegate; | 75 __weak id<CRWContextMenuDelegate> _delegate; |
|
stkhapugin
2017/01/17 15:13:12
Make the |delegate| property weak and @synthesize
Olivier
2017/01/17 18:02:14
Done.
| |
| 83 base::WeakNSProtocol<id<CRWJSInjectionEvaluator>> _injectionEvaluator; | 76 __weak id<CRWJSInjectionEvaluator> _injectionEvaluator; |
|
stkhapugin
2017/01/17 15:13:12
ditto
Olivier
2017/01/17 18:02:14
Done.
| |
| 84 base::WeakNSObject<WKWebView> _webView; | 77 __weak WKWebView* _webView; |
|
stkhapugin
2017/01/17 15:13:12
ditto
Olivier
2017/01/17 18:02:14
Done.
| |
| 85 | 78 |
| 86 // Long press recognizer that allows showing context menus. | 79 // Long press recognizer that allows showing context menus. |
| 87 base::scoped_nsobject<UILongPressGestureRecognizer> _contextMenuRecognizer; | 80 UILongPressGestureRecognizer* _contextMenuRecognizer; |
| 88 // DOM element information for the point where the user made the last touch. | 81 // DOM element information for the point where the user made the last touch. |
| 89 // Can be nil if has not been calculated yet. Precalculation is necessary | 82 // Can be nil if has not been calculated yet. Precalculation is necessary |
| 90 // because retreiving DOM element relies on async API so element info can not | 83 // because retreiving DOM element relies on async API so element info can not |
| 91 // be built on demand. May contain the following keys: @"href", @"src", | 84 // be built on demand. May contain the following keys: @"href", @"src", |
| 92 // @"title", @"referrerPolicy". All values are strings. | 85 // @"title", @"referrerPolicy". All values are strings. |
| 93 base::scoped_nsobject<NSDictionary> _DOMElementForLastTouch; | 86 NSDictionary* _DOMElementForLastTouch; |
| 94 } | 87 } |
| 95 | 88 |
| 89 @synthesize delegate = _delegate; | |
| 90 @synthesize injectionEvaluator = _injectionEvaluator; | |
| 91 @synthesize webView = _webView; | |
| 92 | |
| 96 - (instancetype)initWithWebView:(WKWebView*)webView | 93 - (instancetype)initWithWebView:(WKWebView*)webView |
| 97 injectionEvaluator:(id<CRWJSInjectionEvaluator>)injectionEvaluator | 94 injectionEvaluator:(id<CRWJSInjectionEvaluator>)injectionEvaluator |
| 98 delegate:(id<CRWContextMenuDelegate>)delegate { | 95 delegate:(id<CRWContextMenuDelegate>)delegate { |
| 99 DCHECK(webView); | 96 DCHECK(webView); |
| 100 self = [super init]; | 97 self = [super init]; |
| 101 if (self) { | 98 if (self) { |
| 102 _webView.reset(webView); | 99 _webView = webView; |
| 103 _delegate.reset(delegate); | 100 _delegate = delegate; |
| 104 _injectionEvaluator.reset(injectionEvaluator); | 101 _injectionEvaluator = injectionEvaluator; |
| 105 | 102 |
| 106 // The system context menu triggers after 0.55 second. Add a gesture | 103 // The system context menu triggers after 0.55 second. Add a gesture |
| 107 // recognizer with a shorter delay to be able to cancel the system menu if | 104 // recognizer with a shorter delay to be able to cancel the system menu if |
| 108 // needed. | 105 // needed. |
| 109 _contextMenuRecognizer.reset([[UILongPressGestureRecognizer alloc] | 106 _contextMenuRecognizer = [[UILongPressGestureRecognizer alloc] |
| 110 initWithTarget:self | 107 initWithTarget:self |
| 111 action:@selector(showContextMenu:)]); | 108 action:@selector(showContextMenu:)]; |
| 112 [_contextMenuRecognizer setMinimumPressDuration:kLongPressDurationSeconds]; | 109 [_contextMenuRecognizer setMinimumPressDuration:kLongPressDurationSeconds]; |
| 113 [_contextMenuRecognizer setAllowableMovement:kLongPressMoveDeltaPixels]; | 110 [_contextMenuRecognizer setAllowableMovement:kLongPressMoveDeltaPixels]; |
| 114 [_contextMenuRecognizer setDelegate:self]; | 111 [_contextMenuRecognizer setDelegate:self]; |
| 115 [_webView addGestureRecognizer:_contextMenuRecognizer]; | 112 [_webView addGestureRecognizer:_contextMenuRecognizer]; |
| 116 // Certain system gesture handlers are known to conflict with our context | |
| 117 // menu handler, causing extra events to fire when the context menu is | |
| 118 // active. | |
| 119 | |
| 120 // A number of solutions have been investigated. The lowest-risk solution | |
| 121 // appears to be to recurse through the web controller's recognizers, | |
| 122 // looking | |
| 123 // for fingerprints of the recognizers known to cause problems, which are | |
| 124 // then | |
| 125 // de-prioritized (below our own long click handler). | |
| 126 // Hunting for description fragments of system recognizers is undeniably | |
| 127 // brittle for future versions of iOS. If it does break the context menu | |
| 128 // events may leak (regressing b/5310177), but the app will otherwise work. | |
| 129 // TODO(crbug.com/680930): This code is not needed anymore in iOS9+ and has | |
| 130 // to be removed. | |
| 131 [CRWContextMenuController | |
| 132 requireGestureRecognizerToFail:_contextMenuRecognizer | |
| 133 inView:_webView | |
| 134 containingDescription: | |
| 135 @"action=_highlightLongPressRecognized:"]; | |
| 136 } | 113 } |
| 137 return self; | 114 return self; |
| 138 } | 115 } |
| 139 | 116 |
| 140 - (WKWebView*)webView { | |
| 141 return _webView.get(); | |
| 142 } | |
| 143 | |
| 144 - (id<CRWContextMenuDelegate>)delegate { | |
| 145 return _delegate.get(); | |
| 146 } | |
| 147 | |
| 148 - (id<CRWJSInjectionEvaluator>)injectionEvaluator { | |
| 149 return _injectionEvaluator.get(); | |
| 150 } | |
| 151 | |
| 152 - (UIScrollView*)webScrollView { | 117 - (UIScrollView*)webScrollView { |
| 153 return [_webView scrollView]; | 118 return [_webView scrollView]; |
| 154 } | 119 } |
| 155 | 120 |
| 156 - (CGPoint)scrollPosition { | 121 - (CGPoint)scrollPosition { |
| 157 return self.webScrollView.contentOffset; | 122 return self.webScrollView.contentOffset; |
| 158 } | 123 } |
| 159 | 124 |
| 160 - (void)executeJavaScript:(NSString*)script | 125 - (void)executeJavaScript:(NSString*)script |
| 161 completionHandler:(web::JavaScriptResultBlock)completionHandler { | 126 completionHandler:(web::JavaScriptResultBlock)completionHandler { |
| 162 if (self.injectionEvaluator) { | 127 if (self.injectionEvaluator) { |
| 163 [self.injectionEvaluator executeJavaScript:script | 128 [self.injectionEvaluator executeJavaScript:script |
| 164 completionHandler:completionHandler]; | 129 completionHandler:completionHandler]; |
| 165 } else { | 130 } else { |
| 166 [self.webView evaluateJavaScript:script | 131 [self.webView evaluateJavaScript:script |
| 167 completionHandler:completionHandler]; | 132 completionHandler:completionHandler]; |
| 168 } | 133 } |
| 169 } | 134 } |
| 170 | 135 |
| 171 - (void)showContextMenu:(UIGestureRecognizer*)gestureRecognizer { | 136 - (void)showContextMenu:(UIGestureRecognizer*)gestureRecognizer { |
| 172 // If the gesture has already been handled, ignore it. | 137 // If the gesture has already been handled, ignore it. |
| 173 if ([gestureRecognizer state] != UIGestureRecognizerStateBegan) | 138 if ([gestureRecognizer state] != UIGestureRecognizerStateBegan) |
| 174 return; | 139 return; |
| 175 | 140 |
| 176 if (![_DOMElementForLastTouch count]) | 141 if (![_DOMElementForLastTouch count]) |
| 177 return; | 142 return; |
| 178 | 143 |
| 179 web::ContextMenuParams params = | 144 web::ContextMenuParams params(_DOMElementForLastTouch); |
| 180 [self contextMenuParamsForElement:_DOMElementForLastTouch.get()]; | 145 params.view.reset(_webView); |
| 181 params.view.reset([_webView retain]); | |
| 182 params.location = [gestureRecognizer locationInView:_webView]; | 146 params.location = [gestureRecognizer locationInView:_webView]; |
| 183 if ([_delegate webView:_webView handleContextMenu:params]) { | 147 if ([_delegate webView:_webView handleContextMenu:params]) { |
| 184 // Cancelling all touches has the intended side effect of suppressing the | 148 // Cancelling all touches has the intended side effect of suppressing the |
| 185 // system's context menu. | 149 // system's context menu. |
| 186 [self cancelAllTouches]; | 150 [self cancelAllTouches]; |
| 187 } | 151 } |
| 188 } | 152 } |
| 189 | 153 |
| 190 + (void)requireGestureRecognizerToFail:(UIGestureRecognizer*)recognizer | |
| 191 inView:(UIView*)view | |
| 192 containingDescription:(NSString*)fragment { | |
| 193 for (UIGestureRecognizer* iRecognizer in [view gestureRecognizers]) { | |
| 194 if (iRecognizer != recognizer) { | |
| 195 NSString* description = [iRecognizer description]; | |
| 196 if ([description rangeOfString:fragment].length) { | |
| 197 [iRecognizer requireGestureRecognizerToFail:recognizer]; | |
| 198 // requireGestureRecognizerToFail: doesn't retain the recognizer, so it | |
| 199 // is possible for |iRecognizer| to outlive |recognizer| and end up with | |
| 200 // a dangling pointer. Add a retaining associative reference to ensure | |
| 201 // that the lifetimes work out. | |
| 202 // Note that normally using the value as the key wouldn't make any | |
| 203 // sense, but here it's fine since nothing needs to look up the value. | |
| 204 objc_setAssociatedObject(view, recognizer, recognizer, | |
| 205 OBJC_ASSOCIATION_RETAIN_NONATOMIC); | |
| 206 } | |
| 207 } | |
| 208 } | |
| 209 } | |
| 210 | |
| 211 - (web::ContextMenuParams)contextMenuParamsForElement:(NSDictionary*)element { | |
| 212 web::ContextMenuParams params; | |
| 213 NSString* title = nil; | |
| 214 NSString* href = element[@"href"]; | |
| 215 if (href) { | |
| 216 params.link_url = GURL(base::SysNSStringToUTF8(href)); | |
| 217 if (params.link_url.SchemeIs(url::kJavaScriptScheme)) { | |
| 218 title = @"JavaScript"; | |
| 219 } else { | |
| 220 base::string16 URLText = url_formatter::FormatUrl(params.link_url); | |
| 221 title = base::SysUTF16ToNSString(URLText); | |
| 222 } | |
| 223 } | |
| 224 NSString* src = element[@"src"]; | |
| 225 if (src) { | |
| 226 params.src_url = GURL(base::SysNSStringToUTF8(src)); | |
| 227 if (!title) | |
| 228 title = [[src copy] autorelease]; | |
| 229 if ([title hasPrefix:base::SysUTF8ToNSString(url::kDataScheme)]) | |
| 230 title = nil; | |
| 231 } | |
| 232 NSString* titleAttribute = element[@"title"]; | |
| 233 if (titleAttribute) | |
| 234 title = titleAttribute; | |
| 235 if (title) { | |
| 236 params.menu_title.reset([title copy]); | |
| 237 } | |
| 238 NSString* referrerPolicy = element[@"referrerPolicy"]; | |
| 239 if (referrerPolicy) { | |
| 240 params.referrer_policy = | |
| 241 web::ReferrerPolicyFromString(base::SysNSStringToUTF8(referrerPolicy)); | |
| 242 } | |
| 243 NSString* innerText = element[@"innerText"]; | |
| 244 if ([innerText length] > 0) { | |
| 245 params.link_text.reset([innerText copy]); | |
| 246 } | |
| 247 return params; | |
| 248 } | |
| 249 | |
| 250 - (void)cancelAllTouches { | 154 - (void)cancelAllTouches { |
| 251 // Disable web view scrolling. | 155 // Disable web view scrolling. |
| 252 CancelTouches(self.webView.scrollView.panGestureRecognizer); | 156 CancelTouches(self.webView.scrollView.panGestureRecognizer); |
| 253 | 157 |
| 254 // All user gestures are handled by a subview of web view scroll view | 158 // All user gestures are handled by a subview of web view scroll view |
| 255 // (WKContentView). | 159 // (WKContentView). |
| 256 for (UIView* subview in self.webScrollView.subviews) { | 160 for (UIView* subview in self.webScrollView.subviews) { |
| 257 for (UIGestureRecognizer* recognizer in subview.gestureRecognizers) { | 161 for (UIGestureRecognizer* recognizer in subview.gestureRecognizers) { |
| 258 CancelTouches(recognizer); | 162 CancelTouches(recognizer); |
| 259 } | 163 } |
| 260 } | 164 } |
| 261 | 165 |
| 262 // Just disabling/enabling the gesture recognizers is not enough to suppress | 166 // Just disabling/enabling the gesture recognizers is not enough to suppress |
| 263 // the click handlers on the JS side. This JS performs the function of | 167 // the click handlers on the JS side. This JS performs the function of |
| 264 // suppressing these handlers on the JS side. | 168 // suppressing these handlers on the JS side. |
| 265 NSString* suppressNextClick = @"__gCrWeb.suppressNextClick()"; | 169 NSString* suppressNextClick = @"__gCrWeb.suppressNextClick()"; |
| 266 [self executeJavaScript:suppressNextClick completionHandler:nil]; | 170 [self executeJavaScript:suppressNextClick completionHandler:nil]; |
| 267 } | 171 } |
| 268 | 172 |
| 269 - (void)setDOMElementForLastTouch:(NSDictionary*)element { | 173 - (void)setDOMElementForLastTouch:(NSDictionary*)element { |
| 270 _DOMElementForLastTouch.reset([element copy]); | 174 _DOMElementForLastTouch = [element copy]; |
| 271 } | 175 } |
| 272 | 176 |
| 273 #pragma mark - | 177 #pragma mark - |
| 274 #pragma mark UIGestureRecognizerDelegate | 178 #pragma mark UIGestureRecognizerDelegate |
| 275 | 179 |
| 276 - (BOOL)gestureRecognizer:(UIGestureRecognizer*)gestureRecognizer | 180 - (BOOL)gestureRecognizer:(UIGestureRecognizer*)gestureRecognizer |
| 277 shouldRecognizeSimultaneouslyWithGestureRecognizer: | 181 shouldRecognizeSimultaneouslyWithGestureRecognizer: |
| 278 (UIGestureRecognizer*)otherGestureRecognizer { | 182 (UIGestureRecognizer*)otherGestureRecognizer { |
| 279 // Allows the custom UILongPressGestureRecognizer to fire simultaneously with | 183 // Allows the custom UILongPressGestureRecognizer to fire simultaneously with |
| 280 // other recognizers. | 184 // other recognizers. |
| 281 return YES; | 185 return YES; |
| 282 } | 186 } |
| 283 | 187 |
| 284 - (BOOL)gestureRecognizer:(UIGestureRecognizer*)gestureRecognizer | 188 - (BOOL)gestureRecognizer:(UIGestureRecognizer*)gestureRecognizer |
| 285 shouldReceiveTouch:(UITouch*)touch { | 189 shouldReceiveTouch:(UITouch*)touch { |
| 286 // Expect only _contextMenuRecognizer. | 190 // Expect only _contextMenuRecognizer. |
| 287 DCHECK([gestureRecognizer isEqual:_contextMenuRecognizer]); | 191 DCHECK([gestureRecognizer isEqual:_contextMenuRecognizer]); |
| 288 | 192 |
| 289 // This is custom long press gesture recognizer. By the time the gesture is | 193 // This is custom long press gesture recognizer. By the time the gesture is |
| 290 // recognized the web controller needs to know if there is a link under the | 194 // recognized the web controller needs to know if there is a link under the |
| 291 // touch. If there a link, the web controller will reject system's context | 195 // touch. If there a link, the web controller will reject system's context |
| 292 // menu and show another one. If for some reason context menu info is not | 196 // menu and show another one. If for some reason context menu info is not |
| 293 // fetched - system context menu will be shown. | 197 // fetched - system context menu will be shown. |
| 294 [self setDOMElementForLastTouch:nil]; | 198 [self setDOMElementForLastTouch:nil]; |
| 295 base::WeakNSObject<CRWContextMenuController> weakSelf(self); | 199 __weak CRWContextMenuController* weakSelf = self; |
| 296 [self fetchDOMElementAtPoint:[touch locationInView:_webView] | 200 [self fetchDOMElementAtPoint:[touch locationInView:_webView] |
| 297 completionHandler:^(NSDictionary* element) { | 201 completionHandler:^(NSDictionary* element) { |
| 298 [weakSelf setDOMElementForLastTouch:element]; | 202 [weakSelf setDOMElementForLastTouch:element]; |
| 299 }]; | 203 }]; |
| 300 return YES; | 204 return YES; |
| 301 } | 205 } |
| 302 | 206 |
| 303 - (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer*)gestureRecognizer { | 207 - (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer*)gestureRecognizer { |
| 304 // Expect only _contextMenuRecognizer. | 208 // Expect only _contextMenuRecognizer. |
| 305 DCHECK([gestureRecognizer isEqual:_contextMenuRecognizer]); | 209 DCHECK([gestureRecognizer isEqual:_contextMenuRecognizer]); |
| (...skipping 15 matching lines...) Expand all Loading... | |
| 321 }]; | 225 }]; |
| 322 } | 226 } |
| 323 | 227 |
| 324 - (void)fetchDOMElementAtPoint:(CGPoint)point | 228 - (void)fetchDOMElementAtPoint:(CGPoint)point |
| 325 completionHandler:(void (^)(NSDictionary*))handler { | 229 completionHandler:(void (^)(NSDictionary*))handler { |
| 326 DCHECK(handler); | 230 DCHECK(handler); |
| 327 // Convert point into web page's coordinate system (which may be scaled and/or | 231 // Convert point into web page's coordinate system (which may be scaled and/or |
| 328 // scrolled). | 232 // scrolled). |
| 329 CGPoint scrollOffset = self.scrollPosition; | 233 CGPoint scrollOffset = self.scrollPosition; |
| 330 CGFloat webViewContentWidth = self.webScrollView.contentSize.width; | 234 CGFloat webViewContentWidth = self.webScrollView.contentSize.width; |
| 331 base::WeakNSObject<CRWContextMenuController> weakSelf(self); | 235 CRWContextMenuController* weakSelf = self; |
| 332 [self fetchWebPageWidthWithCompletionHandler:^(CGFloat pageWidth) { | 236 [self fetchWebPageWidthWithCompletionHandler:^(CGFloat pageWidth) { |
| 333 CGFloat scale = pageWidth / webViewContentWidth; | 237 CGFloat scale = pageWidth / webViewContentWidth; |
| 334 CGPoint localPoint = CGPointMake((point.x + scrollOffset.x) * scale, | 238 CGPoint localPoint = CGPointMake((point.x + scrollOffset.x) * scale, |
| 335 (point.y + scrollOffset.y) * scale); | 239 (point.y + scrollOffset.y) * scale); |
| 336 NSString* const kGetElementScript = | 240 NSString* const kGetElementScript = |
| 337 [NSString stringWithFormat:@"__gCrWeb.getElementFromPoint(%g, %g);", | 241 [NSString stringWithFormat:@"__gCrWeb.getElementFromPoint(%g, %g);", |
| 338 localPoint.x, localPoint.y]; | 242 localPoint.x, localPoint.y]; |
| 339 [self executeJavaScript:kGetElementScript | 243 [weakSelf executeJavaScript:kGetElementScript |
| 340 completionHandler:^(id element, NSError*) { | 244 completionHandler:^(id element, NSError*) { |
| 341 handler(base::mac::ObjCCastStrict<NSDictionary>(element)); | 245 handler(base::mac::ObjCCastStrict<NSDictionary>(element)); |
| 342 }]; | 246 }]; |
| 343 }]; | 247 }]; |
| 344 } | 248 } |
| 345 | 249 |
| 346 @end | 250 @end |
| OLD | NEW |