c3b0f017032c1c8b9a1893e72d3a21cb95a189ee
[WebKit-https.git] / Source / WebKit2 / UIProcess / ios / WKContentViewInteraction.mm
1 /*
2  * Copyright (C) 2012-2014 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #import "config.h"
27 #import "WKContentViewInteraction.h"
28
29 #if PLATFORM(IOS)
30
31 #import "APIUIClient.h"
32 #import "EditingRange.h"
33 #import "ManagedConfigurationSPI.h"
34 #import "NativeWebKeyboardEvent.h"
35 #import "NativeWebTouchEvent.h"
36 #import "SmartMagnificationController.h"
37 #import "TextInputSPI.h"
38 #import "UIKitSPI.h"
39 #import "WKActionSheetAssistant.h"
40 #import "WKFormInputControl.h"
41 #import "WKFormSelectControl.h"
42 #import "WKImagePreviewViewController.h"
43 #import "WKInspectorNodeSearchGestureRecognizer.h"
44 #import "WKNSURLExtras.h"
45 #import "WKPreviewActionItemIdentifiers.h"
46 #import "WKPreviewActionItemInternal.h"
47 #import "WKPreviewElementInfoInternal.h"
48 #import "WKUIDelegatePrivate.h"
49 #import "WKWebViewConfiguration.h"
50 #import "WKWebViewInternal.h"
51 #import "WKWebViewPrivate.h"
52 #import "WebEvent.h"
53 #import "WebIOSEventFactory.h"
54 #import "WebPageMessages.h"
55 #import "WebProcessProxy.h"
56 #import "_WKActivatedElementInfoInternal.h"
57 #import "_WKElementAction.h"
58 #import "_WKFocusedElementInfo.h"
59 #import "_WKFormInputSession.h"
60 #import "_WKInputDelegate.h"
61 #import <CoreText/CTFont.h>
62 #import <CoreText/CTFontDescriptor.h>
63 #import <MobileCoreServices/UTCoreTypes.h>
64 #import <WebCore/Color.h>
65 #import <WebCore/CoreGraphicsSPI.h>
66 #import <WebCore/DataDetectorsCoreSPI.h>
67 #import <WebCore/DataDetectorsUISPI.h>
68 #import <WebCore/FloatQuad.h>
69 #import <WebCore/Pasteboard.h>
70 #import <WebCore/Path.h>
71 #import <WebCore/PathUtilities.h>
72 #import <WebCore/RuntimeApplicationChecks.h>
73 #import <WebCore/Scrollbar.h>
74 #import <WebCore/SoftLinking.h>
75 #import <WebCore/TextIndicator.h>
76 #import <WebCore/WebCoreNSURLExtras.h>
77 #import <WebCore/WebEvent.h>
78 #import <WebKit/WebSelectionRect.h> // FIXME: WK2 should not include WebKit headers!
79 #import <wtf/RetainPtr.h>
80
81 @interface UIEvent(UIEventInternal)
82 @property (nonatomic, assign) UIKeyboardInputFlags _inputFlags;
83 @end
84
85 @interface WKWebEvent : WebEvent
86 @property (nonatomic, retain) UIEvent *uiEvent;
87 @end
88
89 @implementation WKWebEvent
90
91 - (void)dealloc
92 {
93     [_uiEvent release];
94     [super dealloc];
95 }
96
97 @end
98
99 using namespace WebCore;
100 using namespace WebKit;
101
102 namespace WebKit {
103
104 WKSelectionDrawingInfo::WKSelectionDrawingInfo()
105     : type(SelectionType::None)
106 {
107 }
108
109 WKSelectionDrawingInfo::WKSelectionDrawingInfo(const EditorState& editorState)
110 {
111     if (editorState.selectionIsNone) {
112         type = SelectionType::None;
113         return;
114     }
115
116     if (editorState.isInPlugin) {
117         type = SelectionType::Plugin;
118         return;
119     }
120
121     type = SelectionType::Range;
122     auto& postLayoutData = editorState.postLayoutData();
123     caretRect = postLayoutData.caretRectAtEnd;
124     selectionRects = postLayoutData.selectionRects;
125 }
126
127 inline bool operator==(const WKSelectionDrawingInfo& a, const WKSelectionDrawingInfo& b)
128 {
129     if (a.type != b.type)
130         return false;
131
132     if (a.type == WKSelectionDrawingInfo::SelectionType::Range) {
133         if (a.caretRect != b.caretRect)
134             return false;
135
136         if (a.selectionRects.size() != b.selectionRects.size())
137             return false;
138
139         for (unsigned i = 0; i < a.selectionRects.size(); ++i) {
140             if (a.selectionRects[i].rect() != b.selectionRects[i].rect())
141                 return false;
142         }
143     }
144
145     return true;
146 }
147
148 inline bool operator!=(const WKSelectionDrawingInfo& a, const WKSelectionDrawingInfo& b)
149 {
150     return !(a == b);
151 }
152
153 } // namespace WebKit
154
155 static const float highlightDelay = 0.12;
156 static const float tapAndHoldDelay  = 0.75;
157 const CGFloat minimumTapHighlightRadius = 2.0;
158
159 @interface WKTextRange : UITextRange {
160     CGRect _startRect;
161     CGRect _endRect;
162     BOOL _isNone;
163     BOOL _isRange;
164     BOOL _isEditable;
165     NSArray *_selectionRects;
166     NSUInteger _selectedTextLength;
167 }
168 @property (nonatomic) CGRect startRect;
169 @property (nonatomic) CGRect endRect;
170 @property (nonatomic) BOOL isNone;
171 @property (nonatomic) BOOL isRange;
172 @property (nonatomic) BOOL isEditable;
173 @property (nonatomic) NSUInteger selectedTextLength;
174 @property (copy, nonatomic) NSArray *selectionRects;
175
176 + (WKTextRange *)textRangeWithState:(BOOL)isNone isRange:(BOOL)isRange isEditable:(BOOL)isEditable startRect:(CGRect)startRect endRect:(CGRect)endRect selectionRects:(NSArray *)selectionRects selectedTextLength:(NSUInteger)selectedTextLength;
177
178 @end
179
180 @interface WKTextPosition : UITextPosition {
181     CGRect _positionRect;
182 }
183
184 @property (nonatomic) CGRect positionRect;
185
186 + (WKTextPosition *)textPositionWithRect:(CGRect)positionRect;
187
188 @end
189
190 @interface WKTextSelectionRect : UITextSelectionRect
191
192 @property (nonatomic, retain) WebSelectionRect *webRect;
193
194 + (NSArray *)textSelectionRectsWithWebRects:(NSArray *)webRects;
195
196 @end
197
198 @interface WKAutocorrectionRects : UIWKAutocorrectionRects
199 + (WKAutocorrectionRects *)autocorrectionRectsWithRects:(CGRect)firstRect lastRect:(CGRect)lastRect;
200 @end
201
202 @interface WKAutocorrectionContext : UIWKAutocorrectionContext
203 + (WKAutocorrectionContext *)autocorrectionContextWithData:(NSString *)beforeText markedText:(NSString *)markedText selectedText:(NSString *)selectedText afterText:(NSString *)afterText selectedRangeInMarkedText:(NSRange)range;
204 @end
205
206 @interface UITextInteractionAssistant (UITextInteractionAssistant_Internal)
207 // FIXME: this needs to be moved from the internal header to the private.
208 - (id)initWithView:(UIResponder <UITextInput> *)view;
209 - (void)selectWord;
210 - (void)scheduleReanalysis;
211 @end
212
213 @interface UITextInteractionAssistant (StagingToRemove)
214 - (void)scheduleReplacementsForText:(NSString *)text;
215 - (void)showTextServiceFor:(NSString *)selectedTerm fromRect:(CGRect)presentationRect;
216 - (void)scheduleChineseTransliterationForText:(NSString *)text;
217 - (void)showShareSheetFor:(NSString *)selectedTerm fromRect:(CGRect)presentationRect;
218 - (void)lookup:(NSString *)textWithContext fromRect:(CGRect)presentationRect;
219 - (void)lookup:(NSString *)textWithContext withRange:(NSRange)range fromRect:(CGRect)presentationRect;
220 @end
221
222 @interface UIWKSelectionAssistant (StagingToRemove)
223 - (void)showTextServiceFor:(NSString *)selectedTerm fromRect:(CGRect)presentationRect;
224 - (void)lookup:(NSString *)textWithContext fromRect:(CGRect)presentationRect;
225 - (void)lookup:(NSString *)textWithContext withRange:(NSRange)range fromRect:(CGRect)presentationRect;
226 @end
227
228 @interface UIKeyboardImpl (StagingToRemove)
229 - (void)didHandleWebKeyEvent;
230 - (void)didHandleWebKeyEvent:(WebIOSEvent *)event;
231 - (void)deleteFromInputWithFlags:(NSUInteger)flags;
232 - (void)addInputString:(NSString *)string withFlags:(NSUInteger)flags withInputManagerHint:(NSString *)hint;
233 @end
234
235 @interface UIView (UIViewInternalHack)
236 + (BOOL)_addCompletion:(void(^)(BOOL))completion;
237 @end
238
239 @protocol UISelectionInteractionAssistant;
240 #if HAVE(LINK_PREVIEW)
241 @interface UIPreviewItemController (StagingToRemove)
242 @property (strong, nonatomic, readonly) UIGestureRecognizer *presentationSecondaryGestureRecognizer;
243 @end
244 #endif
245
246 @interface DDDetectionController (StagingToRemove)
247 - (DDResultRef)resultForURL:(NSURL *)url identifier:(NSString *)identifier selectedText:(NSString *)selectedText results:(NSArray *)results context:(NSDictionary *)context extendedContext:(NSDictionary **)extendedContext;
248 @end
249
250 @interface WKFocusedElementInfo : NSObject <_WKFocusedElementInfo>
251 - (instancetype)initWithAssistedNodeInformation:(const AssistedNodeInformation&)information isUserInitiated:(BOOL)isUserInitiated;
252 @end
253
254 @interface WKFormInputSession : NSObject <_WKFormInputSession>
255
256 - (instancetype)initWithContentView:(WKContentView *)view focusedElementInfo:(WKFocusedElementInfo *)elementInfo userObject:(NSObject <NSSecureCoding> *)userObject;
257 - (void)invalidate;
258
259 @end
260
261 @implementation WKFormInputSession {
262     WKContentView *_contentView;
263     RetainPtr<NSObject <NSSecureCoding>> _userObject;
264     RetainPtr<WKFocusedElementInfo> _focusedElementInfo;
265     RetainPtr<UIView> _customInputView;
266     RetainPtr<NSArray<UITextSuggestion *>> _suggestions;
267     BOOL _accessoryViewShouldNotShow;
268     BOOL _forceSecureTextEntry;
269 }
270
271 - (instancetype)initWithContentView:(WKContentView *)view focusedElementInfo:(WKFocusedElementInfo *)elementInfo userObject:(NSObject <NSSecureCoding> *)userObject
272 {
273     if (!(self = [super init]))
274         return nil;
275
276     _contentView = view;
277     _focusedElementInfo = elementInfo;
278     _userObject = userObject;
279
280     return self;
281 }
282
283 - (id <_WKFocusedElementInfo>)focusedElementInfo
284 {
285     return _focusedElementInfo.get();
286 }
287
288 - (NSObject <NSSecureCoding> *)userObject
289 {
290     return _userObject.get();
291 }
292
293 - (BOOL)isValid
294 {
295     return _contentView != nil;
296 }
297
298 - (NSString *)accessoryViewCustomButtonTitle
299 {
300     return [[[_contentView formAccessoryView] _autofill] title];
301 }
302
303 - (void)setAccessoryViewCustomButtonTitle:(NSString *)title
304 {
305     if (title.length)
306         [[_contentView formAccessoryView] showAutoFillButtonWithTitle:title];
307     else
308         [[_contentView formAccessoryView] hideAutoFillButton];
309     if (UICurrentUserInterfaceIdiomIsPad())
310         [_contentView reloadInputViews];
311 }
312
313 - (BOOL)accessoryViewShouldNotShow
314 {
315     return _accessoryViewShouldNotShow;
316 }
317
318 - (void)setAccessoryViewShouldNotShow:(BOOL)accessoryViewShouldNotShow
319 {
320     if (_accessoryViewShouldNotShow == accessoryViewShouldNotShow)
321         return;
322
323     _accessoryViewShouldNotShow = accessoryViewShouldNotShow;
324     [_contentView reloadInputViews];
325 }
326
327 - (BOOL)forceSecureTextEntry
328 {
329     return _forceSecureTextEntry;
330 }
331
332 - (void)setForceSecureTextEntry:(BOOL)forceSecureTextEntry
333 {
334     if (_forceSecureTextEntry == forceSecureTextEntry)
335         return;
336
337     _forceSecureTextEntry = forceSecureTextEntry;
338     [_contentView reloadInputViews];
339 }
340
341 - (UIView *)customInputView
342 {
343     return _customInputView.get();
344 }
345
346 - (void)setCustomInputView:(UIView *)customInputView
347 {
348     if (customInputView == _customInputView)
349         return;
350
351     _customInputView = customInputView;
352     [_contentView reloadInputViews];
353 }
354
355 - (NSArray<UITextSuggestion *> *)suggestions
356 {
357     return _suggestions.get();
358 }
359
360 - (void)setSuggestions:(NSArray<UITextSuggestion *> *)suggestions
361 {
362 #if __IPHONE_OS_VERSION_MIN_REQUIRED >= 100000
363     id <UITextInputSuggestionDelegate> suggestionDelegate = (id <UITextInputSuggestionDelegate>)_contentView.inputDelegate;
364     _suggestions = adoptNS([suggestions copy]);
365     [suggestionDelegate setSuggestions:suggestions];
366 #endif
367 }
368
369 - (void)invalidate
370 {
371 #if __IPHONE_OS_VERSION_MIN_REQUIRED >= 100000
372     id <UITextInputSuggestionDelegate> suggestionDelegate = (id <UITextInputSuggestionDelegate>)_contentView.inputDelegate;
373     [suggestionDelegate setSuggestions:nil];
374 #endif
375     _contentView = nil;
376 }
377
378 @end
379
380 @implementation WKFocusedElementInfo {
381     WKInputType _type;
382     RetainPtr<NSString> _value;
383     BOOL _isUserInitiated;
384 }
385
386 - (instancetype)initWithAssistedNodeInformation:(const AssistedNodeInformation&)information isUserInitiated:(BOOL)isUserInitiated
387 {
388     if (!(self = [super init]))
389         return nil;
390
391     switch (information.elementType) {
392     case WebKit::InputType::ContentEditable:
393         _type = WKInputTypeContentEditable;
394         break;
395     case WebKit::InputType::Text:
396         _type = WKInputTypeText;
397         break;
398     case WebKit::InputType::Password:
399         _type = WKInputTypePassword;
400         break;
401     case WebKit::InputType::TextArea:
402         _type = WKInputTypeTextArea;
403         break;
404     case WebKit::InputType::Search:
405         _type = WKInputTypeSearch;
406         break;
407     case WebKit::InputType::Email:
408         _type = WKInputTypeEmail;
409         break;
410     case WebKit::InputType::URL:
411         _type = WKInputTypeURL;
412         break;
413     case WebKit::InputType::Phone:
414         _type = WKInputTypePhone;
415         break;
416     case WebKit::InputType::Number:
417         _type = WKInputTypeNumber;
418         break;
419     case WebKit::InputType::NumberPad:
420         _type = WKInputTypeNumberPad;
421         break;
422     case WebKit::InputType::Date:
423         _type = WKInputTypeDate;
424         break;
425     case WebKit::InputType::DateTime:
426         _type = WKInputTypeDateTime;
427         break;
428     case WebKit::InputType::DateTimeLocal:
429         _type = WKInputTypeDateTimeLocal;
430         break;
431     case WebKit::InputType::Month:
432         _type = WKInputTypeMonth;
433         break;
434     case WebKit::InputType::Week:
435         _type = WKInputTypeWeek;
436         break;
437     case WebKit::InputType::Time:
438         _type = WKInputTypeTime;
439         break;
440     case WebKit::InputType::Select:
441         _type = WKInputTypeSelect;
442         break;
443     case WebKit::InputType::None:
444         _type = WKInputTypeNone;
445         break;
446     }
447     _value = information.value;
448     _isUserInitiated = isUserInitiated;
449     return self;
450 }
451
452 - (WKInputType)type
453 {
454     return _type;
455 }
456
457 - (NSString *)value
458 {
459     return _value.get();
460 }
461
462 - (BOOL)isUserInitiated
463 {
464     return _isUserInitiated;
465 }
466 @end
467
468 @interface WKContentView (WKInteractionPrivate)
469 - (void)accessibilitySpeakSelectionSetContent:(NSString *)string;
470 @end
471
472 @implementation WKContentView (WKInteraction)
473
474 static UIWebSelectionMode toUIWebSelectionMode(WKSelectionGranularity granularity)
475 {
476     switch (granularity) {
477     case WKSelectionGranularityDynamic:
478         return UIWebSelectionModeWeb;
479     case WKSelectionGranularityCharacter:
480         return UIWebSelectionModeTextOnly;
481     }
482
483     ASSERT_NOT_REACHED();
484     return UIWebSelectionModeWeb;
485 }
486
487 - (void)_createAndConfigureDoubleTapGestureRecognizer
488 {
489     _doubleTapGestureRecognizer = adoptNS([[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(_doubleTapRecognized:)]);
490     [_doubleTapGestureRecognizer setNumberOfTapsRequired:2];
491     [_doubleTapGestureRecognizer setDelegate:self];
492     [self addGestureRecognizer:_doubleTapGestureRecognizer.get()];
493     [_singleTapGestureRecognizer requireGestureRecognizerToFail:_doubleTapGestureRecognizer.get()];
494 }
495
496 - (void)setupInteraction
497 {
498     if (!_interactionViewsContainerView) {
499         _interactionViewsContainerView = adoptNS([[UIView alloc] init]);
500         [_interactionViewsContainerView setOpaque:NO];
501         [_interactionViewsContainerView layer].anchorPoint = CGPointZero;
502         [self.superview addSubview:_interactionViewsContainerView.get()];
503     }
504
505     [self.layer addObserver:self forKeyPath:@"transform" options:NSKeyValueObservingOptionInitial context:nil];
506
507 #if ENABLE(TOUCH_EVENTS)
508     _touchEventGestureRecognizer = adoptNS([[UIWebTouchEventsGestureRecognizer alloc] initWithTarget:self action:@selector(_webTouchEventsRecognized:) touchDelegate:self]);
509     [_touchEventGestureRecognizer setDelegate:self];
510     [self addGestureRecognizer:_touchEventGestureRecognizer.get()];
511 #endif
512
513     _singleTapGestureRecognizer = adoptNS([[WKSyntheticClickTapGestureRecognizer alloc] initWithTarget:self action:@selector(_singleTapCommited:)]);
514     [_singleTapGestureRecognizer setDelegate:self];
515     [_singleTapGestureRecognizer setGestureRecognizedTarget:self action:@selector(_singleTapRecognized:)];
516     [_singleTapGestureRecognizer setResetTarget:self action:@selector(_singleTapDidReset:)];
517     [self addGestureRecognizer:_singleTapGestureRecognizer.get()];
518
519     _nonBlockingDoubleTapGestureRecognizer = adoptNS([[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(_nonBlockingDoubleTapRecognized:)]);
520     [_nonBlockingDoubleTapGestureRecognizer setNumberOfTapsRequired:2];
521     [_nonBlockingDoubleTapGestureRecognizer setDelegate:self];
522     [_nonBlockingDoubleTapGestureRecognizer setEnabled:NO];
523     [self addGestureRecognizer:_nonBlockingDoubleTapGestureRecognizer.get()];
524
525     [self _createAndConfigureDoubleTapGestureRecognizer];
526
527     _twoFingerDoubleTapGestureRecognizer = adoptNS([[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(_twoFingerDoubleTapRecognized:)]);
528     [_twoFingerDoubleTapGestureRecognizer setNumberOfTapsRequired:2];
529     [_twoFingerDoubleTapGestureRecognizer setNumberOfTouchesRequired:2];
530     [_twoFingerDoubleTapGestureRecognizer setDelegate:self];
531     [self addGestureRecognizer:_twoFingerDoubleTapGestureRecognizer.get()];
532
533     _highlightLongPressGestureRecognizer = adoptNS([[_UIWebHighlightLongPressGestureRecognizer alloc] initWithTarget:self action:@selector(_highlightLongPressRecognized:)]);
534     [_highlightLongPressGestureRecognizer setDelay:highlightDelay];
535     [_highlightLongPressGestureRecognizer setDelegate:self];
536     [self addGestureRecognizer:_highlightLongPressGestureRecognizer.get()];
537
538     _longPressGestureRecognizer = adoptNS([[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(_longPressRecognized:)]);
539     [_longPressGestureRecognizer setDelay:tapAndHoldDelay];
540     [_longPressGestureRecognizer setDelegate:self];
541     [_longPressGestureRecognizer _setRequiresQuietImpulse:YES];
542     [self addGestureRecognizer:_longPressGestureRecognizer.get()];
543
544     _twoFingerSingleTapGestureRecognizer = adoptNS([[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(_twoFingerSingleTapGestureRecognized:)]);
545     [_twoFingerSingleTapGestureRecognizer setAllowableMovement:60];
546     [_twoFingerSingleTapGestureRecognizer _setAllowableSeparation:150];
547     [_twoFingerSingleTapGestureRecognizer setNumberOfTapsRequired:1];
548     [_twoFingerSingleTapGestureRecognizer setNumberOfTouchesRequired:2];
549     [_twoFingerSingleTapGestureRecognizer setDelaysTouchesEnded:NO];
550     [_twoFingerSingleTapGestureRecognizer setDelegate:self];
551     [self addGestureRecognizer:_twoFingerSingleTapGestureRecognizer.get()];
552
553 #if HAVE(LINK_PREVIEW)
554     [self _registerPreview];
555 #endif
556
557     [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_resetShowingTextStyle:) name:UIMenuControllerDidHideMenuNotification object:nil];
558     _showingTextStyleOptions = NO;
559
560     // FIXME: This should be called when we get notified that loading has completed.
561     [self useSelectionAssistantWithMode:toUIWebSelectionMode([_webView _selectionGranularity])];
562     
563     _actionSheetAssistant = adoptNS([[WKActionSheetAssistant alloc] initWithView:self]);
564     [_actionSheetAssistant setDelegate:self];
565     _smartMagnificationController = std::make_unique<SmartMagnificationController>(self);
566     _isExpectingFastSingleTapCommit = NO;
567     _potentialTapInProgress = NO;
568     _isDoubleTapPending = NO;
569     _showDebugTapHighlightsForFastClicking = [[NSUserDefaults standardUserDefaults] boolForKey:@"WebKitShowFastClickDebugTapHighlights"];
570 }
571
572 - (void)cleanupInteraction
573 {
574     _webSelectionAssistant = nil;
575     _textSelectionAssistant = nil;
576     _actionSheetAssistant = nil;
577     _smartMagnificationController = nil;
578     _didAccessoryTabInitiateFocus = NO;
579     _isExpectingFastSingleTapCommit = NO;
580     [_formInputSession invalidate];
581     _formInputSession = nil;
582     [_highlightView removeFromSuperview];
583
584     if (_interactionViewsContainerView) {
585         [self.layer removeObserver:self forKeyPath:@"transform"];
586         [_interactionViewsContainerView removeFromSuperview];
587         _interactionViewsContainerView = nil;
588     }
589
590     [_touchEventGestureRecognizer setDelegate:nil];
591     [self removeGestureRecognizer:_touchEventGestureRecognizer.get()];
592
593     [_singleTapGestureRecognizer setDelegate:nil];
594     [_singleTapGestureRecognizer setGestureRecognizedTarget:nil action:nil];
595     [_singleTapGestureRecognizer setResetTarget:nil action:nil];
596     [self removeGestureRecognizer:_singleTapGestureRecognizer.get()];
597
598     [_highlightLongPressGestureRecognizer setDelegate:nil];
599     [self removeGestureRecognizer:_highlightLongPressGestureRecognizer.get()];
600
601     [_longPressGestureRecognizer setDelegate:nil];
602     [self removeGestureRecognizer:_longPressGestureRecognizer.get()];
603
604     [_doubleTapGestureRecognizer setDelegate:nil];
605     [self removeGestureRecognizer:_doubleTapGestureRecognizer.get()];
606
607     [_nonBlockingDoubleTapGestureRecognizer setDelegate:nil];
608     [self removeGestureRecognizer:_nonBlockingDoubleTapGestureRecognizer.get()];
609
610     [_twoFingerDoubleTapGestureRecognizer setDelegate:nil];
611     [self removeGestureRecognizer:_twoFingerDoubleTapGestureRecognizer.get()];
612
613     [_twoFingerSingleTapGestureRecognizer setDelegate:nil];
614     [self removeGestureRecognizer:_twoFingerSingleTapGestureRecognizer.get()];
615
616     _inspectorNodeSearchEnabled = NO;
617     if (_inspectorNodeSearchGestureRecognizer) {
618         [_inspectorNodeSearchGestureRecognizer setDelegate:nil];
619         [self removeGestureRecognizer:_inspectorNodeSearchGestureRecognizer.get()];
620         _inspectorNodeSearchGestureRecognizer = nil;
621     }
622
623 #if HAVE(LINK_PREVIEW)
624     [self _unregisterPreview];
625 #endif
626
627     if (_fileUploadPanel) {
628         [_fileUploadPanel setDelegate:nil];
629         [_fileUploadPanel dismiss];
630         _fileUploadPanel = nil;
631     }
632 }
633
634 - (void)_removeDefaultGestureRecognizers
635 {
636     [self removeGestureRecognizer:_touchEventGestureRecognizer.get()];
637     [self removeGestureRecognizer:_singleTapGestureRecognizer.get()];
638     [self removeGestureRecognizer:_highlightLongPressGestureRecognizer.get()];
639     [self removeGestureRecognizer:_doubleTapGestureRecognizer.get()];
640     [self removeGestureRecognizer:_nonBlockingDoubleTapGestureRecognizer.get()];
641     [self removeGestureRecognizer:_twoFingerDoubleTapGestureRecognizer.get()];
642     [self removeGestureRecognizer:_twoFingerSingleTapGestureRecognizer.get()];
643 }
644
645 - (void)_addDefaultGestureRecognizers
646 {
647     [self addGestureRecognizer:_touchEventGestureRecognizer.get()];
648     [self addGestureRecognizer:_singleTapGestureRecognizer.get()];
649     [self addGestureRecognizer:_highlightLongPressGestureRecognizer.get()];
650     [self addGestureRecognizer:_doubleTapGestureRecognizer.get()];
651     [self addGestureRecognizer:_nonBlockingDoubleTapGestureRecognizer.get()];
652     [self addGestureRecognizer:_twoFingerDoubleTapGestureRecognizer.get()];
653     [self addGestureRecognizer:_twoFingerSingleTapGestureRecognizer.get()];
654 }
655
656 - (UIView*)unscaledView
657 {
658     return _interactionViewsContainerView.get();
659 }
660
661 - (CGFloat)inverseScale
662 {
663     return 1 / [[self layer] transform].m11;
664 }
665
666 - (UIScrollView *)_scroller
667 {
668     return [_webView scrollView];
669 }
670
671 - (CGRect)unobscuredContentRect
672 {
673     return _page->unobscuredContentRect();
674 }
675
676 - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
677 {
678     ASSERT([keyPath isEqualToString:@"transform"]);
679     ASSERT(object == self.layer);
680
681     if ([UIView _isInAnimationBlock] && _page->editorState().selectionIsNone) {
682         // If the utility views are not already visible, we don't want them to become visible during the animation since
683         // they could not start from a reasonable state.
684         // This is not perfect since views could also get updated during the animation, in practice this is rare and the end state
685         // remains correct.
686         [self _cancelInteraction];
687         [_interactionViewsContainerView setHidden:YES];
688         [UIView _addCompletion:^(BOOL){ [_interactionViewsContainerView setHidden:NO]; }];
689     }
690
691     _selectionNeedsUpdate = YES;
692     [self _updateChangedSelection:YES];
693     [self _updateTapHighlight];
694 }
695
696 - (void)_enableInspectorNodeSearch
697 {
698     _inspectorNodeSearchEnabled = YES;
699
700     [self _cancelInteraction];
701
702     [self _removeDefaultGestureRecognizers];
703     _inspectorNodeSearchGestureRecognizer = adoptNS([[WKInspectorNodeSearchGestureRecognizer alloc] initWithTarget:self action:@selector(_inspectorNodeSearchRecognized:)]);
704     [self addGestureRecognizer:_inspectorNodeSearchGestureRecognizer.get()];
705 }
706
707 - (void)_disableInspectorNodeSearch
708 {
709     _inspectorNodeSearchEnabled = NO;
710
711     [self _addDefaultGestureRecognizers];
712     [self removeGestureRecognizer:_inspectorNodeSearchGestureRecognizer.get()];
713     _inspectorNodeSearchGestureRecognizer = nil;
714 }
715
716 - (UIView *)hitTest:(CGPoint)point withEvent:(::UIEvent *)event
717 {
718     for (UIView *subView in [_interactionViewsContainerView.get() subviews]) {
719         UIView *hitView = [subView hitTest:[subView convertPoint:point fromView:self] withEvent:event];
720         if (hitView)
721             return hitView;
722     }
723     return [super hitTest:point withEvent:event];
724 }
725
726 - (const InteractionInformationAtPosition&)positionInformation
727 {
728     return _positionInformation;
729 }
730
731 - (void)setInputDelegate:(id <UITextInputDelegate>)inputDelegate
732 {
733     _inputDelegate = inputDelegate;
734 }
735
736 - (id <UITextInputDelegate>)inputDelegate
737 {
738     return _inputDelegate;
739 }
740
741 - (CGPoint)lastInteractionLocation
742 {
743     return _lastInteractionLocation;
744 }
745
746 - (BOOL)isEditable
747 {
748     return _isEditable;
749 }
750
751 - (BOOL)canBecomeFirstResponder
752 {
753     if (_resigningFirstResponder)
754         return NO;
755     // We might want to return something else
756     // if we decide to enable/disable interaction programmatically.
757     return YES;
758 }
759
760 - (BOOL)becomeFirstResponder
761 {
762     if (_resigningFirstResponder)
763         return NO;
764     BOOL didBecomeFirstResponder = [super becomeFirstResponder];
765     if (didBecomeFirstResponder)
766         [_textSelectionAssistant activateSelection];
767
768     return didBecomeFirstResponder;
769 }
770
771 - (BOOL)resignFirstResponder
772 {
773     // FIXME: Maybe we should call resignFirstResponder on the superclass
774     // and do nothing if the return value is NO.
775
776     _resigningFirstResponder = YES;
777
778     if (!_webView->_activeFocusedStateRetainCount) {
779         // We need to complete the editing operation before we blur the element.
780         [_inputPeripheral endEditing];
781         _page->blurAssistedNode();
782     }
783
784     [self _cancelInteraction];
785     [_webSelectionAssistant resignedFirstResponder];
786     [_textSelectionAssistant deactivateSelection];
787
788     bool superDidResign = [super resignFirstResponder];
789
790     _resigningFirstResponder = NO;
791
792     return superDidResign;
793 }
794
795 #if ENABLE(TOUCH_EVENTS)
796 - (void)_webTouchEventsRecognized:(UIWebTouchEventsGestureRecognizer *)gestureRecognizer
797 {
798     const _UIWebTouchEvent* lastTouchEvent = gestureRecognizer.lastTouchEvent;
799     NativeWebTouchEvent nativeWebTouchEvent(lastTouchEvent);
800
801     _lastInteractionLocation = lastTouchEvent->locationInDocumentCoordinates;
802     nativeWebTouchEvent.setCanPreventNativeGestures(!_canSendTouchEventsAsynchronously || [gestureRecognizer isDefaultPrevented]);
803
804     if (_canSendTouchEventsAsynchronously)
805         _page->handleTouchEventAsynchronously(nativeWebTouchEvent);
806     else
807         _page->handleTouchEventSynchronously(nativeWebTouchEvent);
808
809     if (nativeWebTouchEvent.allTouchPointsAreReleased())
810         _canSendTouchEventsAsynchronously = NO;
811 }
812 #endif
813
814 - (void)_inspectorNodeSearchRecognized:(UIGestureRecognizer *)gestureRecognizer
815 {
816     ASSERT(_inspectorNodeSearchEnabled);
817     [self _resetIsDoubleTapPending];
818
819     CGPoint point = [gestureRecognizer locationInView:self];
820
821     switch (gestureRecognizer.state) {
822     case UIGestureRecognizerStateBegan:
823     case UIGestureRecognizerStateChanged:
824         _page->inspectorNodeSearchMovedToPosition(point);
825         break;
826     case UIGestureRecognizerStateEnded:
827     case UIGestureRecognizerStateCancelled:
828     default: // To ensure we turn off node search.
829         _page->inspectorNodeSearchEndedAtPosition(point);
830         break;
831     }
832 }
833
834 static FloatQuad inflateQuad(const FloatQuad& quad, float inflateSize)
835 {
836     // We sort the output points like this (as expected by the highlight view):
837     //  p2------p3
838     //  |       |
839     //  p1------p4
840
841     // 1) Sort the points horizontally.
842     FloatPoint points[4] = { quad.p1(), quad.p4(), quad.p2(), quad.p3() };
843     if (points[0].x() > points[1].x())
844         std::swap(points[0], points[1]);
845     if (points[2].x() > points[3].x())
846         std::swap(points[2], points[3]);
847
848     if (points[0].x() > points[2].x())
849         std::swap(points[0], points[2]);
850     if (points[1].x() > points[3].x())
851         std::swap(points[1], points[3]);
852
853     if (points[1].x() > points[2].x())
854         std::swap(points[1], points[2]);
855
856     // 2) Swap them vertically to have the output points [p2, p1, p3, p4].
857     if (points[1].y() < points[0].y())
858         std::swap(points[0], points[1]);
859     if (points[3].y() < points[2].y())
860         std::swap(points[2], points[3]);
861
862     // 3) Adjust the positions.
863     points[0].move(-inflateSize, -inflateSize);
864     points[1].move(-inflateSize, inflateSize);
865     points[2].move(inflateSize, -inflateSize);
866     points[3].move(inflateSize, inflateSize);
867
868     return FloatQuad(points[1], points[0], points[2], points[3]);
869 }
870
871 #if ENABLE(TOUCH_EVENTS)
872 - (void)_webTouchEvent:(const WebKit::NativeWebTouchEvent&)touchEvent preventsNativeGestures:(BOOL)preventsNativeGesture
873 {
874     if (preventsNativeGesture) {
875         _highlightLongPressCanClick = NO;
876
877         _canSendTouchEventsAsynchronously = YES;
878         [_touchEventGestureRecognizer setDefaultPrevented:YES];
879     }
880 }
881 #endif
882
883 static inline bool highlightedQuadsAreSmallerThanRect(const Vector<FloatQuad>& quads, const FloatRect& rect)
884 {
885     for (size_t i = 0; i < quads.size(); ++i) {
886         FloatRect boundingBox = quads[i].boundingBox();
887         if (boundingBox.width() > rect.width() || boundingBox.height() > rect.height())
888             return false;
889     }
890     return true;
891 }
892
893 static NSValue *nsSizeForTapHighlightBorderRadius(WebCore::IntSize borderRadius, CGFloat borderRadiusScale)
894 {
895     return [NSValue valueWithCGSize:CGSizeMake((borderRadius.width() * borderRadiusScale) + minimumTapHighlightRadius, (borderRadius.height() * borderRadiusScale) + minimumTapHighlightRadius)];
896 }
897
898 - (void)_updateTapHighlight
899 {
900     if (![_highlightView superview])
901         return;
902
903     {
904         RetainPtr<UIColor> highlightUIKitColor = adoptNS([[UIColor alloc] initWithCGColor:cachedCGColor(_tapHighlightInformation.color)]);
905         [_highlightView setColor:highlightUIKitColor.get()];
906     }
907
908     CGFloat selfScale = self.layer.transform.m11;
909     bool allHighlightRectsAreRectilinear = true;
910     float deviceScaleFactor = _page->deviceScaleFactor();
911     const Vector<WebCore::FloatQuad>& highlightedQuads = _tapHighlightInformation.quads;
912     const size_t quadCount = highlightedQuads.size();
913     RetainPtr<NSMutableArray> rects = adoptNS([[NSMutableArray alloc] initWithCapacity:static_cast<const NSUInteger>(quadCount)]);
914     for (size_t i = 0; i < quadCount; ++i) {
915         const FloatQuad& quad = highlightedQuads[i];
916         if (quad.isRectilinear()) {
917             FloatRect boundingBox = quad.boundingBox();
918             boundingBox.scale(selfScale);
919             boundingBox.inflate(minimumTapHighlightRadius);
920             CGRect pixelAlignedRect = static_cast<CGRect>(encloseRectToDevicePixels(boundingBox, deviceScaleFactor));
921             [rects addObject:[NSValue valueWithCGRect:pixelAlignedRect]];
922         } else {
923             allHighlightRectsAreRectilinear = false;
924             rects.clear();
925             break;
926         }
927     }
928
929     if (allHighlightRectsAreRectilinear)
930         [_highlightView setFrames:rects.get() boundaryRect:_page->exposedContentRect()];
931     else {
932         RetainPtr<NSMutableArray> quads = adoptNS([[NSMutableArray alloc] initWithCapacity:static_cast<const NSUInteger>(quadCount)]);
933         for (size_t i = 0; i < quadCount; ++i) {
934             FloatQuad quad = highlightedQuads[i];
935             quad.scale(selfScale, selfScale);
936             FloatQuad extendedQuad = inflateQuad(quad, minimumTapHighlightRadius);
937             [quads addObject:[NSValue valueWithCGPoint:extendedQuad.p1()]];
938             [quads addObject:[NSValue valueWithCGPoint:extendedQuad.p2()]];
939             [quads addObject:[NSValue valueWithCGPoint:extendedQuad.p3()]];
940             [quads addObject:[NSValue valueWithCGPoint:extendedQuad.p4()]];
941         }
942         [_highlightView setQuads:quads.get() boundaryRect:_page->exposedContentRect()];
943     }
944
945     RetainPtr<NSMutableArray> borderRadii = adoptNS([[NSMutableArray alloc] initWithCapacity:4]);
946     [borderRadii addObject:nsSizeForTapHighlightBorderRadius(_tapHighlightInformation.topLeftRadius, selfScale)];
947     [borderRadii addObject:nsSizeForTapHighlightBorderRadius(_tapHighlightInformation.topRightRadius, selfScale)];
948     [borderRadii addObject:nsSizeForTapHighlightBorderRadius(_tapHighlightInformation.bottomLeftRadius, selfScale)];
949     [borderRadii addObject:nsSizeForTapHighlightBorderRadius(_tapHighlightInformation.bottomRightRadius, selfScale)];
950     [_highlightView setCornerRadii:borderRadii.get()];
951 }
952
953 - (void)_showTapHighlight
954 {
955     if (!highlightedQuadsAreSmallerThanRect(_tapHighlightInformation.quads, _page->unobscuredContentRect()) && !_showDebugTapHighlightsForFastClicking)
956         return;
957
958     if (!_highlightView) {
959         _highlightView = adoptNS([[_UIHighlightView alloc] initWithFrame:CGRectZero]);
960         [_highlightView setUserInteractionEnabled:NO];
961         [_highlightView setOpaque:NO];
962         [_highlightView setCornerRadius:minimumTapHighlightRadius];
963     }
964     [_highlightView layer].opacity = 1;
965     [_interactionViewsContainerView addSubview:_highlightView.get()];
966     [self _updateTapHighlight];
967 }
968
969 - (void)_didGetTapHighlightForRequest:(uint64_t)requestID color:(const WebCore::Color&)color quads:(const Vector<WebCore::FloatQuad>&)highlightedQuads topLeftRadius:(const WebCore::IntSize&)topLeftRadius topRightRadius:(const WebCore::IntSize&)topRightRadius bottomLeftRadius:(const WebCore::IntSize&)bottomLeftRadius bottomRightRadius:(const WebCore::IntSize&)bottomRightRadius
970 {
971     if (!_isTapHighlightIDValid || _latestTapID != requestID)
972         return;
973
974     _isTapHighlightIDValid = NO;
975
976     _tapHighlightInformation.quads = highlightedQuads;
977     _tapHighlightInformation.topLeftRadius = topLeftRadius;
978     _tapHighlightInformation.topRightRadius = topRightRadius;
979     _tapHighlightInformation.bottomLeftRadius = bottomLeftRadius;
980     _tapHighlightInformation.bottomRightRadius = bottomRightRadius;
981     if (_showDebugTapHighlightsForFastClicking)
982         _tapHighlightInformation.color = [self _tapHighlightColorForFastClick:![_doubleTapGestureRecognizer isEnabled]];
983     else
984         _tapHighlightInformation.color = color;
985
986     if (_potentialTapInProgress) {
987         _hasTapHighlightForPotentialTap = YES;
988         return;
989     }
990
991     [self _showTapHighlight];
992     if (_isExpectingFastSingleTapCommit) {
993         [self _finishInteraction];
994         if (!_potentialTapInProgress)
995             _isExpectingFastSingleTapCommit = NO;
996     }
997 }
998
999 - (BOOL)_mayDisableDoubleTapGesturesDuringSingleTap
1000 {
1001     return _potentialTapInProgress;
1002 }
1003
1004 - (void)_disableDoubleTapGesturesDuringTapIfNecessary:(uint64_t)requestID
1005 {
1006     if (_latestTapID != requestID)
1007         return;
1008
1009     [self _setDoubleTapGesturesEnabled:NO];
1010 }
1011
1012 - (void)_cancelLongPressGestureRecognizer
1013 {
1014     [_highlightLongPressGestureRecognizer cancel];
1015 }
1016
1017 - (void)_didScroll
1018 {
1019     [self _cancelLongPressGestureRecognizer];
1020     [self _cancelInteraction];
1021 }
1022
1023 - (void)_overflowScrollingWillBegin
1024 {
1025     [_webSelectionAssistant willStartScrollingOverflow];
1026     [_textSelectionAssistant willStartScrollingOverflow];    
1027 }
1028
1029 - (void)_overflowScrollingDidEnd
1030 {
1031     // If scrolling ends before we've received a selection update,
1032     // we postpone showing the selection until the update is received.
1033     if (!_selectionNeedsUpdate) {
1034         _shouldRestoreSelection = YES;
1035         return;
1036     }
1037     [self _updateChangedSelection];
1038     [_webSelectionAssistant didEndScrollingOverflow];
1039     [_textSelectionAssistant didEndScrollingOverflow];
1040 }
1041
1042 - (BOOL)_requiresKeyboardWhenFirstResponder
1043 {
1044     // FIXME: We should add the logic to handle keyboard visibility during focus redirects.
1045     switch (_assistedNodeInformation.elementType) {
1046     case InputType::None:
1047         return NO;
1048     case InputType::Select:
1049         return !UICurrentUserInterfaceIdiomIsPad();
1050     case InputType::Date:
1051     case InputType::Month:
1052     case InputType::DateTimeLocal:
1053     case InputType::Time:
1054         return !UICurrentUserInterfaceIdiomIsPad();
1055     default:
1056         return !_assistedNodeInformation.isReadOnly;
1057     }
1058     return NO;
1059 }
1060
1061 - (BOOL)_requiresKeyboardResetOnReload
1062 {
1063     return YES;
1064 }
1065
1066 - (void)_displayFormNodeInputView
1067 {
1068     // In case user scaling is force enabled, do not use that scaling when zooming in with an input field.
1069     // Zooming above the page's default scale factor should only happen when the user performs it.
1070     [self _zoomToFocusRect:_assistedNodeInformation.elementRect
1071              selectionRect: _didAccessoryTabInitiateFocus ? IntRect() : _assistedNodeInformation.selectionRect
1072                   fontSize:_assistedNodeInformation.nodeFontSize
1073               minimumScale:_assistedNodeInformation.minimumScaleFactor
1074               maximumScale:_assistedNodeInformation.maximumScaleFactor
1075               allowScaling:(_assistedNodeInformation.allowsUserScalingIgnoringForceAlwaysScaling && !UICurrentUserInterfaceIdiomIsPad())
1076                forceScroll:[self requiresAccessoryView]];
1077
1078     _didAccessoryTabInitiateFocus = NO;
1079     [self _ensureFormAccessoryView];
1080     [self _updateAccessory];
1081 }
1082
1083 - (UIView *)inputView
1084 {
1085     if (_assistedNodeInformation.elementType == InputType::None)
1086         return nil;
1087
1088     if (!_inputPeripheral)
1089         _inputPeripheral = adoptNS(_assistedNodeInformation.elementType == InputType::Select ? [[WKFormSelectControl alloc] initWithView:self] : [[WKFormInputControl alloc] initWithView:self]);
1090     else
1091         [self _displayFormNodeInputView];
1092
1093     return [_formInputSession customInputView] ?: [_inputPeripheral assistantView];
1094 }
1095
1096 - (CGRect)_selectionClipRect
1097 {
1098     if (_assistedNodeInformation.elementType == InputType::None)
1099         return CGRectZero;
1100     return _page->editorState().postLayoutData().selectionClipRect;
1101 }
1102
1103 - (BOOL)gestureRecognizer:(UIGestureRecognizer *)preventingGestureRecognizer canPreventGestureRecognizer:(UIGestureRecognizer *)preventedGestureRecognizer
1104 {
1105     // A long-press gesture can not be recognized while panning, but a pan can be recognized
1106     // during a long-press gesture.
1107     BOOL shouldNotPreventScrollViewGestures = preventingGestureRecognizer == _highlightLongPressGestureRecognizer || preventingGestureRecognizer == _longPressGestureRecognizer;
1108     return !(shouldNotPreventScrollViewGestures
1109         && ([preventedGestureRecognizer isKindOfClass:NSClassFromString(@"UIScrollViewPanGestureRecognizer")] || [preventedGestureRecognizer isKindOfClass:NSClassFromString(@"UIScrollViewPinchGestureRecognizer")]));
1110 }
1111
1112 - (BOOL)gestureRecognizer:(UIGestureRecognizer *)preventedGestureRecognizer canBePreventedByGestureRecognizer:(UIGestureRecognizer *)preventingGestureRecognizer {
1113     // Don't allow the highlight to be prevented by a selection gesture. Press-and-hold on a link should highlight the link, not select it.
1114     if ((preventingGestureRecognizer == _textSelectionAssistant.get().loupeGesture || [_webSelectionAssistant isSelectionGestureRecognizer:preventingGestureRecognizer])
1115         && (preventedGestureRecognizer == _highlightLongPressGestureRecognizer || preventedGestureRecognizer == _longPressGestureRecognizer)) {
1116         return NO;
1117     }
1118
1119     return YES;
1120 }
1121
1122 static inline bool isSamePair(UIGestureRecognizer *a, UIGestureRecognizer *b, UIGestureRecognizer *x, UIGestureRecognizer *y)
1123 {
1124     return (a == x && b == y) || (b == x && a == y);
1125 }
1126
1127 - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer*)otherGestureRecognizer
1128 {
1129     if (isSamePair(gestureRecognizer, otherGestureRecognizer, _highlightLongPressGestureRecognizer.get(), _longPressGestureRecognizer.get()))
1130         return YES;
1131
1132     if (isSamePair(gestureRecognizer, otherGestureRecognizer, _highlightLongPressGestureRecognizer.get(), _webSelectionAssistant.get().selectionLongPressRecognizer))
1133         return YES;
1134
1135     if (isSamePair(gestureRecognizer, otherGestureRecognizer, _singleTapGestureRecognizer.get(), _textSelectionAssistant.get().singleTapGesture))
1136         return YES;
1137
1138     if (isSamePair(gestureRecognizer, otherGestureRecognizer, _singleTapGestureRecognizer.get(), _nonBlockingDoubleTapGestureRecognizer.get()))
1139         return YES;
1140
1141     if (isSamePair(gestureRecognizer, otherGestureRecognizer, _highlightLongPressGestureRecognizer.get(), _nonBlockingDoubleTapGestureRecognizer.get()))
1142         return YES;
1143
1144     if (isSamePair(gestureRecognizer, otherGestureRecognizer, _highlightLongPressGestureRecognizer.get(), _previewSecondaryGestureRecognizer.get()))
1145         return YES;
1146
1147     if (isSamePair(gestureRecognizer, otherGestureRecognizer, _highlightLongPressGestureRecognizer.get(), _previewGestureRecognizer.get()))
1148         return YES;
1149
1150     return NO;
1151 }
1152
1153 - (void)_showImageSheet
1154 {
1155     [_actionSheetAssistant showImageSheet];
1156 }
1157
1158 - (void)_showAttachmentSheet
1159 {
1160     id <WKUIDelegatePrivate> uiDelegate = static_cast<id <WKUIDelegatePrivate>>([_webView UIDelegate]);
1161     if (![uiDelegate respondsToSelector:@selector(_webView:showCustomSheetForElement:)])
1162         return;
1163
1164     auto element = adoptNS([[_WKActivatedElementInfo alloc] _initWithType:_WKActivatedElementTypeAttachment URL:[NSURL _web_URLWithWTFString:_positionInformation.url] location:_positionInformation.point title:_positionInformation.title ID:_positionInformation.idAttribute rect:_positionInformation.bounds image:nil]);
1165     [uiDelegate _webView:_webView showCustomSheetForElement:element.get()];
1166 }
1167
1168 - (void)_showLinkSheet
1169 {
1170     [_actionSheetAssistant showLinkSheet];
1171 }
1172
1173 - (void)_showDataDetectorsSheet
1174 {
1175     [_actionSheetAssistant showDataDetectorsSheet];
1176 }
1177
1178 - (SEL)_actionForLongPress
1179 {
1180     if (!_positionInformation.touchCalloutEnabled)
1181         return nil;
1182
1183     if (_positionInformation.isImage)
1184         return @selector(_showImageSheet);
1185
1186     if (_positionInformation.isLink) {
1187         NSURL *targetURL = [NSURL URLWithString:_positionInformation.url];
1188         if ([[getDDDetectionControllerClass() tapAndHoldSchemes] containsObject:[targetURL scheme]])
1189             return @selector(_showDataDetectorsSheet);
1190         return @selector(_showLinkSheet);
1191     }
1192     
1193     if (_positionInformation.isAttachment)
1194         return @selector(_showAttachmentSheet);
1195
1196     return nil;
1197 }
1198
1199 - (void)ensurePositionInformationIsUpToDate:(CGPoint)point
1200 {
1201     if (!_hasValidPositionInformation || roundedIntPoint(point) != _positionInformation.point) {
1202         _page->getPositionInformation(roundedIntPoint(point), _positionInformation);
1203         _hasValidPositionInformation = YES;
1204     }
1205 }
1206
1207 #if ENABLE(DATA_DETECTION)
1208 - (NSArray *)_dataDetectionResults
1209 {
1210     return _page->dataDetectionResults();
1211 }
1212 #endif
1213
1214 - (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
1215 {
1216     CGPoint point = [gestureRecognizer locationInView:self];
1217
1218     if (gestureRecognizer == _highlightLongPressGestureRecognizer
1219         || gestureRecognizer == _doubleTapGestureRecognizer
1220         || gestureRecognizer == _nonBlockingDoubleTapGestureRecognizer
1221         || gestureRecognizer == _twoFingerDoubleTapGestureRecognizer
1222         || gestureRecognizer == _singleTapGestureRecognizer) {
1223
1224         if (_textSelectionAssistant) {
1225             // Request information about the position with sync message.
1226             // If the assisted node is the same, prevent the gesture.
1227             _page->getPositionInformation(roundedIntPoint(point), _positionInformation);
1228             _hasValidPositionInformation = YES;
1229             if (_positionInformation.nodeAtPositionIsAssistedNode)
1230                 return NO;
1231         }
1232     }
1233
1234     if (gestureRecognizer == _highlightLongPressGestureRecognizer) {
1235         if (_textSelectionAssistant) {
1236             // This is a different node than the assisted one.
1237             // Prevent the gesture if there is no node.
1238             // Allow the gesture if it is a node that wants highlight or if there is an action for it.
1239             if (!_positionInformation.isElement)
1240                 return NO;
1241             return [self _actionForLongPress] != nil;
1242         } else {
1243             // We still have no idea about what is at the location.
1244             // Send and async message to find out.
1245             _hasValidPositionInformation = NO;
1246             _page->requestPositionInformation(roundedIntPoint(point));
1247             return YES;
1248         }
1249     }
1250
1251     if (gestureRecognizer == _longPressGestureRecognizer) {
1252         // Use the information retrieved with one of the previous calls
1253         // to gestureRecognizerShouldBegin.
1254         // Force a sync call if not ready yet.
1255         [self ensurePositionInformationIsUpToDate:point];
1256
1257         if (_textSelectionAssistant) {
1258             // Prevent the gesture if it is the same node.
1259             if (_positionInformation.nodeAtPositionIsAssistedNode)
1260                 return NO;
1261         } else {
1262             // Prevent the gesture if there is no action for the node.
1263             return [self _actionForLongPress] != nil;
1264         }
1265     }
1266
1267     return YES;
1268 }
1269
1270 - (void)_cancelInteraction
1271 {
1272     _isTapHighlightIDValid = NO;
1273     [_highlightView removeFromSuperview];
1274 }
1275
1276 - (void)_finishInteraction
1277 {
1278     _isTapHighlightIDValid = NO;
1279     CGFloat tapHighlightFadeDuration = _showDebugTapHighlightsForFastClicking ? 0.25 : 0.1;
1280     [UIView animateWithDuration:tapHighlightFadeDuration
1281                      animations:^{
1282                          [_highlightView layer].opacity = 0;
1283                      }
1284                      completion:^(BOOL finished){
1285                          if (finished)
1286                              [_highlightView removeFromSuperview];
1287                      }];
1288 }
1289
1290 - (BOOL)hasSelectablePositionAtPoint:(CGPoint)point
1291 {
1292     if (_inspectorNodeSearchEnabled)
1293         return NO;
1294
1295     [self ensurePositionInformationIsUpToDate:point];
1296     return _positionInformation.isSelectable;
1297 }
1298
1299 - (BOOL)pointIsNearMarkedText:(CGPoint)point
1300 {
1301     [self ensurePositionInformationIsUpToDate:point];
1302     return _positionInformation.isNearMarkedText;
1303 }
1304
1305 - (BOOL)pointIsInAssistedNode:(CGPoint)point
1306 {
1307     [self ensurePositionInformationIsUpToDate:point];
1308     return _positionInformation.nodeAtPositionIsAssistedNode;
1309 }
1310
1311 - (NSArray *)webSelectionRects
1312 {
1313     if (_page->editorState().selectionIsNone)
1314         return nil;
1315     const auto& selectionRects = _page->editorState().postLayoutData().selectionRects;
1316     unsigned size = selectionRects.size();
1317     if (!size)
1318         return nil;
1319
1320     NSMutableArray *webRects = [NSMutableArray arrayWithCapacity:size];
1321     for (unsigned i = 0; i < size; i++) {
1322         const WebCore::SelectionRect& coreRect = selectionRects[i];
1323         WebSelectionRect *webRect = [WebSelectionRect selectionRect];
1324         webRect.rect = coreRect.rect();
1325         webRect.writingDirection = coreRect.direction() == LTR ? WKWritingDirectionLeftToRight : WKWritingDirectionRightToLeft;
1326         webRect.isLineBreak = coreRect.isLineBreak();
1327         webRect.isFirstOnLine = coreRect.isFirstOnLine();
1328         webRect.isLastOnLine = coreRect.isLastOnLine();
1329         webRect.containsStart = coreRect.containsStart();
1330         webRect.containsEnd = coreRect.containsEnd();
1331         webRect.isInFixedPosition = coreRect.isInFixedPosition();
1332         webRect.isHorizontal = coreRect.isHorizontal();
1333         [webRects addObject:webRect];
1334     }
1335
1336     return webRects;
1337 }
1338
1339 - (void)_highlightLongPressRecognized:(UILongPressGestureRecognizer *)gestureRecognizer
1340 {
1341     ASSERT(gestureRecognizer == _highlightLongPressGestureRecognizer);
1342     [self _resetIsDoubleTapPending];
1343
1344     _lastInteractionLocation = gestureRecognizer.startPoint;
1345
1346     switch ([gestureRecognizer state]) {
1347     case UIGestureRecognizerStateBegan:
1348         _highlightLongPressCanClick = YES;
1349         cancelPotentialTapIfNecessary(self);
1350         _page->tapHighlightAtPosition([gestureRecognizer startPoint], ++_latestTapID);
1351         _isTapHighlightIDValid = YES;
1352         break;
1353     case UIGestureRecognizerStateEnded:
1354         if (_highlightLongPressCanClick && _positionInformation.isElement) {
1355             [self _attemptClickAtLocation:[gestureRecognizer startPoint]];
1356             [self _finishInteraction];
1357         } else
1358             [self _cancelInteraction];
1359         _highlightLongPressCanClick = NO;
1360         break;
1361     case UIGestureRecognizerStateCancelled:
1362         [self _cancelInteraction];
1363         _highlightLongPressCanClick = NO;
1364         break;
1365     default:
1366         break;
1367     }
1368 }
1369
1370 - (void)_twoFingerSingleTapGestureRecognized:(UITapGestureRecognizer *)gestureRecognizer
1371 {
1372     _isTapHighlightIDValid = YES;
1373     _isExpectingFastSingleTapCommit = YES;
1374     _page->handleTwoFingerTapAtPoint(roundedIntPoint(gestureRecognizer.centroid), ++_latestTapID);
1375 }
1376
1377 - (void)_longPressRecognized:(UILongPressGestureRecognizer *)gestureRecognizer
1378 {
1379     ASSERT(gestureRecognizer == _longPressGestureRecognizer);
1380     [self _resetIsDoubleTapPending];
1381
1382     _lastInteractionLocation = gestureRecognizer.startPoint;
1383
1384     if ([gestureRecognizer state] == UIGestureRecognizerStateBegan) {
1385         SEL action = [self _actionForLongPress];
1386         if (action) {
1387             [self performSelector:action];
1388             [self _cancelLongPressGestureRecognizer];
1389         }
1390     }
1391 }
1392
1393 - (void)_endPotentialTapAndEnableDoubleTapGesturesIfNecessary
1394 {
1395     if (_webView._allowsDoubleTapGestures)
1396         [self _setDoubleTapGesturesEnabled:YES];
1397
1398     _potentialTapInProgress = NO;
1399 }
1400
1401 - (void)_singleTapRecognized:(UITapGestureRecognizer *)gestureRecognizer
1402 {
1403     ASSERT(gestureRecognizer == _singleTapGestureRecognizer);
1404     ASSERT(!_potentialTapInProgress);
1405     [self _resetIsDoubleTapPending];
1406
1407     _page->potentialTapAtPosition(gestureRecognizer.location, ++_latestTapID);
1408     _potentialTapInProgress = YES;
1409     _isTapHighlightIDValid = YES;
1410     _isExpectingFastSingleTapCommit = !_doubleTapGestureRecognizer.get().enabled;
1411 }
1412
1413 static void cancelPotentialTapIfNecessary(WKContentView* contentView)
1414 {
1415     if (contentView->_potentialTapInProgress) {
1416         [contentView _endPotentialTapAndEnableDoubleTapGesturesIfNecessary];
1417         [contentView _cancelInteraction];
1418         contentView->_page->cancelPotentialTap();
1419     }
1420 }
1421
1422 - (void)_singleTapDidReset:(UITapGestureRecognizer *)gestureRecognizer
1423 {
1424     ASSERT(gestureRecognizer == _singleTapGestureRecognizer);
1425     cancelPotentialTapIfNecessary(self);
1426 }
1427
1428 - (void)_commitPotentialTapFailed
1429 {
1430     [self _cancelInteraction];
1431 }
1432
1433 - (void)_didNotHandleTapAsClick:(const WebCore::IntPoint&)point
1434 {
1435     // FIXME: we should also take into account whether or not the UI delegate
1436     // has handled this notification.
1437     if (_hasValidPositionInformation && point == _positionInformation.point && _positionInformation.isDataDetectorLink) {
1438         [self _showDataDetectorsSheet];
1439         return;
1440     }
1441
1442     if (!_isDoubleTapPending)
1443         return;
1444
1445     _smartMagnificationController->handleSmartMagnificationGesture(_lastInteractionLocation);
1446     _isDoubleTapPending = NO;
1447 }
1448
1449 - (void)_singleTapCommited:(UITapGestureRecognizer *)gestureRecognizer
1450 {
1451     ASSERT(gestureRecognizer == _singleTapGestureRecognizer);
1452
1453     if (![self isFirstResponder])
1454         [self becomeFirstResponder];
1455
1456     if (_webSelectionAssistant && ![_webSelectionAssistant shouldHandleSingleTapAtPoint:gestureRecognizer.location]) {
1457         [self _singleTapDidReset:gestureRecognizer];
1458         return;
1459     }
1460
1461     ASSERT(_potentialTapInProgress);
1462
1463     // We don't want to clear the selection if it is in editable content.
1464     // The selection could have been set by autofocusing on page load and not
1465     // reflected in the UI process since the user was not interacting with the page.
1466     if (!_page->editorState().isContentEditable)
1467         [_webSelectionAssistant clearSelection];
1468
1469     _lastInteractionLocation = gestureRecognizer.location;
1470
1471     [self _endPotentialTapAndEnableDoubleTapGesturesIfNecessary];
1472
1473     if (_hasTapHighlightForPotentialTap) {
1474         [self _showTapHighlight];
1475         _hasTapHighlightForPotentialTap = NO;
1476     }
1477
1478     [_inputPeripheral endEditing];
1479     _page->commitPotentialTap();
1480
1481     if (!_isExpectingFastSingleTapCommit)
1482         [self _finishInteraction];
1483 }
1484
1485 - (void)_doubleTapRecognized:(UITapGestureRecognizer *)gestureRecognizer
1486 {
1487     [self _resetIsDoubleTapPending];
1488     _lastInteractionLocation = gestureRecognizer.location;
1489
1490     _smartMagnificationController->handleSmartMagnificationGesture(gestureRecognizer.location);
1491 }
1492
1493 - (void)_resetIsDoubleTapPending
1494 {
1495     _isDoubleTapPending = NO;
1496 }
1497
1498 - (void)_nonBlockingDoubleTapRecognized:(UITapGestureRecognizer *)gestureRecognizer
1499 {
1500     _lastInteractionLocation = gestureRecognizer.location;
1501     _isDoubleTapPending = YES;
1502 }
1503
1504 - (void)_twoFingerDoubleTapRecognized:(UITapGestureRecognizer *)gestureRecognizer
1505 {
1506     [self _resetIsDoubleTapPending];
1507     _lastInteractionLocation = gestureRecognizer.location;
1508
1509     _smartMagnificationController->handleResetMagnificationGesture(gestureRecognizer.location);
1510 }
1511
1512 - (void)_attemptClickAtLocation:(CGPoint)location
1513 {
1514     if (![self isFirstResponder])
1515         [self becomeFirstResponder];
1516
1517     [_inputPeripheral endEditing];
1518     _page->handleTap(location);
1519 }
1520
1521 - (void)useSelectionAssistantWithMode:(UIWebSelectionMode)selectionMode
1522 {
1523     if (selectionMode == UIWebSelectionModeWeb) {
1524         if (_textSelectionAssistant) {
1525             [_textSelectionAssistant deactivateSelection];
1526             _textSelectionAssistant = nil;
1527         }
1528         if (!_webSelectionAssistant)
1529             _webSelectionAssistant = adoptNS([[UIWKSelectionAssistant alloc] initWithView:self]);
1530     } else if (selectionMode == UIWebSelectionModeTextOnly) {
1531         if (_webSelectionAssistant)
1532             _webSelectionAssistant = nil;
1533
1534         if (!_textSelectionAssistant)
1535             _textSelectionAssistant = adoptNS([[UIWKTextInteractionAssistant alloc] initWithView:self]);
1536         else {
1537             // Reset the gesture recognizers in case editibility has changed.
1538             [_textSelectionAssistant setGestureRecognizers];
1539         }
1540
1541         if (self.isFirstResponder)
1542             [_textSelectionAssistant activateSelection];
1543     }
1544 }
1545
1546 - (void)clearSelection
1547 {
1548     _page->clearSelection();
1549 }
1550
1551 - (void)_positionInformationDidChange:(const InteractionInformationAtPosition&)info
1552 {
1553     _positionInformation = info;
1554     _hasValidPositionInformation = YES;
1555     if (_actionSheetAssistant)
1556         [_actionSheetAssistant updateSheetPosition];
1557 }
1558
1559 - (void)_willStartScrollingOrZooming
1560 {
1561     [_webSelectionAssistant willStartScrollingOrZoomingPage];
1562     [_textSelectionAssistant willStartScrollingOverflow];
1563 }
1564
1565 - (void)scrollViewWillStartPanOrPinchGesture
1566 {
1567     _canSendTouchEventsAsynchronously = YES;
1568 }
1569
1570 - (void)_didEndScrollingOrZooming
1571 {
1572     [_webSelectionAssistant didEndScrollingOrZoomingPage];
1573     [_textSelectionAssistant didEndScrollingOverflow];
1574 }
1575
1576 - (BOOL)requiresAccessoryView
1577 {
1578     if ([_formInputSession accessoryViewShouldNotShow])
1579         return NO;
1580
1581     switch (_assistedNodeInformation.elementType) {
1582     case InputType::None:
1583         return NO;
1584     case InputType::Text:
1585     case InputType::Password:
1586     case InputType::Search:
1587     case InputType::Email:
1588     case InputType::URL:
1589     case InputType::Phone:
1590     case InputType::Number:
1591     case InputType::NumberPad:
1592     case InputType::ContentEditable:
1593     case InputType::TextArea:
1594     case InputType::Select:
1595     case InputType::Date:
1596     case InputType::DateTime:
1597     case InputType::DateTimeLocal:
1598     case InputType::Month:
1599     case InputType::Week:
1600     case InputType::Time:
1601         return !UICurrentUserInterfaceIdiomIsPad();
1602     }
1603 }
1604
1605 - (void)_ensureFormAccessoryView
1606 {
1607     if (_formAccessoryView)
1608         return;
1609
1610     _formAccessoryView = adoptNS([[UIWebFormAccessory alloc] initWithInputAssistantItem:self.inputAssistantItem]);
1611     [_formAccessoryView setDelegate:self];
1612 }
1613
1614 - (UIView *)inputAccessoryView
1615 {
1616     if (![self requiresAccessoryView])
1617         return nil;
1618
1619     return self.formAccessoryView;
1620 }
1621
1622 - (NSArray *)supportedPasteboardTypesForCurrentSelection
1623 {
1624     if (_page->editorState().selectionIsNone)
1625         return nil;
1626     
1627     static NSMutableArray *richTypes = nil;
1628     static NSMutableArray *plainTextTypes = nil;
1629     if (!plainTextTypes) {
1630         plainTextTypes = [[NSMutableArray alloc] init];
1631         [plainTextTypes addObject:(id)kUTTypeURL];
1632         [plainTextTypes addObjectsFromArray:UIPasteboardTypeListString];
1633
1634         richTypes = [[NSMutableArray alloc] init];
1635         [richTypes addObject:WebArchivePboardType];
1636         [richTypes addObjectsFromArray:UIPasteboardTypeListImage];
1637         [richTypes addObjectsFromArray:plainTextTypes];
1638     }
1639
1640     return (_page->editorState().isContentRichlyEditable) ? richTypes : plainTextTypes;
1641 }
1642
1643 - (void)_lookup:(id)sender
1644 {
1645     RetainPtr<WKContentView> view = self;
1646     _page->getSelectionContext([view](const String& selectedText, const String& textBefore, const String& textAfter, CallbackBase::Error error) {
1647         if (error != CallbackBase::Error::None)
1648             return;
1649         if (!selectedText)
1650             return;
1651         
1652         CGRect presentationRect = view->_page->editorState().selectionIsRange ? view->_page->editorState().postLayoutData().selectionRects[0].rect() : view->_page->editorState().postLayoutData().caretRectAtStart;
1653         
1654         String selectionContext = textBefore + selectedText + textAfter;
1655         if (view->_textSelectionAssistant) {
1656             if ([view->_textSelectionAssistant respondsToSelector:@selector(lookup:withRange:fromRect:)])
1657                 [view->_textSelectionAssistant lookup:selectionContext withRange:NSMakeRange(textBefore.length(), selectedText.length()) fromRect:presentationRect];
1658             else
1659                 [view->_textSelectionAssistant lookup:selectedText fromRect:presentationRect];
1660         } else {
1661             if ([view->_webSelectionAssistant respondsToSelector:@selector(lookup:withRange:fromRect:)])
1662                 [view->_webSelectionAssistant lookup:selectionContext withRange:NSMakeRange(textBefore.length(), selectedText.length()) fromRect:presentationRect];
1663             else
1664                 [view->_webSelectionAssistant lookup:selectedText fromRect:presentationRect];
1665         }
1666     });
1667 }
1668
1669 - (void)_share:(id)sender
1670 {
1671     RetainPtr<WKContentView> view = self;
1672     _page->getSelectionOrContentsAsString([view](const String& string, CallbackBase::Error error) {
1673         if (error != CallbackBase::Error::None)
1674             return;
1675         if (!string)
1676             return;
1677
1678         CGRect presentationRect = view->_page->editorState().postLayoutData().selectionRects[0].rect();
1679
1680         if (view->_textSelectionAssistant && [view->_textSelectionAssistant respondsToSelector:@selector(showShareSheetFor:fromRect:)])
1681             [view->_textSelectionAssistant showShareSheetFor:string fromRect:presentationRect];
1682         else if (view->_webSelectionAssistant && [view->_webSelectionAssistant respondsToSelector:@selector(showShareSheetFor:fromRect:)])
1683             [view->_webSelectionAssistant showShareSheetFor:string fromRect:presentationRect];
1684     });
1685 }
1686
1687 - (void)_addShortcut:(id)sender
1688 {
1689     if (_textSelectionAssistant && [_textSelectionAssistant respondsToSelector:@selector(showTextServiceFor:fromRect:)])
1690         [_textSelectionAssistant showTextServiceFor:[self selectedText] fromRect:_page->editorState().postLayoutData().selectionRects[0].rect()];
1691     else if (_webSelectionAssistant && [_webSelectionAssistant respondsToSelector:@selector(showTextServiceFor:fromRect:)])
1692         [_webSelectionAssistant showTextServiceFor:[self selectedText] fromRect:_page->editorState().postLayoutData().selectionRects[0].rect()];
1693 }
1694
1695 - (NSString *)selectedText
1696 {
1697     return (NSString *)_page->editorState().postLayoutData().wordAtSelection;
1698 }
1699
1700 - (BOOL)isReplaceAllowed
1701 {
1702     return _page->editorState().postLayoutData().isReplaceAllowed;
1703 }
1704
1705 - (void)replaceText:(NSString *)text withText:(NSString *)word
1706 {
1707     _page->replaceSelectedText(text, word);
1708 }
1709
1710 - (void)selectWordBackward
1711 {
1712     _page->selectWordBackward();
1713 }
1714
1715 - (void)_promptForReplace:(id)sender
1716 {
1717     const auto& wordAtSelection = _page->editorState().postLayoutData().wordAtSelection;
1718     if (wordAtSelection.isEmpty())
1719         return;
1720
1721     if ([_textSelectionAssistant respondsToSelector:@selector(scheduleReplacementsForText:)])
1722         [_textSelectionAssistant scheduleReplacementsForText:wordAtSelection];
1723 }
1724
1725 - (void)_transliterateChinese:(id)sender
1726 {
1727     if ([_textSelectionAssistant respondsToSelector:@selector(scheduleChineseTransliterationForText:)])
1728         [_textSelectionAssistant scheduleChineseTransliterationForText:_page->editorState().postLayoutData().wordAtSelection];
1729 }
1730
1731 - (void)_reanalyze:(id)sender
1732 {
1733     [_textSelectionAssistant scheduleReanalysis];
1734 }
1735
1736 - (void)replace:(id)sender
1737 {
1738     [[UIKeyboardImpl sharedInstance] replaceText:sender];
1739 }
1740
1741 - (NSDictionary *)textStylingAtPosition:(UITextPosition *)position inDirection:(UITextStorageDirection)direction
1742 {
1743     if (!position || !_page->editorState().isContentRichlyEditable)
1744         return nil;
1745
1746     NSMutableDictionary* result = [NSMutableDictionary dictionary];
1747
1748     auto typingAttributes = _page->editorState().postLayoutData().typingAttributes;
1749     CTFontSymbolicTraits symbolicTraits = 0;
1750     if (typingAttributes & AttributeBold)
1751         symbolicTraits |= kCTFontBoldTrait;
1752     if (typingAttributes & AttributeItalics)
1753         symbolicTraits |= kCTFontTraitItalic;
1754
1755     // We chose a random font family and size.
1756     // What matters are the traits but the caller expects a font object
1757     // in the dictionary for NSFontAttributeName.
1758     RetainPtr<CTFontDescriptorRef> fontDescriptor = adoptCF(CTFontDescriptorCreateWithNameAndSize(CFSTR("Helvetica"), 10));
1759     if (symbolicTraits)
1760         fontDescriptor = adoptCF(CTFontDescriptorCreateCopyWithSymbolicTraits(fontDescriptor.get(), symbolicTraits, symbolicTraits));
1761     
1762     RetainPtr<CTFontRef> font = adoptCF(CTFontCreateWithFontDescriptor(fontDescriptor.get(), 10, nullptr));
1763     if (font)
1764         [result setObject:(id)font.get() forKey:NSFontAttributeName];
1765     
1766     if (typingAttributes & AttributeUnderline)
1767         [result setObject:[NSNumber numberWithInt:NSUnderlineStyleSingle] forKey:NSUnderlineStyleAttributeName];
1768
1769     return result;
1770 }
1771
1772 - (BOOL)canPerformAction:(SEL)action withSender:(id)sender
1773 {
1774     BOOL hasWebSelection = _webSelectionAssistant && !CGRectIsEmpty(_webSelectionAssistant.get().selectionFrame);
1775
1776     if (action == @selector(_arrowKey:))
1777         return [self isFirstResponder];
1778         
1779     if (action == @selector(_showTextStyleOptions:))
1780         return _page->editorState().isContentRichlyEditable && _page->editorState().selectionIsRange && !_showingTextStyleOptions;
1781     if (_showingTextStyleOptions)
1782         return (action == @selector(toggleBoldface:) || action == @selector(toggleItalics:) || action == @selector(toggleUnderline:));
1783     if (action == @selector(toggleBoldface:) || action == @selector(toggleItalics:) || action == @selector(toggleUnderline:))
1784         return _page->editorState().isContentRichlyEditable;
1785     if (action == @selector(cut:))
1786         return !_page->editorState().isInPasswordField && _page->editorState().isContentEditable && _page->editorState().selectionIsRange;
1787     
1788     if (action == @selector(paste:)) {
1789         if (_page->editorState().selectionIsNone || !_page->editorState().isContentEditable)
1790             return NO;
1791         UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
1792         NSArray *types = [self supportedPasteboardTypesForCurrentSelection];
1793         NSIndexSet *indices = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, [pasteboard numberOfItems])];
1794         return [pasteboard containsPasteboardTypes:types inItemSet:indices];
1795     }
1796
1797     if (action == @selector(copy:)) {
1798         if (_page->editorState().isInPasswordField)
1799             return NO;
1800         return hasWebSelection || _page->editorState().selectionIsRange;
1801     }
1802
1803     if (action == @selector(_define:)) {
1804         if (_page->editorState().isInPasswordField || !(hasWebSelection || _page->editorState().selectionIsRange))
1805             return NO;
1806
1807         NSUInteger textLength = _page->editorState().postLayoutData().selectedTextLength;
1808         // FIXME: We should be calling UIReferenceLibraryViewController to check if the length is
1809         // acceptable, but the interface takes a string.
1810         // <rdar://problem/15254406>
1811         if (!textLength || textLength > 200)
1812             return NO;
1813
1814         if ([[getMCProfileConnectionClass() sharedConnection] effectiveBoolValueForSetting:MCFeatureDefinitionLookupAllowed] == MCRestrictedBoolExplicitNo)
1815             return NO;
1816             
1817         return YES;
1818     }
1819
1820     if (action == @selector(_lookup:)) {
1821         if (_page->editorState().isInPasswordField)
1822             return NO;
1823         
1824         if ([[getMCProfileConnectionClass() sharedConnection] effectiveBoolValueForSetting:MCFeatureDefinitionLookupAllowed] == MCRestrictedBoolExplicitNo)
1825             return NO;
1826         
1827         return YES;
1828     }
1829
1830     if (action == @selector(_share:)) {
1831         if (_page->editorState().isInPasswordField || !(hasWebSelection || _page->editorState().selectionIsRange))
1832             return NO;
1833
1834         NSUInteger textLength = _page->editorState().postLayoutData().selectedTextLength;
1835         // See FIXME above for _define.
1836         if (!textLength || textLength > 200)
1837             return NO;
1838         
1839         return YES;
1840     }
1841
1842     if (action == @selector(_addShortcut:)) {
1843         if (_page->editorState().isInPasswordField || !(hasWebSelection || _page->editorState().selectionIsRange))
1844             return NO;
1845
1846         NSString *selectedText = [self selectedText];
1847         if (![selectedText length])
1848             return NO;
1849
1850         if (!UIKeyboardEnabledInputModesAllowOneToManyShortcuts())
1851             return NO;
1852         if (![selectedText _containsCJScripts])
1853             return NO;
1854         return YES;
1855     }
1856
1857     if (action == @selector(_promptForReplace:)) {
1858         if (!_page->editorState().selectionIsRange || !_page->editorState().postLayoutData().isReplaceAllowed || ![[UIKeyboardImpl activeInstance] autocorrectSpellingEnabled])
1859             return NO;
1860         if ([[self selectedText] _containsCJScriptsOnly])
1861             return NO;
1862         return YES;
1863     }
1864
1865     if (action == @selector(_transliterateChinese:)) {
1866         if (!_page->editorState().selectionIsRange || !_page->editorState().postLayoutData().isReplaceAllowed || ![[UIKeyboardImpl activeInstance] autocorrectSpellingEnabled])
1867             return NO;
1868         return UIKeyboardEnabledInputModesAllowChineseTransliterationForText([self selectedText]);
1869     }
1870
1871     if (action == @selector(_reanalyze:)) {
1872         if (!_page->editorState().selectionIsRange || !_page->editorState().postLayoutData().isReplaceAllowed || ![[UIKeyboardImpl activeInstance] autocorrectSpellingEnabled])
1873             return NO;
1874         return UIKeyboardCurrentInputModeAllowsChineseOrJapaneseReanalysisForText([self selectedText]);
1875     }
1876
1877     if (action == @selector(select:)) {
1878         // Disable select in password fields so that you can't see word boundaries.
1879         return !_page->editorState().isInPasswordField && [self hasContent] && !_page->editorState().selectionIsNone && !_page->editorState().selectionIsRange;
1880     }
1881
1882     if (action == @selector(selectAll:)) {
1883         if (_page->editorState().selectionIsNone || ![self hasContent])
1884             return NO;
1885         if (!_page->editorState().selectionIsRange)
1886             return YES;
1887         // Enable selectAll for non-editable text, where the user can't access
1888         // this command via long-press to get a caret.
1889         if (_page->editorState().isContentEditable)
1890             return NO;
1891         // Don't attempt selectAll with general web content.
1892         if (hasWebSelection)
1893             return NO;
1894         // FIXME: Only enable if the selection doesn't already span the entire document.
1895         return YES;
1896     }
1897
1898     if (action == @selector(replace:))
1899         return _page->editorState().isContentEditable && !_page->editorState().isInPasswordField;
1900
1901     return [super canPerformAction:action withSender:sender];
1902 }
1903
1904 - (void)_resetShowingTextStyle:(NSNotification *)notification
1905 {
1906     _showingTextStyleOptions = NO;
1907     [_textSelectionAssistant hideTextStyleOptions];
1908 }
1909
1910 - (void)copy:(id)sender
1911 {
1912     _page->executeEditCommand(ASCIILiteral("copy"));
1913 }
1914
1915 - (void)cut:(id)sender
1916 {
1917     _page->executeEditCommand(ASCIILiteral("cut"));
1918 }
1919
1920 - (void)paste:(id)sender
1921 {
1922     _page->executeEditCommand(ASCIILiteral("paste"));
1923 }
1924
1925 - (void)select:(id)sender
1926 {
1927     [_textSelectionAssistant selectWord];
1928     // We cannot use selectWord command, because we want to be able to select the word even when it is the last in the paragraph.
1929     _page->extendSelection(WordGranularity);
1930 }
1931
1932 - (void)selectAll:(id)sender
1933 {
1934     [_textSelectionAssistant selectAll:sender];
1935     _page->executeEditCommand(ASCIILiteral("selectAll"));
1936 }
1937
1938 - (void)toggleBoldface:(id)sender
1939 {
1940     if (!_page->editorState().isContentRichlyEditable)
1941         return;
1942
1943     [self executeEditCommandWithCallback:@"toggleBold"];
1944 }
1945
1946 - (void)toggleItalics:(id)sender
1947 {
1948     if (!_page->editorState().isContentRichlyEditable)
1949         return;
1950
1951     [self executeEditCommandWithCallback:@"toggleItalic"];
1952 }
1953
1954 - (void)toggleUnderline:(id)sender
1955 {
1956     if (!_page->editorState().isContentRichlyEditable)
1957         return;
1958
1959     [self executeEditCommandWithCallback:@"toggleUnderline"];
1960 }
1961
1962 - (void)_showTextStyleOptions:(id)sender
1963 {
1964     _showingTextStyleOptions = YES;
1965     [_textSelectionAssistant showTextStyleOptions];
1966 }
1967
1968 - (void)_showDictionary:(NSString *)text
1969 {
1970     CGRect presentationRect = _page->editorState().postLayoutData().selectionRects[0].rect();
1971     if (_textSelectionAssistant)
1972         [_textSelectionAssistant showDictionaryFor:text fromRect:presentationRect];
1973     else
1974         [_webSelectionAssistant showDictionaryFor:text fromRect:presentationRect];
1975 }
1976
1977 - (void)_define:(id)sender
1978 {
1979     if ([[getMCProfileConnectionClass() sharedConnection] effectiveBoolValueForSetting:MCFeatureDefinitionLookupAllowed] == MCRestrictedBoolExplicitNo)
1980         return;
1981
1982     RetainPtr<WKContentView> view = self;
1983     _page->getSelectionOrContentsAsString([view](const String& string, WebKit::CallbackBase::Error error) {
1984         if (error != WebKit::CallbackBase::Error::None)
1985             return;
1986         if (!string)
1987             return;
1988
1989         [view _showDictionary:string];
1990     });
1991 }
1992
1993 - (void)accessibilityRetrieveSpeakSelectionContent
1994 {
1995     RetainPtr<WKContentView> view = self;
1996     _page->getSelectionOrContentsAsString([view](const String& string, WebKit::CallbackBase::Error error) {
1997         if (error != WebKit::CallbackBase::Error::None)
1998             return;
1999         if ([view respondsToSelector:@selector(accessibilitySpeakSelectionSetContent:)])
2000             [view accessibilitySpeakSelectionSetContent:string];
2001     });
2002 }
2003
2004 // UIWKInteractionViewProtocol
2005
2006 static inline GestureType toGestureType(UIWKGestureType gestureType)
2007 {
2008     switch (gestureType) {
2009     case UIWKGestureLoupe:
2010         return GestureType::Loupe;
2011     case UIWKGestureOneFingerTap:
2012         return GestureType::OneFingerTap;
2013     case UIWKGestureTapAndAHalf:
2014         return GestureType::TapAndAHalf;
2015     case UIWKGestureDoubleTap:
2016         return GestureType::DoubleTap;
2017     case UIWKGestureTapAndHalf:
2018         return GestureType::TapAndHalf;
2019     case UIWKGestureDoubleTapInUneditable:
2020         return GestureType::DoubleTapInUneditable;
2021     case UIWKGestureOneFingerTapInUneditable:
2022         return GestureType::OneFingerTapInUneditable;
2023     case UIWKGestureOneFingerTapSelectsAll:
2024         return GestureType::OneFingerTapSelectsAll;
2025     case UIWKGestureOneFingerDoubleTap:
2026         return GestureType::OneFingerDoubleTap;
2027     case UIWKGestureOneFingerTripleTap:
2028         return GestureType::OneFingerTripleTap;
2029     case UIWKGestureTwoFingerSingleTap:
2030         return GestureType::TwoFingerSingleTap;
2031     case UIWKGestureTwoFingerRangedSelectGesture:
2032         return GestureType::TwoFingerRangedSelectGesture;
2033     case UIWKGestureTapOnLinkWithGesture:
2034         return GestureType::TapOnLinkWithGesture;
2035     case UIWKGestureMakeWebSelection:
2036         return GestureType::MakeWebSelection;
2037     case UIWKGesturePhraseBoundary:
2038         return GestureType::PhraseBoundary;
2039     }
2040     ASSERT_NOT_REACHED();
2041     return GestureType::Loupe;
2042 }
2043
2044 static inline UIWKGestureType toUIWKGestureType(GestureType gestureType)
2045 {
2046     switch (gestureType) {
2047     case GestureType::Loupe:
2048         return UIWKGestureLoupe;
2049     case GestureType::OneFingerTap:
2050         return UIWKGestureOneFingerTap;
2051     case GestureType::TapAndAHalf:
2052         return UIWKGestureTapAndAHalf;
2053     case GestureType::DoubleTap:
2054         return UIWKGestureDoubleTap;
2055     case GestureType::TapAndHalf:
2056         return UIWKGestureTapAndHalf;
2057     case GestureType::DoubleTapInUneditable:
2058         return UIWKGestureDoubleTapInUneditable;
2059     case GestureType::OneFingerTapInUneditable:
2060         return UIWKGestureOneFingerTapInUneditable;
2061     case GestureType::OneFingerTapSelectsAll:
2062         return UIWKGestureOneFingerTapSelectsAll;
2063     case GestureType::OneFingerDoubleTap:
2064         return UIWKGestureOneFingerDoubleTap;
2065     case GestureType::OneFingerTripleTap:
2066         return UIWKGestureOneFingerTripleTap;
2067     case GestureType::TwoFingerSingleTap:
2068         return UIWKGestureTwoFingerSingleTap;
2069     case GestureType::TwoFingerRangedSelectGesture:
2070         return UIWKGestureTwoFingerRangedSelectGesture;
2071     case GestureType::TapOnLinkWithGesture:
2072         return UIWKGestureTapOnLinkWithGesture;
2073     case GestureType::MakeWebSelection:
2074         return UIWKGestureMakeWebSelection;
2075     case GestureType::PhraseBoundary:
2076         return UIWKGesturePhraseBoundary;
2077     }
2078 }
2079
2080 static inline SelectionTouch toSelectionTouch(UIWKSelectionTouch touch)
2081 {
2082     switch (touch) {
2083     case UIWKSelectionTouchStarted:
2084         return SelectionTouch::Started;
2085     case UIWKSelectionTouchMoved:
2086         return SelectionTouch::Moved;
2087     case UIWKSelectionTouchEnded:
2088         return SelectionTouch::Ended;
2089     case UIWKSelectionTouchEndedMovingForward:
2090         return SelectionTouch::EndedMovingForward;
2091     case UIWKSelectionTouchEndedMovingBackward:
2092         return SelectionTouch::EndedMovingBackward;
2093     case UIWKSelectionTouchEndedNotMoving:
2094         return SelectionTouch::EndedNotMoving;
2095     }
2096     ASSERT_NOT_REACHED();
2097     return SelectionTouch::Ended;
2098 }
2099
2100 static inline UIWKSelectionTouch toUIWKSelectionTouch(SelectionTouch touch)
2101 {
2102     switch (touch) {
2103     case SelectionTouch::Started:
2104         return UIWKSelectionTouchStarted;
2105     case SelectionTouch::Moved:
2106         return UIWKSelectionTouchMoved;
2107     case SelectionTouch::Ended:
2108         return UIWKSelectionTouchEnded;
2109     case SelectionTouch::EndedMovingForward:
2110         return UIWKSelectionTouchEndedMovingForward;
2111     case SelectionTouch::EndedMovingBackward:
2112         return UIWKSelectionTouchEndedMovingBackward;
2113     case SelectionTouch::EndedNotMoving:
2114         return UIWKSelectionTouchEndedNotMoving;
2115     }
2116 }
2117
2118 static inline GestureRecognizerState toGestureRecognizerState(UIGestureRecognizerState state)
2119 {
2120     switch (state) {
2121     case UIGestureRecognizerStatePossible:
2122         return GestureRecognizerState::Possible;
2123     case UIGestureRecognizerStateBegan:
2124         return GestureRecognizerState::Began;
2125     case UIGestureRecognizerStateChanged:
2126         return GestureRecognizerState::Changed;
2127     case UIGestureRecognizerStateCancelled:
2128         return GestureRecognizerState::Cancelled;
2129     case UIGestureRecognizerStateEnded:
2130         return GestureRecognizerState::Ended;
2131     case UIGestureRecognizerStateFailed:
2132         return GestureRecognizerState::Failed;
2133     }
2134 }
2135
2136 static inline UIGestureRecognizerState toUIGestureRecognizerState(GestureRecognizerState state)
2137 {
2138     switch (state) {
2139     case GestureRecognizerState::Possible:
2140         return UIGestureRecognizerStatePossible;
2141     case GestureRecognizerState::Began:
2142         return UIGestureRecognizerStateBegan;
2143     case GestureRecognizerState::Changed:
2144         return UIGestureRecognizerStateChanged;
2145     case GestureRecognizerState::Cancelled:
2146         return UIGestureRecognizerStateCancelled;
2147     case GestureRecognizerState::Ended:
2148         return UIGestureRecognizerStateEnded;
2149     case GestureRecognizerState::Failed:
2150         return UIGestureRecognizerStateFailed;
2151     }
2152 }
2153
2154 static inline UIWKSelectionFlags toUIWKSelectionFlags(SelectionFlags flags)
2155 {
2156     NSInteger uiFlags = UIWKNone;
2157     if (flags & WordIsNearTap)
2158         uiFlags |= UIWKWordIsNearTap;
2159     if (flags & IsBlockSelection)
2160         uiFlags |= UIWKIsBlockSelection;
2161     if (flags & PhraseBoundaryChanged)
2162         uiFlags |= UIWKPhraseBoundaryChanged;
2163
2164     return static_cast<UIWKSelectionFlags>(uiFlags);
2165 }
2166
2167 static inline SelectionHandlePosition toSelectionHandlePosition(UIWKHandlePosition position)
2168 {
2169     switch (position) {
2170     case UIWKHandleTop:
2171         return SelectionHandlePosition::Top;
2172     case UIWKHandleRight:
2173         return SelectionHandlePosition::Right;
2174     case UIWKHandleBottom:
2175         return SelectionHandlePosition::Bottom;
2176     case UIWKHandleLeft:
2177         return SelectionHandlePosition::Left;
2178     }
2179 }
2180
2181 static inline WebCore::TextGranularity toWKTextGranularity(UITextGranularity granularity)
2182 {
2183     switch (granularity) {
2184     case UITextGranularityCharacter:
2185         return CharacterGranularity;
2186     case UITextGranularityWord:
2187         return WordGranularity;
2188     case UITextGranularitySentence:
2189         return SentenceGranularity;
2190     case UITextGranularityParagraph:
2191         return ParagraphGranularity;
2192     case UITextGranularityLine:
2193         return LineGranularity;
2194     case UITextGranularityDocument:
2195         return DocumentGranularity;
2196     }
2197 }
2198
2199 static inline WebCore::SelectionDirection toWKSelectionDirection(UITextDirection direction)
2200 {
2201     switch (direction) {
2202     case UITextLayoutDirectionDown:
2203     case UITextLayoutDirectionRight:
2204         return DirectionRight;
2205     case UITextLayoutDirectionUp:
2206     case UITextLayoutDirectionLeft:
2207         return DirectionLeft;
2208     default:
2209         // UITextDirection is not an enum, but we only want to accept values from UITextLayoutDirection.
2210         ASSERT_NOT_REACHED();
2211         return DirectionRight;
2212     }
2213 }
2214
2215 static void selectionChangedWithGesture(WKContentView *view, const WebCore::IntPoint& point, uint32_t gestureType, uint32_t gestureState, uint32_t flags, WebKit::CallbackBase::Error error)
2216 {
2217     if (error != WebKit::CallbackBase::Error::None) {
2218         ASSERT_NOT_REACHED();
2219         return;
2220     }
2221     if ([view webSelectionAssistant])
2222         [(UIWKSelectionAssistant *)[view webSelectionAssistant] selectionChangedWithGestureAt:(CGPoint)point withGesture:toUIWKGestureType((GestureType)gestureType) withState:toUIGestureRecognizerState(static_cast<GestureRecognizerState>(gestureState)) withFlags:(toUIWKSelectionFlags((SelectionFlags)flags))];
2223     else
2224         [(UIWKTextInteractionAssistant *)[view interactionAssistant] selectionChangedWithGestureAt:(CGPoint)point withGesture:toUIWKGestureType((GestureType)gestureType) withState:toUIGestureRecognizerState(static_cast<GestureRecognizerState>(gestureState)) withFlags:(toUIWKSelectionFlags((SelectionFlags)flags))];
2225 }
2226
2227 static void selectionChangedWithTouch(WKContentView *view, const WebCore::IntPoint& point, uint32_t touch, uint32_t flags, WebKit::CallbackBase::Error error)
2228 {
2229     if (error != WebKit::CallbackBase::Error::None) {
2230         ASSERT_NOT_REACHED();
2231         return;
2232     }
2233     if ([view webSelectionAssistant])
2234         [(UIWKSelectionAssistant *)[view webSelectionAssistant] selectionChangedWithTouchAt:(CGPoint)point withSelectionTouch:toUIWKSelectionTouch((SelectionTouch)touch) withFlags:static_cast<UIWKSelectionFlags>(flags)];
2235     else
2236         [(UIWKTextInteractionAssistant *)[view interactionAssistant] selectionChangedWithTouchAt:(CGPoint)point withSelectionTouch:toUIWKSelectionTouch((SelectionTouch)touch)];
2237 }
2238
2239 - (void)_didUpdateBlockSelectionWithTouch:(SelectionTouch)touch withFlags:(SelectionFlags)flags growThreshold:(CGFloat)growThreshold shrinkThreshold:(CGFloat)shrinkThreshold
2240 {
2241     [_webSelectionAssistant blockSelectionChangedWithTouch:toUIWKSelectionTouch(touch) withFlags:toUIWKSelectionFlags(flags) growThreshold:growThreshold shrinkThreshold:shrinkThreshold];
2242     if (touch != SelectionTouch::Started && touch != SelectionTouch::Moved)
2243         _usingGestureForSelection = NO;
2244 }
2245
2246 - (BOOL)_isInteractingWithAssistedNode
2247 {
2248     return _textSelectionAssistant != nil;
2249 }
2250
2251 - (void)changeSelectionWithGestureAt:(CGPoint)point withGesture:(UIWKGestureType)gestureType withState:(UIGestureRecognizerState)state
2252 {
2253     _usingGestureForSelection = YES;
2254     _page->selectWithGesture(WebCore::IntPoint(point), CharacterGranularity, static_cast<uint32_t>(toGestureType(gestureType)), static_cast<uint32_t>(toGestureRecognizerState(state)), [self _isInteractingWithAssistedNode], [self, state](const WebCore::IntPoint& point, uint32_t gestureType, uint32_t gestureState, uint32_t flags, WebKit::CallbackBase::Error error) {
2255         selectionChangedWithGesture(self, point, gestureType, gestureState, flags, error);
2256         if (state == UIGestureRecognizerStateEnded || state == UIGestureRecognizerStateCancelled)
2257             _usingGestureForSelection = NO;
2258     });
2259 }
2260
2261 - (void)changeSelectionWithTouchAt:(CGPoint)point withSelectionTouch:(UIWKSelectionTouch)touch baseIsStart:(BOOL)baseIsStart
2262 {
2263     _usingGestureForSelection = YES;
2264     _page->updateSelectionWithTouches(WebCore::IntPoint(point), static_cast<uint32_t>(toSelectionTouch(touch)), baseIsStart, [self, touch](const WebCore::IntPoint& point, uint32_t touch, uint32_t flags, WebKit::CallbackBase::Error error) {
2265         selectionChangedWithTouch(self, point, touch, flags, error);
2266         if (touch != UIWKSelectionTouchStarted && touch != UIWKSelectionTouchMoved)
2267             _usingGestureForSelection = NO;
2268     });
2269 }
2270
2271 - (void)changeSelectionWithTouchesFrom:(CGPoint)from to:(CGPoint)to withGesture:(UIWKGestureType)gestureType withState:(UIGestureRecognizerState)gestureState
2272 {
2273     _usingGestureForSelection = YES;
2274     _page->selectWithTwoTouches(WebCore::IntPoint(from), WebCore::IntPoint(to), static_cast<uint32_t>(toGestureType(gestureType)), static_cast<uint32_t>(toGestureRecognizerState(gestureState)), [self, gestureState](const WebCore::IntPoint& point, uint32_t gestureType, uint32_t gestureState, uint32_t flags, WebKit::CallbackBase::Error error) {
2275         selectionChangedWithGesture(self, point, gestureType, gestureState, flags, error);
2276         if (gestureState == UIGestureRecognizerStateEnded || gestureState == UIGestureRecognizerStateCancelled)
2277             _usingGestureForSelection = NO;
2278     });
2279 }
2280
2281 - (void)changeBlockSelectionWithTouchAt:(CGPoint)point withSelectionTouch:(UIWKSelectionTouch)touch forHandle:(UIWKHandlePosition)handle
2282 {
2283     _usingGestureForSelection = YES;
2284     _page->updateBlockSelectionWithTouch(WebCore::IntPoint(point), static_cast<uint32_t>(toSelectionTouch(touch)), static_cast<uint32_t>(toSelectionHandlePosition(handle)));
2285 }
2286
2287 - (void)moveByOffset:(NSInteger)offset
2288 {
2289     if (!offset)
2290         return;
2291     
2292     [self beginSelectionChange];
2293     RetainPtr<WKContentView> view = self;
2294     _page->moveSelectionByOffset(offset, [view](WebKit::CallbackBase::Error) {
2295         [view endSelectionChange];
2296     });
2297 }
2298
2299 - (const WKAutoCorrectionData&)autocorrectionData
2300 {
2301     return _autocorrectionData;
2302 }
2303
2304 // The completion handler can pass nil if input does not match the actual text preceding the insertion point.
2305 - (void)requestAutocorrectionRectsForString:(NSString *)input withCompletionHandler:(void (^)(UIWKAutocorrectionRects *rectsForInput))completionHandler
2306 {
2307     if (!input || ![input length]) {
2308         completionHandler(nil);
2309         return;
2310     }
2311
2312     RetainPtr<WKContentView> view = self;
2313     _autocorrectionData.autocorrectionHandler = [completionHandler copy];
2314     _page->requestAutocorrectionData(input, [view](const Vector<FloatRect>& rects, const String& fontName, double fontSize, uint64_t traits, WebKit::CallbackBase::Error) {
2315         CGRect firstRect = CGRectZero;
2316         CGRect lastRect = CGRectZero;
2317         if (rects.size()) {
2318             firstRect = rects[0];
2319             lastRect = rects[rects.size() - 1];
2320         }
2321         
2322         view->_autocorrectionData.fontName = fontName;
2323         view->_autocorrectionData.fontSize = fontSize;
2324         view->_autocorrectionData.fontTraits = traits;
2325         view->_autocorrectionData.textFirstRect = firstRect;
2326         view->_autocorrectionData.textLastRect = lastRect;
2327
2328         view->_autocorrectionData.autocorrectionHandler(rects.size() ? [WKAutocorrectionRects autocorrectionRectsWithRects:firstRect lastRect:lastRect] : nil);
2329         [view->_autocorrectionData.autocorrectionHandler release];
2330         view->_autocorrectionData.autocorrectionHandler = nil;
2331     });
2332 }
2333
2334 - (void)selectPositionAtPoint:(CGPoint)point completionHandler:(void (^)(void))completionHandler
2335 {
2336     _usingGestureForSelection = YES;
2337     UIWKSelectionCompletionHandler selectionHandler = [completionHandler copy];
2338     RetainPtr<WKContentView> view = self;
2339     
2340     _page->selectPositionAtPoint(WebCore::IntPoint(point), [self _isInteractingWithAssistedNode], [view, selectionHandler](WebKit::CallbackBase::Error error) {
2341         selectionHandler();
2342         view->_usingGestureForSelection = NO;
2343         [selectionHandler release];
2344     });
2345 }
2346
2347 - (void)selectPositionAtBoundary:(UITextGranularity)granularity inDirection:(UITextDirection)direction fromPoint:(CGPoint)point completionHandler:(void (^)(void))completionHandler
2348 {
2349     _usingGestureForSelection = YES;
2350     UIWKSelectionCompletionHandler selectionHandler = [completionHandler copy];
2351     RetainPtr<WKContentView> view = self;
2352     
2353     _page->selectPositionAtBoundaryWithDirection(WebCore::IntPoint(point), toWKTextGranularity(granularity), toWKSelectionDirection(direction), [self _isInteractingWithAssistedNode], [view, selectionHandler](WebKit::CallbackBase::Error error) {
2354         selectionHandler();
2355         view->_usingGestureForSelection = NO;
2356         [selectionHandler release];
2357     });
2358 }
2359
2360 - (void)moveSelectionAtBoundary:(UITextGranularity)granularity inDirection:(UITextDirection)direction completionHandler:(void (^)(void))completionHandler
2361 {
2362     _usingGestureForSelection = YES;
2363     UIWKSelectionCompletionHandler selectionHandler = [completionHandler copy];
2364     RetainPtr<WKContentView> view = self;
2365     
2366     _page->moveSelectionAtBoundaryWithDirection(toWKTextGranularity(granularity), toWKSelectionDirection(direction), [view, selectionHandler](WebKit::CallbackBase::Error error) {
2367         selectionHandler();
2368         view->_usingGestureForSelection = NO;
2369         [selectionHandler release];
2370     });
2371 }
2372
2373 - (void)selectTextWithGranularity:(UITextGranularity)granularity atPoint:(CGPoint)point completionHandler:(void (^)(void))completionHandler
2374 {
2375     _usingGestureForSelection = YES;
2376     UIWKSelectionCompletionHandler selectionHandler = [completionHandler copy];
2377     RetainPtr<WKContentView> view = self;
2378
2379     _page->selectTextWithGranularityAtPoint(WebCore::IntPoint(point), toWKTextGranularity(granularity), [self _isInteractingWithAssistedNode], [view, selectionHandler](WebKit::CallbackBase::Error error) {
2380         selectionHandler();
2381         view->_usingGestureForSelection = NO;
2382         [selectionHandler release];
2383     });
2384 }
2385
2386 - (void)beginSelectionInDirection:(UITextDirection)direction completionHandler:(void (^)(BOOL endIsMoving))completionHandler
2387 {
2388     UIWKSelectionWithDirectionCompletionHandler selectionHandler = [completionHandler copy];
2389
2390     _page->beginSelectionInDirection(toWKSelectionDirection(direction), [selectionHandler](bool endIsMoving, WebKit::CallbackBase::Error error) {
2391         selectionHandler(endIsMoving);
2392         [selectionHandler release];
2393     });
2394 }
2395
2396 - (void)updateSelectionWithExtentPoint:(CGPoint)point completionHandler:(void (^)(BOOL endIsMoving))completionHandler
2397 {
2398     UIWKSelectionWithDirectionCompletionHandler selectionHandler = [completionHandler copy];
2399     
2400     _page->updateSelectionWithExtentPoint(WebCore::IntPoint(point), [self _isInteractingWithAssistedNode], [selectionHandler](bool endIsMoving, WebKit::CallbackBase::Error error) {
2401         selectionHandler(endIsMoving);
2402         [selectionHandler release];
2403     });
2404 }
2405
2406 - (void)updateSelectionWithExtentPoint:(CGPoint)point withBoundary:(UITextGranularity)granularity completionHandler:(void (^)(BOOL selectionEndIsMoving))completionHandler
2407 {
2408     UIWKSelectionWithDirectionCompletionHandler selectionHandler = [completionHandler copy];
2409     
2410     _page->updateSelectionWithExtentPointAndBoundary(WebCore::IntPoint(point), toWKTextGranularity(granularity), [self _isInteractingWithAssistedNode], [selectionHandler](bool endIsMoving, WebKit::CallbackBase::Error error) {
2411         selectionHandler(endIsMoving);
2412         [selectionHandler release];
2413     });
2414 }
2415
2416 - (UTF32Char)_characterBeforeCaretSelection
2417 {
2418     return _page->editorState().postLayoutData().characterBeforeSelection;
2419 }
2420
2421 - (UTF32Char)_characterInRelationToCaretSelection:(int)amount
2422 {
2423     switch (amount) {
2424     case 0:
2425         return _page->editorState().postLayoutData().characterAfterSelection;
2426     case -1:
2427         return _page->editorState().postLayoutData().characterBeforeSelection;
2428     case -2:
2429         return _page->editorState().postLayoutData().twoCharacterBeforeSelection;
2430     default:
2431         return 0;
2432     }
2433 }
2434
2435 - (BOOL)_selectionAtDocumentStart
2436 {
2437     return !_page->editorState().postLayoutData().characterBeforeSelection;
2438 }
2439
2440 - (CGRect)textFirstRect
2441 {
2442     return (_page->editorState().hasComposition) ? _page->editorState().firstMarkedRect : _autocorrectionData.textFirstRect;
2443 }
2444
2445 - (CGRect)textLastRect
2446 {
2447     return (_page->editorState().hasComposition) ? _page->editorState().lastMarkedRect : _autocorrectionData.textLastRect;
2448 }
2449
2450 - (void)replaceDictatedText:(NSString*)oldText withText:(NSString *)newText
2451 {
2452     _page->replaceDictatedText(oldText, newText);
2453 }
2454
2455 - (void)requestDictationContext:(void (^)(NSString *selectedText, NSString *beforeText, NSString *afterText))completionHandler
2456 {
2457     UIWKDictationContextHandler dictationHandler = [completionHandler copy];
2458
2459     _page->requestDictationContext([dictationHandler](const String& selectedText, const String& beforeText, const String& afterText, WebKit::CallbackBase::Error) {
2460         dictationHandler(selectedText, beforeText, afterText);
2461         [dictationHandler release];
2462     });
2463 }
2464
2465 // The completion handler should pass the rect of the correction text after replacing the input text, or nil if the replacement could not be performed.
2466 - (void)applyAutocorrection:(NSString *)correction toString:(NSString *)input withCompletionHandler:(void (^)(UIWKAutocorrectionRects *rectsForCorrection))completionHandler
2467 {
2468     // FIXME: Remove the synchronous call when <rdar://problem/16207002> is fixed.
2469     const bool useSyncRequest = true;
2470
2471     if (useSyncRequest) {
2472         completionHandler(_page->applyAutocorrection(correction, input) ? [WKAutocorrectionRects autocorrectionRectsWithRects:_autocorrectionData.textFirstRect lastRect:_autocorrectionData.textLastRect] : nil);
2473         return;
2474     }
2475     _autocorrectionData.autocorrectionHandler = [completionHandler copy];
2476     RetainPtr<WKContentView> view = self;
2477     _page->applyAutocorrection(correction, input, [view](const String& string, WebKit::CallbackBase::Error error) {
2478         view->_autocorrectionData.autocorrectionHandler(!string.isNull() ? [WKAutocorrectionRects autocorrectionRectsWithRects:view->_autocorrectionData.textFirstRect lastRect:view->_autocorrectionData.textLastRect] : nil);
2479         [view->_autocorrectionData.autocorrectionHandler release];
2480         view->_autocorrectionData.autocorrectionHandler = nil;
2481     });
2482 }
2483
2484 - (void)requestAutocorrectionContextWithCompletionHandler:(void (^)(UIWKAutocorrectionContext *autocorrectionContext))completionHandler
2485 {
2486     // FIXME: Remove the synchronous call when <rdar://problem/16207002> is fixed.
2487     const bool useSyncRequest = true;
2488
2489     if (useSyncRequest) {
2490         String beforeText;
2491         String markedText;
2492         String selectedText;
2493         String afterText;
2494         uint64_t location;
2495         uint64_t length;
2496         _page->getAutocorrectionContext(beforeText, markedText, selectedText, afterText, location, length);
2497         completionHandler([WKAutocorrectionContext autocorrectionContextWithData:beforeText markedText:markedText selectedText:selectedText afterText:afterText selectedRangeInMarkedText:NSMakeRange(location, length)]);
2498     } else {
2499         _autocorrectionData.autocorrectionContextHandler = [completionHandler copy];
2500         RetainPtr<WKContentView> view = self;
2501         _page->requestAutocorrectionContext([view](const String& beforeText, const String& markedText, const String& selectedText, const String& afterText, uint64_t location, uint64_t length, WebKit::CallbackBase::Error) {
2502             view->_autocorrectionData.autocorrectionContextHandler([WKAutocorrectionContext autocorrectionContextWithData:beforeText markedText:markedText selectedText:selectedText afterText:afterText selectedRangeInMarkedText:NSMakeRange(location, length)]);
2503         });
2504     }
2505 }
2506
2507 // UIWebFormAccessoryDelegate
2508 - (void)accessoryDone
2509 {
2510     [self resignFirstResponder];
2511 }
2512
2513 - (NSArray *)keyCommands
2514 {
2515     static NSArray* nonEditableKeyCommands = [@[
2516        [UIKeyCommand keyCommandWithInput:UIKeyInputUpArrow modifierFlags:0 action:@selector(_arrowKey:)],
2517        [UIKeyCommand keyCommandWithInput:UIKeyInputDownArrow modifierFlags:0 action:@selector(_arrowKey:)],
2518        [UIKeyCommand keyCommandWithInput:UIKeyInputLeftArrow modifierFlags:0 action:@selector(_arrowKey:)],
2519        [UIKeyCommand keyCommandWithInput:UIKeyInputRightArrow modifierFlags:0 action:@selector(_arrowKey:)],
2520        
2521        [UIKeyCommand keyCommandWithInput:UIKeyInputUpArrow modifierFlags:UIKeyModifierCommand action:@selector(_arrowKey:)],
2522        [UIKeyCommand keyCommandWithInput:UIKeyInputDownArrow modifierFlags:UIKeyModifierCommand action:@selector(_arrowKey:)],
2523        
2524        [UIKeyCommand keyCommandWithInput:UIKeyInputUpArrow modifierFlags:UIKeyModifierShift action:@selector(_arrowKey:)],
2525        [UIKeyCommand keyCommandWithInput:UIKeyInputDownArrow modifierFlags:UIKeyModifierShift action:@selector(_arrowKey:)],
2526        [UIKeyCommand keyCommandWithInput:UIKeyInputLeftArrow modifierFlags:UIKeyModifierShift action:@selector(_arrowKey:)],
2527        [UIKeyCommand keyCommandWithInput:UIKeyInputRightArrow modifierFlags:UIKeyModifierShift action:@selector(_arrowKey:)],
2528        
2529        [UIKeyCommand keyCommandWithInput:UIKeyInputUpArrow modifierFlags:UIKeyModifierAlternate action:@selector(_arrowKey:)],
2530        [UIKeyCommand keyCommandWithInput:UIKeyInputDownArrow modifierFlags:UIKeyModifierAlternate action:@selector(_arrowKey:)],
2531        [UIKeyCommand keyCommandWithInput:UIKeyInputLeftArrow modifierFlags:UIKeyModifierAlternate action:@selector(_arrowKey:)],
2532        [UIKeyCommand keyCommandWithInput:UIKeyInputRightArrow modifierFlags:UIKeyModifierAlternate action:@selector(_arrowKey:)],
2533        
2534        [UIKeyCommand keyCommandWithInput:@" " modifierFlags:0 action:@selector(_arrowKey:)],
2535        [UIKeyCommand keyCommandWithInput:@" " modifierFlags:UIKeyModifierShift action:@selector(_arrowKey:)],
2536        
2537        [UIKeyCommand keyCommandWithInput:UIKeyInputPageDown modifierFlags:0 action:@selector(_arrowKey:)],
2538        [UIKeyCommand keyCommandWithInput:UIKeyInputPageDown modifierFlags:0 action:@selector(_arrowKey:)],
2539     ] retain];
2540
2541     static NSArray* editableKeyCommands = [@[
2542        [UIKeyCommand keyCommandWithInput:@"\t" modifierFlags:0 action:@selector(_nextAccessoryTab:)],
2543        [UIKeyCommand keyCommandWithInput:@"\t" modifierFlags:UIKeyModifierShift action:@selector(_prevAccessoryTab:)]
2544     ] retain];
2545     
2546     return (_page->editorState().isContentEditable) ? editableKeyCommands : nonEditableKeyCommands;
2547 }
2548
2549 - (void)_arrowKey:(id)sender
2550 {
2551     UIKeyCommand* command = sender;
2552     [self handleKeyEvent:command._triggeringEvent];
2553 }
2554
2555 - (void)_nextAccessoryTab:(id)sender
2556 {
2557     [self accessoryTab:YES];
2558 }
2559
2560 - (void)_prevAccessoryTab:(id)sender
2561 {
2562     [self accessoryTab:NO];
2563 }
2564
2565 - (void)accessoryTab:(BOOL)isNext
2566 {
2567     [_inputPeripheral endEditing];
2568     _inputPeripheral = nil;
2569
2570     _didAccessoryTabInitiateFocus = YES; // Will be cleared in either -_displayFormNodeInputView or -cleanupInteraction.
2571     [self beginSelectionChange];
2572     RetainPtr<WKContentView> view = self;
2573     _page->focusNextAssistedNode(isNext, [view](WebKit::CallbackBase::Error) {
2574         [view endSelectionChange];
2575         [view reloadInputViews];
2576     });
2577
2578 }
2579
2580 - (void)_becomeFirstResponderWithSelectionMovingForward:(BOOL)selectingForward completionHandler:(void (^)(BOOL didBecomeFirstResponder))completionHandler
2581 {
2582     auto completionHandlerCopy = Block_copy(completionHandler);
2583     RetainPtr<WKContentView> view = self;
2584     _page->setInitialFocus(selectingForward, false, WebKit::WebKeyboardEvent(), [view, completionHandlerCopy](WebKit::CallbackBase::Error) {
2585         BOOL didBecomeFirstResponder = view->_assistedNodeInformation.elementType != InputType::None && [view becomeFirstResponder];
2586         completionHandlerCopy(didBecomeFirstResponder);
2587         Block_release(completionHandlerCopy);
2588     });
2589 }
2590
2591 - (WebCore::Color)_tapHighlightColorForFastClick:(BOOL)forFastClick
2592 {
2593     ASSERT(_showDebugTapHighlightsForFastClicking);
2594     return forFastClick ? WebCore::Color(0, 225, 0, 127) : WebCore::Color(225, 0, 0, 127);
2595 }
2596
2597 - (void)_setDoubleTapGesturesEnabled:(BOOL)enabled
2598 {
2599     if (enabled && ![_doubleTapGestureRecognizer isEnabled]) {
2600         // The first tap recognized after re-enabling double tap gestures will not wait for the
2601         // second tap before committing. To fix this, we use a new double tap gesture recognizer.
2602         [self removeGestureRecognizer:_doubleTapGestureRecognizer.get()];
2603         [_doubleTapGestureRecognizer setDelegate:nil];
2604         [self _createAndConfigureDoubleTapGestureRecognizer];
2605     }
2606
2607     if (_showDebugTapHighlightsForFastClicking && !enabled)
2608         _tapHighlightInformation.color = [self _tapHighlightColorForFastClick:YES];
2609
2610     [_doubleTapGestureRecognizer setEnabled:enabled];
2611     [_nonBlockingDoubleTapGestureRecognizer setEnabled:!enabled];
2612     [self _resetIsDoubleTapPending];
2613 }
2614
2615 - (void)accessoryAutoFill
2616 {
2617     id <_WKInputDelegate> inputDelegate = [_webView _inputDelegate];
2618     if ([inputDelegate respondsToSelector:@selector(_webView:accessoryViewCustomButtonTappedInFormInputSession:)])
2619         [inputDelegate _webView:_webView accessoryViewCustomButtonTappedInFormInputSession:_formInputSession.get()];
2620 }
2621
2622 - (void)accessoryClear
2623 {
2624     _page->setAssistedNodeValue(String());
2625 }
2626
2627 - (void)_updateAccessory
2628 {
2629     [_formAccessoryView setNextEnabled:_assistedNodeInformation.hasNextNode];
2630     [_formAccessoryView setPreviousEnabled:_assistedNodeInformation.hasPreviousNode];
2631
2632     if (UICurrentUserInterfaceIdiomIsPad())
2633         [_formAccessoryView setClearVisible:NO];
2634     else {
2635         switch (_assistedNodeInformation.elementType) {
2636         case InputType::Date:
2637         case InputType::Month:
2638         case InputType::DateTimeLocal:
2639         case InputType::Time:
2640             [_formAccessoryView setClearVisible:YES];
2641             break;
2642         default:
2643             [_formAccessoryView setClearVisible:NO];
2644             break;
2645         }
2646     }
2647
2648     // FIXME: hide or show the AutoFill button as needed.
2649 }
2650
2651 // Keyboard interaction
2652 // UITextInput protocol implementation
2653
2654 - (void)beginSelectionChange
2655 {
2656     [self.inputDelegate selectionWillChange:self];
2657 }
2658
2659 - (void)endSelectionChange
2660 {
2661     [self.inputDelegate selectionDidChange:self];
2662 }
2663     
2664 #if __IPHONE_OS_VERSION_MIN_REQUIRED >= 100000
2665 - (void)insertTextSuggestion:(UITextSuggestion *)textSuggestion
2666 {
2667     id <_WKInputDelegate> inputDelegate = [_webView _inputDelegate];
2668     if ([inputDelegate respondsToSelector:@selector(_webView:insertTextSuggestion:inInputSession:)])
2669         [inputDelegate _webView:_webView insertTextSuggestion:textSuggestion inInputSession:_formInputSession.get()];
2670 }
2671 #endif
2672
2673 - (NSString *)textInRange:(UITextRange *)range
2674 {
2675     return nil;
2676 }
2677
2678 - (void)replaceRange:(UITextRange *)range withText:(NSString *)text
2679 {
2680 }
2681
2682 - (UITextRange *)selectedTextRange
2683 {
2684     if (_page->editorState().selectionIsNone)
2685         return nil;
2686     auto& postLayoutEditorStateData = _page->editorState().postLayoutData();
2687     FloatRect startRect = postLayoutEditorStateData.caretRectAtStart;
2688     FloatRect endRect = postLayoutEditorStateData.caretRectAtEnd;
2689     double inverseScale = [self inverseScale];
2690     // We want to keep the original caret width, while the height scales with
2691     // the content taking orientation into account.
2692     // We achieve this by scaling the width with the inverse
2693     // scale factor. This way, when it is converted from the content view
2694     // the width remains unchanged.
2695     if (startRect.width() < startRect.height())
2696         startRect.setWidth(startRect.width() * inverseScale);
2697     else
2698         startRect.setHeight(startRect.height() * inverseScale);
2699     if (endRect.width() < endRect.height()) {
2700         double delta = endRect.width();
2701         endRect.setWidth(endRect.width() * inverseScale);
2702         delta = endRect.width() - delta;
2703         endRect.move(delta, 0);
2704     } else {
2705         double delta = endRect.height();
2706         endRect.setHeight(endRect.height() * inverseScale);
2707         delta = endRect.height() - delta;
2708         endRect.move(0, delta);
2709     }
2710     return [WKTextRange textRangeWithState:_page->editorState().selectionIsNone
2711                                    isRange:_page->editorState().selectionIsRange
2712                                 isEditable:_page->editorState().isContentEditable
2713                                  startRect:startRect
2714                                    endRect:endRect
2715                             selectionRects:[self webSelectionRects]
2716                         selectedTextLength:postLayoutEditorStateData.selectedTextLength];
2717 }
2718
2719 - (CGRect)caretRectForPosition:(UITextPosition *)position
2720 {
2721     return ((WKTextPosition *)position).positionRect;
2722 }
2723
2724 - (NSArray *)selectionRectsForRange:(UITextRange *)range
2725 {
2726     return [WKTextSelectionRect textSelectionRectsWithWebRects:((WKTextRange *)range).selectionRects];
2727 }
2728
2729 - (void)setSelectedTextRange:(UITextRange *)range
2730 {
2731 }
2732
2733 - (BOOL)hasMarkedText
2734 {
2735     return [_markedText length];
2736 }
2737
2738 - (NSString *)markedText
2739 {
2740     return _markedText.get();
2741 }
2742
2743 - (UITextRange *)markedTextRange
2744 {
2745     return nil;
2746 }
2747
2748 - (NSDictionary *)markedTextStyle
2749 {
2750     return nil;
2751 }
2752
2753 - (void)setMarkedTextStyle:(NSDictionary *)styleDictionary
2754 {
2755 }
2756
2757 - (void)setMarkedText:(NSString *)markedText selectedRange:(NSRange)selectedRange
2758 {
2759     _markedText = markedText;
2760     _page->setCompositionAsync(markedText, Vector<WebCore::CompositionUnderline>(), selectedRange, EditingRange());
2761 }
2762
2763 - (void)unmarkText
2764 {
2765     _markedText = nil;
2766     _page->confirmCompositionAsync();
2767 }
2768
2769 - (UITextPosition *)beginningOfDocument
2770 {
2771     return nil;
2772 }
2773
2774 - (UITextPosition *)endOfDocument
2775 {
2776     return nil;
2777 }
2778
2779 - (UITextRange *)textRangeFromPosition:(UITextPosition *)fromPosition toPosition:(UITextPosition *)toPosition
2780 {
2781     return nil;
2782 }
2783
2784 - (UITextPosition *)positionFromPosition:(UITextPosition *)position offset:(NSInteger)offset
2785 {
2786     return nil;
2787 }
2788
2789 - (UITextPosition *)positionFromPosition:(UITextPosition *)position inDirection:(UITextLayoutDirection)direction offset:(NSInteger)offset
2790 {
2791     return nil;
2792 }
2793
2794 - (NSComparisonResult)comparePosition:(UITextPosition *)position toPosition:(UITextPosition *)other
2795 {
2796     return NSOrderedSame;
2797 }
2798
2799 - (NSInteger)offsetFromPosition:(UITextPosition *)from toPosition:(UITextPosition *)toPosition
2800 {
2801     return 0;
2802 }
2803
2804 - (id <UITextInputTokenizer>)tokenizer
2805 {
2806     return nil;
2807 }
2808
2809 - (UITextPosition *)positionWithinRange:(UITextRange *)range farthestInDirection:(UITextLayoutDirection)direction
2810 {
2811     return nil;
2812 }
2813
2814 - (UITextRange *)characterRangeByExtendingPosition:(UITextPosition *)position inDirection:(UITextLayoutDirection)direction
2815 {
2816     return nil;
2817 }
2818
2819 - (UITextWritingDirection)baseWritingDirectionForPosition:(UITextPosition *)position inDirection:(UITextStorageDirection)direction
2820 {
2821     return UITextWritingDirectionLeftToRight;
2822 }
2823
2824 - (void)setBaseWritingDirection:(UITextWritingDirection)writingDirection forRange:(UITextRange *)range
2825 {
2826 }
2827
2828 - (CGRect)firstRectForRange:(UITextRange *)range
2829 {
2830     return CGRectZero;
2831 }
2832
2833 /* Hit testing. */
2834 - (UITextPosition *)closestPositionToPoint:(CGPoint)point
2835 {
2836     return nil;
2837 }
2838
2839 - (UITextPosition *)closestPositionToPoint:(CGPoint)point withinRange:(UITextRange *)range
2840 {
2841     return nil;
2842 }
2843
2844 - (UITextRange *)characterRangeAtPoint:(CGPoint)point
2845 {
2846     return nil;
2847 }
2848
2849 - (void)deleteBackward
2850 {
2851     _page->executeEditCommand(ASCIILiteral("deleteBackward"));
2852 }
2853
2854 // Inserts the given string, replacing any selected or marked text.
2855 - (void)insertText:(NSString *)aStringValue
2856 {
2857     _page->insertTextAsync(aStringValue, EditingRange());
2858 }
2859
2860 - (BOOL)hasText
2861 {
2862     return YES;
2863 }
2864
2865 // end of UITextInput protocol implementation
2866
2867 static UITextAutocapitalizationType toUITextAutocapitalize(WebAutocapitalizeType webkitType)
2868 {
2869     switch (webkitType) {
2870     case WebAutocapitalizeTypeDefault:
2871         return UITextAutocapitalizationTypeSentences;
2872     case WebAutocapitalizeTypeNone:
2873         return UITextAutocapitalizationTypeNone;
2874     case WebAutocapitalizeTypeWords:
2875         return UITextAutocapitalizationTypeWords;
2876     case WebAutocapitalizeTypeSentences:
2877         return UITextAutocapitalizationTypeSentences;
2878     case WebAutocapitalizeTypeAllCharacters:
2879         return UITextAutocapitalizationTypeAllCharacters;
2880     }
2881
2882     return UITextAutocapitalizationTypeSentences;
2883 }
2884
2885 #if __IPHONE_OS_VERSION_MIN_REQUIRED >= 100000
2886 static NSString *contentTypeFromFieldName(WebCore::AutofillFieldName fieldName)
2887 {
2888     switch (fieldName) {
2889     case WebCore::AutofillFieldName::Name:
2890         return UITextContentTypeName;
2891     case WebCore::AutofillFieldName::HonorificPrefix:
2892         return UITextContentTypeNamePrefix;
2893     case WebCore::AutofillFieldName::GivenName:
2894         return UITextContentTypeMiddleName;
2895     case WebCore::AutofillFieldName::AdditionalName:
2896         return UITextContentTypeMiddleName;
2897     case WebCore::AutofillFieldName::FamilyName:
2898         return UITextContentTypeFamilyName;
2899     case WebCore::AutofillFieldName::HonorificSuffix:
2900         return UITextContentTypeNameSuffix;
2901     case WebCore::AutofillFieldName::Nickname:
2902         return UITextContentTypeNickname;
2903     case WebCore::AutofillFieldName::OrganizationTitle:
2904         return UITextContentTypeJobTitle;
2905     case WebCore::AutofillFieldName::Organization:
2906         return UITextContentTypeOrganizationName;
2907     case WebCore::AutofillFieldName::StreetAddress:
2908         return UITextContentTypeFullStreetAddress;
2909     case WebCore::AutofillFieldName::AddressLine1:
2910         return UITextContentTypeStreetAddressLine1;
2911     case WebCore::AutofillFieldName::AddressLine2:
2912         return UITextContentTypeStreetAddressLine2;
2913     case WebCore::AutofillFieldName::AddressLevel3:
2914         return UITextContentTypeSublocality;
2915     case WebCore::AutofillFieldName::AddressLevel2:
2916         return UITextContentTypeAddressCity;
2917     case WebCore::AutofillFieldName::AddressLevel1:
2918         return UITextContentTypeAddressState;
2919     case WebCore::AutofillFieldName::CountryName:
2920         return UITextContentTypeCountryName;
2921     case WebCore::AutofillFieldName::PostalCode:
2922         return UITextContentTypePostalCode;
2923     case WebCore::AutofillFieldName::Tel:
2924         return UITextContentTypeTelephoneNumber;
2925     case WebCore::AutofillFieldName::Email:
2926         return UITextContentTypeEmailAddress;
2927     case WebCore::AutofillFieldName::URL:
2928         return UITextContentTypeURL;
2929     case WebCore::AutofillFieldName::None:
2930     case WebCore::AutofillFieldName::Username:
2931     case WebCore::AutofillFieldName::NewPassword:
2932     case WebCore::AutofillFieldName::CurrentPassword:
2933     case WebCore::AutofillFieldName::AddressLine3:
2934     case WebCore::AutofillFieldName::AddressLevel4:
2935     case WebCore::AutofillFieldName::Country:
2936     case WebCore::AutofillFieldName::CcName:
2937     case WebCore::AutofillFieldName::CcGivenName:
2938     case WebCore::AutofillFieldName::CcAdditionalName:
2939     case WebCore::AutofillFieldName::CcFamilyName:
2940     case WebCore::AutofillFieldName::CcNumber:
2941     case WebCore::AutofillFieldName::CcExp:
2942     case WebCore::AutofillFieldName::CcExpMonth:
2943     case WebCore::AutofillFieldName::CcExpYear:
2944     case WebCore::AutofillFieldName::CcCsc:
2945     case WebCore::AutofillFieldName::CcType:
2946     case WebCore::AutofillFieldName::TransactionCurrency:
2947     case WebCore::AutofillFieldName::TransactionAmount:
2948     case WebCore::AutofillFieldName::Language:
2949     case WebCore::AutofillFieldName::Bday:
2950     case WebCore::AutofillFieldName::BdayDay:
2951     case WebCore::AutofillFieldName::BdayMonth:
2952     case WebCore::AutofillFieldName::BdayYear:
2953     case WebCore::AutofillFieldName::Sex:
2954     case WebCore::AutofillFieldName::Photo:
2955     case WebCore::AutofillFieldName::TelCountryCode:
2956     case WebCore::AutofillFieldName::TelNational:
2957     case WebCore::AutofillFieldName::TelAreaCode:
2958     case WebCore::AutofillFieldName::TelLocal:
2959     case WebCore::AutofillFieldName::TelLocalPrefix:
2960     case WebCore::AutofillFieldName::TelLocalSuffix:
2961     case WebCore::AutofillFieldName::TelExtension:
2962     case WebCore::AutofillFieldName::Impp:
2963         break;
2964     };
2965
2966     return nil;
2967 }
2968 #endif
2969
2970 // UITextInputPrivate protocol
2971 // Direct access to the (private) UITextInputTraits object.
2972 - (UITextInputTraits *)textInputTraits
2973 {
2974     if (!_traits)
2975         _traits = adoptNS([[UITextInputTraits alloc] init]);
2976
2977     [_traits setSecureTextEntry:_assistedNodeInformation.elementType == InputType::Password || [_formInputSession forceSecureTextEntry]];
2978     [_traits setShortcutConversionType:_assistedNodeInformation.elementType == InputType::Password ? UITextShortcutConversionTypeNo : UITextShortcutConversionTypeDefault];
2979
2980     if (!_assistedNodeInformation.formAction.isEmpty())
2981         [_traits setReturnKeyType:(_assistedNodeInformation.elementType == InputType::Search) ? UIReturnKeySearch : UIReturnKeyGo];
2982
2983     if (_assistedNodeInformation.elementType == InputType::Password || _assistedNodeInformation.elementType == InputType::Email || _assistedNodeInformation.elementType == InputType::URL || _assistedNodeInformation.formAction.contains("login")) {
2984         [_traits setAutocapitalizationType:UITextAutocapitalizationTypeNone];
2985         [_traits setAutocorrectionType:UITextAutocorrectionTypeNo];
2986     } else {
2987         [_traits setAutocapitalizationType:toUITextAutocapitalize(_assistedNodeInformation.autocapitalizeType)];
2988         [_traits setAutocorrectionType:_assistedNodeInformation.isAutocorrect ? UITextAutocorrectionTypeYes : UITextAutocorrectionTypeNo];
2989     }
2990
2991     switch (_assistedNodeInformation.elementType) {
2992     case InputType::Phone:
2993         [_traits setKeyboardType:UIKeyboardTypePhonePad];
2994         break;
2995     case InputType::URL:
2996         [_traits setKeyboardType:UIKeyboardTypeURL];
2997         break;
2998     case InputType::Email:
2999         [_traits setKeyboardType:UIKeyboardTypeEmailAddress];
3000         break;
3001     case InputType::Number:
3002         [_traits setKeyboardType:UIKeyboardTypeNumbersAndPunctuation];
3003         break;
3004     case InputType::NumberPad:
3005         [_traits setKeyboardType:UIKeyboardTypeNumberPad];
3006         break;
3007     default:
3008         [_traits setKeyboardType:UIKeyboardTypeDefault];
3009     }
3010
3011 #if __IPHONE_OS_VERSION_MIN_REQUIRED >= 100000
3012     [_traits setTextContentType:contentTypeFromFieldName(_assistedNodeInformation.autofillFieldName)];
3013 #endif
3014
3015     return _traits.get();
3016 }
3017
3018 - (UITextInteractionAssistant *)interactionAssistant
3019 {
3020     return _textSelectionAssistant.get();
3021 }
3022
3023 - (UIWebSelectionAssistant *)webSelectionAssistant
3024 {
3025     return _webSelectionAssistant.get();
3026 }
3027
3028 - (id<UISelectionInteractionAssistant>)selectionInteractionAssistant
3029 {
3030     if ([_webSelectionAssistant conformsToProtocol:@protocol(UISelectionInteractionAssistant)])
3031         return (id<UISelectionInteractionAssistant>)_webSelectionAssistant.get();
3032     return nil;
3033 }
3034
3035 // NSRange support.  Would like to deprecate to the extent possible, although some support
3036 // (i.e. selectionRange) has shipped as API.
3037 - (NSRange)selectionRange
3038 {
3039     return NSMakeRange(NSNotFound, 0);
3040 }
3041
3042 - (CGRect)rectForNSRange:(NSRange)range
3043 {
3044     return CGRectZero;
3045 }
3046
3047 - (NSRange)_markedTextNSRange
3048 {
3049     return NSMakeRange(NSNotFound, 0);
3050 }
3051
3052 // DOM range support.
3053 - (DOMRange *)selectedDOMRange
3054 {
3055     return nil;
3056 }
3057
3058 - (void)setSelectedDOMRange:(DOMRange *)range affinityDownstream:(BOOL)affinityDownstream
3059 {
3060 }
3061
3062 // Modify text without starting a new undo grouping.
3063 - (void)replaceRangeWithTextWithoutClosingTyping:(UITextRange *)range replacementText:(NSString *)text
3064 {
3065 }
3066
3067 // Caret rect support.  Shouldn't be necessary, but firstRectForRange doesn't offer precisely
3068 // the same functionality.
3069 - (CGRect)rectContainingCaretSelection
3070 {
3071     return CGRectZero;
3072 }
3073
3074 // Web events.
3075 - (BOOL)requiresKeyEvents
3076 {
3077     return YES;
3078 }
3079
3080 - (void)_handleKeyUIEvent:(::UIEvent *)event
3081 {
3082     // We only want to handle key event from the hardware keyboard when we are
3083     // first responder and we are not interacting with editable content.
3084     if ([self isFirstResponder] && event._hidEvent && !_page->editorState().isContentEditable) {
3085         [self handleKeyEvent:event];
3086         return;
3087     }
3088
3089     [super _handleKeyUIEvent:event];
3090 }
3091
3092 - (void)handleKeyEvent:(::UIEvent *)event
3093 {
3094     // WebCore has already seen the event, no need for custom processing.
3095     if (event == _uiEventBeingResent)
3096         return;
3097
3098     WKWebEvent *webEvent = [[[WKWebEvent alloc] initWithKeyEventType:(event._isKeyDown) ? WebEventKeyDown : WebEventKeyUp
3099                                                            timeStamp:event.timestamp
3100                                                           characters:event._modifiedInput
3101                                          charactersIgnoringModifiers:event._unmodifiedInput
3102                                                            modifiers:event._modifierFlags
3103                                                          isRepeating:(event._inputFlags & kUIKeyboardInputRepeat)
3104                                                            withFlags:event._inputFlags
3105                                                              keyCode:0
3106                                                             isTabKey:[event._modifiedInput isEqualToString:@"\t"]
3107                                                         characterSet:WebEventCharacterSetUnicode] autorelease];
3108     webEvent.uiEvent = event;
3109     
3110     [self handleKeyWebEvent:webEvent];    
3111 }
3112
3113 - (void)handleKeyWebEvent:(WebIOSEvent *)theEvent
3114 {
3115     _page->handleKeyboardEvent(NativeWebKeyboardEvent(theEvent));
3116 }
3117
3118 - (void)handleKeyWebEvent:(WebIOSEvent *)theEvent withCompletionHandler:(void (^)(WebIOSEvent *theEvent, BOOL wasHandled))completionHandler
3119 {
3120     _keyWebEventHandler = [completionHandler copy];
3121     _page->handleKeyboardEvent(NativeWebKeyboardEvent(theEvent));
3122 }
3123
3124 - (void)_didHandleKeyEvent:(WebIOSEvent *)event eventWasHandled:(BOOL)eventWasHandled
3125 {
3126     if (_keyWebEventHandler) {
3127         _keyWebEventHandler(event, eventWasHandled);
3128         [_keyWebEventHandler release];
3129         _keyWebEventHandler = nil;
3130         return;
3131     }
3132         
3133     if (event.type == WebEventKeyDown) {
3134         // FIXME: This is only for staging purposes.
3135         if ([[UIKeyboardImpl sharedInstance] respondsToSelector:@selector(didHandleWebKeyEvent:)])
3136             [[UIKeyboardImpl sharedInstance] didHandleWebKeyEvent:event];
3137         else
3138             [[UIKeyboardImpl sharedInstance] didHandleWebKeyEvent];
3139     }
3140
3141     // If we aren't interacting with editable content, we still need to call [super _handleKeyUIEvent:]
3142     // so that keyboard repeat will work correctly. If we are interacting with editable content,
3143     // we already did so in _handleKeyUIEvent.
3144     if (eventWasHandled && _page->editorState().isContentEditable)
3145         return;
3146
3147     if (![event isKindOfClass:[WKWebEvent class]])
3148         return;
3149
3150     // Resending the event may destroy this WKContentView.
3151     RetainPtr<WKContentView> protector(self);
3152
3153     // We keep here the event when resending it to the application to distinguish
3154     // the case of a new event from one that has been already sent to WebCore.
3155     ASSERT(!_uiEventBeingResent);
3156     _uiEventBeingResent = [(WKWebEvent *)event uiEvent];
3157     [super _handleKeyUIEvent:_uiEventBeingResent.get()];
3158     _uiEventBeingResent = nil;
3159 }
3160
3161 - (Optional<FloatPoint>)_scrollOffsetForEvent:(WebIOSEvent *)event
3162 {
3163     static const unsigned kWebSpaceKey = 0x20;
3164
3165     if (_page->editorState().isContentEditable)
3166         return Nullopt;
3167
3168     NSString *charactersIgnoringModifiers = event.charactersIgnoringModifiers;
3169     if (!charactersIgnoringModifiers.length)
3170         return Nullopt;
3171
3172     enum ScrollingIncrement { Document, Page, Line };
3173     enum ScrollingDirection { Up, Down, Left, Right };
3174
3175     auto computeOffset = ^(ScrollingIncrement increment, ScrollingDirection direction) {
3176         bool isHorizontal = (direction == Left || direction == Right);
3177
3178         CGFloat scrollDistance = ^ CGFloat {
3179             switch (increment) {
3180             case Document:
3181                 ASSERT(!isHorizontal);
3182                 return self.bounds.size.height;
3183             case Page:
3184                 ASSERT(!isHorizontal);
3185                 return Scrollbar::pageStep(_page->unobscuredContentRect().height(), self.bounds.size.height);
3186             case Line:
3187                 return Scrollbar::pixelsPerLineStep();
3188             }
3189             ASSERT_NOT_REACHED();
3190             return 0;
3191         }();
3192
3193         if (direction == Up || direction == Left)
3194             scrollDistance = -scrollDistance;
3195         
3196         return (isHorizontal ? FloatPoint(scrollDistance, 0) : FloatPoint(0, scrollDistance));
3197     };
3198
3199     if ([charactersIgnoringModifiers isEqualToString:UIKeyInputLeftArrow])
3200         return computeOffset(Line, Left);
3201     if ([charactersIgnoringModifiers isEqualToString:UIKeyInputRightArrow])
3202         return computeOffset(Line, Right);
3203
3204     ScrollingIncrement incrementForVerticalArrowKey = Line;
3205     if (event.modifierFlags & WebEventFlagMaskAlternate)
3206         incrementForVerticalArrowKey = Page;
3207     else if (event.modifierFlags & WebEventFlagMaskCommand)
3208         incrementForVerticalArrowKey = Document;
3209     if ([charactersIgnoringModifiers isEqualToString:UIKeyInputUpArrow])
3210         return computeOffset(incrementForVerticalArrowKey, Up);
3211     if ([charactersIgnoringModifiers isEqualToString:UIKeyInputDownArrow])
3212         return computeOffset(incrementForVerticalArrowKey, Down);
3213
3214     if ([charactersIgnoringModifiers isEqualToString:UIKeyInputPageDown])
3215         return computeOffset(Page, Down);
3216     if ([charactersIgnoringModifiers isEqualToString:UIKeyInputPageUp])
3217         return computeOffset(Page, Up);
3218
3219     if ([charactersIgnoringModifiers characterAtIndex:0] == kWebSpaceKey)
3220         return computeOffset(Page, (event.modifierFlags & WebEventFlagMaskShift) ? Up : Down);
3221
3222     return Nullopt;
3223 }
3224
3225 - (BOOL)_interpretKeyEvent:(WebIOSEvent *)event isCharEvent:(BOOL)isCharEvent
3226 {
3227     static const unsigned kWebEnterKey = 0x0003;
3228     static const unsigned kWebBackspaceKey = 0x0008;
3229     static const unsigned kWebReturnKey = 0x000D;
3230     static const unsigned kWebDeleteKey = 0x007F;
3231     static const unsigned kWebDeleteForwardKey = 0xF728;
3232     static const unsigned kWebSpaceKey = 0x20;
3233
3234     BOOL contentEditable = _page->editorState().isContentEditable;
3235
3236     if (!contentEditable && event.isTabKey)
3237         return NO;
3238
3239     if (Optional<FloatPoint> scrollOffset = [self _scrollOffsetForEvent:event]) {
3240         [_webView _scrollByContentOffset:*scrollOffset];
3241         return YES;
3242     }
3243
3244     UIKeyboardImpl *keyboard = [UIKeyboardImpl sharedInstance];
3245     NSString *characters = event.characters;
3246     
3247     if (!characters.length)
3248         return NO;
3249
3250     switch ([characters characterAtIndex:0]) {
3251     case kWebBackspaceKey:
3252     case kWebDeleteKey:
3253         if (contentEditable) {
3254             // FIXME: remove deleteFromInput once UIKit adopts deleteFromInputWithFlags
3255             if ([keyboard respondsToSelector:@selector(deleteFromInputWithFlags:)])
3256                 [keyboard deleteFromInputWithFlags:event.keyboardFlags];
3257             else
3258                 [keyboard deleteFromInput];
3259             return YES;
3260         }
3261         break;
3262
3263     case kWebSpaceKey:
3264         if (contentEditable && isCharEvent) {
3265             if ([keyboard respondsToSelector:@selector(addInputString:withFlags:withInputManagerHint:)])
3266                 [keyboard addInputString:event.characters withFlags:event.keyboardFlags withInputManagerHint:event.inputManagerHint];
3267             else
3268                 [keyboard addInputString:event.characters withFlags:event.keyboardFlags];
3269             return YES;
3270         }
3271         break;
3272
3273     case kWebEnterKey:
3274     case kWebReturnKey:
3275         if (contentEditable && isCharEvent) {
3276             // Map \r from HW keyboard to \n to match the behavior of the soft keyboard.
3277             [keyboard addInputString:@"\n" withFlags:0];
3278             return YES;
3279         }
3280         break;
3281
3282     case kWebDeleteForwardKey:
3283         _page->executeEditCommand(ASCIILiteral("deleteForward"));
3284         return YES;
3285
3286     default:
3287         if (contentEditable && isCharEvent) {
3288             if ([keyboard respondsToSelector:@selector(addInputString:withFlags:withInputManagerHint:)])
3289                 [keyboard addInputString:event.characters withFlags:event.keyboardFlags withInputManagerHint:event.inputManagerHint];
3290             else
3291                 [keyboard addInputString:event.characters withFlags:event.keyboardFlags];
3292             return YES;
3293         }
3294         break;
3295     }
3296
3297     return NO;
3298 }
3299
3300 - (void)executeEditCommandWithCallback:(NSString *)commandName
3301 {
3302     [self beginSelectionChange];
3303     RetainPtr<WKContentView> view = self;
3304     _page->executeEditCommand(commandName, [view](WebKit::CallbackBase::Error) {
3305         [view endSelectionChange];
3306     });
3307 }
3308
3309 - (UITextInputArrowKeyHistory *)_moveUp:(BOOL)extending withHistory:(UITextInputArrowKeyHistory *)history
3310 {
3311     [self executeEditCommandWithCallback:extending ? @"moveUpAndModifySelection" : @"moveUp"];
3312     return nil;
3313 }
3314
3315 - (UITextInputArrowKeyHistory *)_moveDown:(BOOL)extending withHistory:(UITextInputArrowKeyHistory *)history
3316 {
3317     [self executeEditCommandWithCallback:extending ? @"moveDownAndModifySelection" : @"moveDown"];
3318     return nil;
3319 }
3320
3321 - (UITextInputArrowKeyHistory *)_moveLeft:(BOOL)extending withHistory:(UITextInputArrowKeyHistory *)history
3322 {
3323     [self executeEditCommandWithCallback:extending? @"moveLeftAndModifySelection" : @"moveLeft"];
3324     return nil;
3325 }
3326
3327 - (UITextInputArrowKeyHistory *)_moveRight:(BOOL)extending withHistory:(UITextInputArrowKeyHistory *)history
3328 {
3329     [self executeEditCommandWithCallback:extending ? @"moveRightAndModifySelection" : @"moveRight"];
3330     return nil;
3331 }
3332
3333 - (UITextInputArrowKeyHistory *)_moveToStartOfWord:(BOOL)extending withHistory:(UITextInputArrowKeyHistory *)history
3334 {
3335     [self executeEditCommandWithCallback:extending ? @"moveWordBackwardAndModifySelection" : @"moveWordBackward"];
3336     return nil;
3337 }
3338
3339 - (UITextInputArrowKeyHistory *)_moveToStartOfParagraph:(BOOL)extending withHistory:(UITextInputArrowKeyHistory *)history
3340 {
3341     [self executeEditCommandWithCallback:extending ? @"moveToBeginningOfParagraphAndModifySelection" : @"moveToBeginningOfParagraph"];
3342     return nil;
3343 }
3344
3345 - (UITextInputArrowKeyHistory *)_moveToStartOfLine:(BOOL)extending withHistory:(UITextInputArrowKeyHistory *)history
3346 {
3347     [self executeEditCommandWithCallback:extending ? @"moveToBeginningOfLineAndModifySelection" : @"moveToBeginningOfLine"];
3348     return nil;
3349 }
3350
3351 - (UITextInputArrowKeyHistory *)_moveToStartOfDocument:(BOOL)extending withHistory:(UITextInputArrowKeyHistory *)history
3352 {
3353     [self executeEditCommandWithCallback:extending ? @"moveToBeginningOfDocumentAndModifySelection" : @"moveToBeginningOfDocument"];
3354     return nil;
3355 }
3356
3357 - (UITextInputArrowKeyHistory *)_moveToEndOfWord:(BOOL)extending withHistory:(UITextInputArrowKeyHistory *)history
3358 {
3359     [self executeEditCommandWithCallback:extending ? @"moveWordForwardAndModifySelection" : @"moveWordForward"];
3360     return nil;
3361 }
3362
3363 - (UITextInputArrowKeyHistory *)_moveToEndOfParagraph:(BOOL)extending withHistory:(UITextInputArrowKeyHistory *)history
3364 {
3365     [self executeEditCommandWithCallback:extending ? @"moveToEndOfParagraphAndModifySelection" : @"moveToEndOfParagraph"];
3366     return nil;
3367 }
3368
3369 - (UITextInputArrowKeyHistory *)_moveToEndOfLine:(BOOL)extending withHistory:(UITextInputArrowKeyHistory *)history
3370 {
3371     [self executeEditCommandWithCallback:extending ? @"moveToEndOfLineAndModifySelection" : @"moveToEndOfLine"];
3372     return nil;
3373 }
3374
3375 - (UITextInputArrowKeyHistory *)_moveToEndOfDocument:(BOOL)extending withHistory:(UITextInputArrowKeyHistory *)history
3376 {
3377     [self executeEditCommandWithCallback:extending ? @"moveToEndOfDocumentAndModifySelection" : @"moveToEndOfDocument"];
3378     return nil;
3379 }
3380
3381 // Sets a buffer to make room for autocorrection views
3382 - (void)setBottomBufferHeight:(CGFloat)bottomBuffer
3383 {
3384 }
3385
3386 - (UIView *)automaticallySelectedOverlay
3387 {
3388     return self;
3389 }
3390
3391 - (UITextGranularity)selectionGranularity
3392 {
3393     return UITextGranularityCharacter;
3394 }
3395
3396 // Should return an array of NSDictionary objects that key/value paries for the final text, correction identifier and
3397 // alternative selection counts using the keys defined at the top of this header.
3398 - (NSArray *)metadataDictionariesForDictationResults
3399 {
3400     return nil;
3401 }
3402
3403 // Returns the dictation result boundaries from position so that text that was not dictated can be excluded from logging.
3404 // If these are not implemented, no text will be logged.
3405 - (UITextPosition *)previousUnperturbedDictationResultBoundaryFromPosition:(UITextPosition *)position
3406 {
3407     return nil;
3408 }
3409
3410 - (UITextPosition *)nextUnperturbedDictationResultBoundaryFromPosition:(UITextPosition *)position
3411 {
3412     return nil;
3413 }
3414
3415 // The can all be (and have been) trivially implemented in terms of UITextInput.  Deprecate and remove.
3416 - (void)moveBackward:(unsigned)count
3417 {
3418 }
3419
3420 - (void)moveForward:(unsigned)count
3421 {
3422 }
3423
3424 - (unichar)characterBeforeCaretSelection
3425 {
3426     return 0;
3427 }
3428
3429 - (NSString *)wordContainingCaretSelection
3430 {
3431     return nil;
3432 }
3433
3434 - (DOMRange *)wordRangeContainingCaretSelection
3435 {
3436     return nil;
3437 }
3438
3439 - (void)setMarkedText:(NSString *)text
3440 {
3441 }
3442
3443 - (BOOL)hasContent
3444 {
3445     return _page->editorState().postLayoutData().hasContent;
3446 }
3447
3448 - (void)selectAll
3449 {
3450 }
3451
3452 - (UIColor *)textColorForCaretSelection
3453 {
3454     return [UIColor blackColor];
3455 }
3456
3457 - (UIFont *)fontForCaretSelection
3458 {
3459     CGFloat zoomScale = 1.0;    // FIXME: retrieve the actual document scale factor.
3460     CGFloat scaledSize = _autocorrectionData.fontSize;
3461     if (CGFAbs(zoomScale - 1.0) > FLT_EPSILON)
3462         scaledSize *= zoomScale;
3463     return [UIFont fontWithFamilyName:_autocorrectionData.fontName traits:(UIFontTrait)_autocorrectionData.fontTraits size:scaledSize];
3464 }
3465
3466 - (BOOL)hasSelection
3467 {
3468     return NO;
3469 }
3470
3471 - (BOOL)isPosition:(UITextPosition *)position atBoundary:(UITextGranularity)granularity inDirection:(UITextDirection)direction
3472 {
3473     return NO;
3474 }
3475
3476 - (UITextPosition *)positionFromPosition:(UITextPosition *)position toBoundary:(UITextGranularity)granularity inDirection:(UITextDirection)direction
3477 {
3478     return nil;
3479 }
3480
3481 - (BOOL)isPosition:(UITextPosition *)position withinTextUnit:(UITextGranularity)granularity inDirection:(UITextDirection)direction
3482 {
3483     return NO;
3484 }
3485
3486 - (UITextRange *)rangeEnclosingPosition:(UITextPosition *)position withGranularity:(UITextGranularity)granularity inDirection:(UITextDirection)direction
3487 {
3488     return nil;
3489 }
3490
3491 - (void)takeTraitsFrom:(UITextInputTraits *)traits
3492 {
3493     [[self textInputTraits] takeTraitsFrom:traits];
3494 }
3495
3496 // FIXME: I want to change the name of these functions, but I'm leaving it for now
3497 // to make it easier to look up the corresponding functions in UIKit.
3498
3499 - (void)_startAssistingKeyboard
3500 {
3501     [self useSelectionAssistantWithMode:UIWebSelectionModeTextOnly];
3502     [self reloadInputViews];
3503 }
3504
3505 - (void)_stopAssistingKeyboard
3506 {
3507     [self useSelectionAssistantWithMode:toUIWebSelectionMode([_webView _selectionGranularity])];
3508 }
3509
3510 - (const AssistedNodeInformation&)assistedNodeInformation
3511 {
3512     return _assistedNodeInformation;
3513 }
3514
3515 - (Vector<OptionItem>&)assistedNodeSelectOptions
3516 {
3517     return _assistedNodeInformation.selectOptions;
3518 }
3519
3520 - (UIWebFormAccessory *)formAccessoryView
3521 {
3522     [self _ensureFormAccessoryView];
3523     return _formAccessoryView.get();
3524 }
3525
3526 static bool isAssistableInputType(InputType type)
3527 {
3528     switch (type) {
3529     case InputType::ContentEditable:
3530     case InputType::Text:
3531     case InputType::Password:
3532     case InputType::TextArea:
3533     case InputType::Search:
3534     case InputType::Email:
3535     case InputType::URL:
3536     case InputType::Phone:
3537     case InputType::Number:
3538     case InputType::NumberPad:
3539     case InputType::Date:
3540     case InputType::DateTime:
3541     case InputType::DateTimeLocal:
3542     case InputType::Month:
3543     case InputType::Week:
3544     case InputType::Time:
3545     case InputType::Select:
3546         return true;
3547
3548     case InputType::None:
3549         return false;
3550     }
3551
3552     ASSERT_NOT_REACHED();
3553     return false;
3554 }
3555
3556 - (void)_startAssistingNode:(const AssistedNodeInformation&)information userIsInteracting:(BOOL)userIsInteracting blurPreviousNode:(BOOL)blurPreviousNode userObject:(NSObject <NSSecureCoding> *)userObject
3557 {
3558     id <_WKInputDelegate> inputDelegate = [_webView _inputDelegate];
3559     RetainPtr<WKFocusedElementInfo> focusedElementInfo = adoptNS([[WKFocusedElementInfo alloc] initWithAssistedNodeInformation:information isUserInitiated:userIsInteracting]);
3560     BOOL shouldShowKeyboard;
3561
3562     if ([inputDelegate respondsToSelector:@selector(_webView:focusShouldStartInputSession:)])
3563         shouldShowKeyboard = [inputDelegate _webView:_webView focusShouldStartInputSession:focusedElementInfo.get()];
3564     else {
3565         // The default behavior is to allow node assistance if the user is interacting or the keyboard is already active.
3566         shouldShowKeyboard = userIsInteracting || _textSelectionAssistant;
3567     }
3568     if (!shouldShowKeyboard)
3569         return;
3570
3571     if (blurPreviousNode)
3572         [self _stopAssistingNode];
3573
3574     if (!isAssistableInputType(information.elementType))
3575         return;
3576
3577     // FIXME: We should remove this check when we manage to send StartAssistingNode from the WebProcess
3578     // only when it is truly time to show the keyboard.
3579     if (_assistedNodeInformation.elementType == information.elementType && _assistedNodeInformation.elementRect == information.elementRect)
3580         return;
3581
3582     _isEditable = YES;
3583     _assistedNodeInformation = information;
3584     _inputPeripheral = nil;
3585     _traits = nil;
3586     if (![self isFirstResponder])
3587         [self becomeFirstResponder];
3588
3589     [self reloadInputViews];
3590     
3591     switch (information.elementType) {
3592     case InputType::Select:
3593     case InputType::DateTimeLocal:
3594     case InputType::Time:
3595     case InputType::Month:
3596     case InputType::Date:
3597         break;
3598     default:
3599         [self _startAssistingKeyboard];
3600         break;
3601     }
3602     
3603     if (information.insideFixedPosition)
3604         [_webView _updateVisibleContentRects];
3605     
3606     [self _displayFormNodeInputView];
3607
3608     // _inputPeripheral has been initialized in inputView called by reloadInputViews.
3609     [_inputPeripheral beginEditing];
3610
3611     if ([inputDelegate respondsToSelector:@selector(_webView:didStartInputSession:)]) {
3612         _formInputSession = adoptNS([[WKFormInputSession alloc] initWithContentView:self focusedElementInfo:focusedElementInfo.get() userObject:userObject]);
3613         [inputDelegate _webView:_webView didStartInputSession:_formInputSession.get()];
3614     }
3615 }
3616
3617 - (void)_stopAssistingNode
3618 {
3619     [_formInputSession invalidate];
3620     _formInputSession = nil;
3621     _isEditable = NO;
3622     _assistedNodeInformation.elementType = InputType::None;
3623     _inputPeripheral = nil;
3624
3625     [self _stopAssistingKeyboard];
3626     [_formAccessoryView hideAutoFillButton];
3627     [self reloadInputViews];
3628     [self _updateAccessory];
3629     // The name is misleading, but this actually clears the selection views and removes any selection.
3630     [_webSelectionAssistant resignedFirstResponder];
3631 }
3632
3633 - (void)_selectionChanged
3634 {
3635     _selectionNeedsUpdate = YES;
3636     // If we are changing the selection with a gesture there is no need
3637     // to wait to paint the selection.
3638     if (_usingGestureForSelection)
3639         [self _updateChangedSelection];
3640 }
3641
3642 - (void)selectWordForReplacement
3643 {
3644     _page->extendSelection(WordGranularity);
3645 }
3646
3647 - (void)_updateChangedSelection
3648 {
3649     [self _updateChangedSelection:NO];
3650 }
3651
3652 - (void)_updateChangedSelection:(BOOL)force
3653 {
3654     if (!_selectionNeedsUpdate)
3655         return;
3656
3657     WKSelectionDrawingInfo selectionDrawingInfo(_page->editorState());
3658     if (!force && selectionDrawingInfo == _lastSelectionDrawingInfo)
3659         return;
3660
3661     _lastSelectionDrawingInfo = selectionDrawingInfo;
3662
3663     // FIXME: We need to figure out what to do if the selection is changed by Javascript.
3664     if (_textSelectionAssistant) {
3665         _markedText = (_page->editorState().hasComposition) ? _page->editorState().markedText : String();
3666         if (!_showingTextStyleOptions)
3667             [_textSelectionAssistant selectionChanged];
3668     } else if (!_page->editorState().isContentEditable)
3669         [_webSelectionAssistant selectionChanged];
3670     _selectionNeedsUpdate = NO;
3671     if (_shouldRestoreSelection) {
3672         [_webSelectionAssistant didEndScrollingOverflow];
3673         [_textSelectionAssistant didEndScrollingOverflow];
3674         _shouldRestoreSelection = NO;
3675     }
3676 }
3677
3678 - (void)_showPlaybackTargetPicker:(BOOL)hasVideo fromRect:(const IntRect&)elementRect
3679 {
3680     if (!_airPlayRoutePicker)
3681         _airPlayRoutePicker = adoptNS([[WKAirPlayRoutePicker alloc] initWithView:self]);
3682     [_airPlayRoutePicker show:hasVideo fromRect:elementRect];
3683 }
3684
3685 - (void)_showRunOpenPanel:(API::OpenPanelParameters*)parameters resultListener:(WebOpenPanelResultListenerProxy*)listener
3686 {
3687     ASSERT(!_fileUploadPanel);
3688     if (_fileUploadPanel)
3689         return;
3690
3691     _fileUploadPanel = adoptNS([[WKFileUploadPanel alloc] initWithView:self]);
3692     [_fileUploadPanel setDelegate:self];
3693     [_fileUploadPanel presentWithParameters:parameters resultListener:listener];
3694 }
3695
3696 - (void)fileUploadPanelDidDismiss:(WKFileUploadPanel *)fileUploadPanel
3697 {
3698     ASSERT(_fileUploadPanel.get() == fileUploadPanel);
3699
3700     [_fileUploadPanel setDelegate:nil];
3701     _fileUploadPanel = nil;
3702 }
3703
3704 #pragma mark - Implementation of UIWebTouchEventsGestureRecognizerDelegate.
3705
3706 - (BOOL)shouldIgnoreWebTouch
3707 {
3708     return NO;
3709 }
3710
3711 - (BOOL)isAnyTouchOverActiveArea:(NSSet *)touches
3712 {
3713     return YES;
3714 }
3715
3716 #pragma mark - Implementation of WKActionSheetAssistantDelegate.
3717
3718 - (const WebKit::InteractionInformationAtPosition&)positionInformationForActionSheetAssistant:(WKActionSheetAssistant *)assistant
3719 {
3720     return _positionInformation;
3721 }
3722
3723 - (void)updatePositionInformationForActionSheetAssistant:(WKActionSheetAssistant *)assistant
3724 {
3725     _hasValidPositionInformation = NO;
3726     _page->requestPositionInformation(_positionInformation.point);
3727 }
3728
3729 - (void)actionSheetAssistant:(WKActionSheetAssistant *)assistant performAction:(WebKit::SheetAction)action
3730 {
3731     _page->performActionOnElement((uint32_t)action);
3732 }
3733
3734 - (void)actionSheetAssistant:(WKActionSheetAssistant *)assistant openElementAtLocation:(CGPoint)location
3735 {
3736     [self _attemptClickAtLocation:location];
3737 }
3738
3739 - (void)actionSheetAssistant:(WKActionSheetAssistant *)assistant shareElementWithURL:(NSURL *)url rect:(CGRect)boundingRect
3740 {
3741     if (_textSelectionAssistant)
3742         [_textSelectionAssistant showShareSheetFor:userVisibleString(url) fromRect:boundingRect];
3743     else if (_webSelectionAssistant)
3744         [_webSelectionAssistant showShareSheetFor:userVisibleString(url) fromRect:boundingRect];
3745 }
3746
3747 #if HAVE(APP_LINKS)
3748 - (BOOL)actionSheetAssistant:(WKActionSheetAssistant *)assistant shouldIncludeAppLinkActionsForElement:(_WKActivatedElementInfo *)element
3749 {
3750     return _page->uiClient().shouldIncludeAppLinkActionsForElement(element);
3751 }
3752 #endif
3753
3754 - (BOOL)actionSheetAssistant:(WKActionSheetAssistant *)assistant showCustomSheetForElement:(_WKActivatedElementInfo *)element
3755 {
3756     id <WKUIDelegatePrivate> uiDelegate = static_cast<id <WKUIDelegatePrivate>>([_webView UIDelegate]);
3757     
3758     if ([uiDelegate respondsToSelector:@selector(_webView:showCustomSheetForElement:)]) {
3759         if ([uiDelegate _webView:_webView showCustomSheetForElement:element]) {
3760             // Prevent tap-and-hold and drag.
3761             [UIApp _cancelAllTouches];
3762             return YES;
3763         }
3764     }
3765
3766     return NO;
3767 }
3768
3769 - (RetainPtr<NSArray>)actionSheetAssistant:(WKActionSheetAssistant *)assistant decideActionsForElement:(_WKActivatedElementInfo *)element defaultActions:(RetainPtr<NSArray>)defaultActions
3770 {
3771     return _page->uiClient().actionsForElement(element, WTFMove(defaultActions));
3772 }
3773
3774 - (void)actionSheetAssistant:(WKActionSheetAssistant *)assistant willStartInteractionWithElement:(_WKActivatedElementInfo *)element
3775 {
3776     _page->startInteractionWithElementAtPosition(_positionInformation.point);
3777 }
3778
3779 - (void)actionSheetAssistantDidStopInteraction:(WKActionSheetAssistant *)assistant
3780 {
3781     _page->stopInteraction();
3782 }
3783
3784 - (NSDictionary *)dataDetectionContextForActionSheetAssistant:(WKActionSheetAssistant *)assistant
3785 {
3786     NSDictionary *context = nil;
3787     id <WKUIDelegatePrivate> uiDelegate = static_cast<id <WKUIDelegatePrivate>>([_webView UIDelegate]);
3788     if ([uiDelegate respondsToSelector:@selector(_dataDetectionContextForWebView:)])
3789         context = [uiDelegate _dataDetectionContextForWebView:_webView];
3790     return context;
3791 }
3792
3793 - (NSString *)selectedTextForActionSheetAssistant:(WKActionSheetAssistant *)assistant
3794 {
3795     return [self selectedText];
3796 }
3797
3798 @end
3799
3800 @implementation WKContentView (WKTesting)
3801
3802 - (void)selectFormAccessoryPickerRow:(NSInteger)rowIndex
3803 {
3804     if ([_inputPeripheral isKindOfClass:[WKFormSelectControl self]])
3805         [(WKFormSelectControl *)_inputPeripheral selectRow:rowIndex inComponent:0 extendingSelection:NO];
3806 }
3807
3808 @end
3809
3810 #if HAVE(LINK_PREVIEW)
3811
3812 @implementation WKContentView (WKInteractionPreview)
3813
3814 - (void)_registerPreview
3815 {
3816     if (!_webView.allowsLinkPreview)
3817         return;
3818
3819     _previewItemController = adoptNS([[UIPreviewItemController alloc] initWithView:self]);
3820     [_previewItemController setDelegate:self];
3821     _previewGestureRecognizer = _previewItemController.get().presentationGestureRecognizer;
3822     if ([_previewItemController respondsToSelector:@selector(presentationSecondaryGestureRecognizer)])
3823         _previewSecondaryGestureRecognizer = _previewItemController.get().presentationSecondaryGestureRecognizer;
3824 }
3825
3826 - (void)_unregisterPreview
3827 {
3828     [_previewItemController setDelegate:nil];
3829     _previewGestureRecognizer = nil;
3830     _previewSecondaryGestureRecognizer = nil;
3831     _previewItemController = nil;
3832 }
3833
3834 - (BOOL)_interactionShouldBeginFromPreviewItemController:(UIPreviewItemController *)controller forPosition:(CGPoint)position
3835 {
3836     if (!_highlightLongPressCanClick)
3837         return NO;
3838
3839     [self ensurePositionInformationIsUpToDate:position];
3840     if (!_positionInformation.isLink && !_positionInformation.isImage && !_positionInformation.isAttachment)
3841         return NO;
3842     
3843     String absoluteLinkURL = _positionInformation.url;
3844     if (_positionInformation.isLink) {
3845         NSURL *targetURL = [NSURL _web_URLWithWTFString:_positionInformation.url];
3846         id <WKUIDelegatePrivate> uiDelegate = static_cast<id <WKUIDelegatePrivate>>([_webView UIDelegate]);
3847         if ([uiDelegate respondsToSelector:@selector(webView:shouldPreviewElement:)]) {
3848             auto previewElementInfo = adoptNS([[WKPreviewElementInfo alloc] _initWithLinkURL:targetURL]);
3849             return [uiDelegate webView:_webView shouldPreviewElement:previewElementInfo.get()];
3850         }
3851         if (absoluteLinkURL.isEmpty())
3852             return NO;
3853         if (WebCore::protocolIsInHTTPFamily(absoluteLinkURL))
3854             return YES;
3855         if ([[getDDDetectionControllerClass() tapAndHoldSchemes] containsObject:[targetURL scheme]])
3856             return YES;
3857         return NO;
3858     }
3859     return YES;
3860 }
3861
3862 - (NSDictionary *)_dataForPreviewItemController:(UIPreviewItemController *)controller atPosition:(CGPoint)position type:(UIPreviewItemType *)type
3863 {
3864     *type = UIPreviewItemTypeNone;
3865
3866     id <WKUIDelegatePrivate> uiDelegate = static_cast<id <WKUIDelegatePrivate>>([_webView UIDelegate]);
3867     BOOL supportsImagePreview = [uiDelegate respondsToSelector:@selector(_webView:commitPreviewedImageWithURL:)];
3868     BOOL canShowImagePreview = _positionInformation.isImage && supportsImagePreview;
3869     BOOL canShowLinkPreview = _positionInformation.isLink || canShowImagePreview;
3870     BOOL useImageURLForLink = NO;
3871     BOOL respondsToAttachmentListForWebViewSourceIsManaged = [uiDelegate respondsToSelector:@selector(_attachmentListForWebView:sourceIsManaged:)];
3872     BOOL supportsAttachmentPreview = ([uiDelegate respondsToSelector:@selector(_attachmentListForWebView:)] || respondsToAttachmentListForWebViewSourceIsManaged)
3873         && [uiDelegate respondsToSelector:@selector(_webView:indexIntoAttachmentListForElement:)];
3874     BOOL canShowAttachmentPreview = (_positionInformation.isAttachment || _positionInformation.isImage) && supportsAttachmentPreview;
3875
3876     if (canShowImagePreview && _positionInformation.isAnimatedImage) {
3877         canShowImagePreview = NO;
3878         canShowLinkPreview = YES;
3879         useImageURLForLink = YES;
3880     }
3881
3882     if (!canShowLinkPreview && !canShowImagePreview && !canShowAttachmentPreview)
3883         return nil;
3884
3885     String absoluteLinkURL = _positionInformation.url;
3886     if (!useImageURLForLink && (absoluteLinkURL.isEmpty() || (!WebCore::protocolIsInHTTPFamily(absoluteLinkURL) && !_positionInformation.isDataDetectorLink))) {
3887         if (canShowLinkPreview && !canShowImagePreview)
3888             return nil;
3889         canShowLinkPreview = NO;
3890     }
3891
3892     NSMutableDictionary *dataForPreview = [[[NSMutableDictionary alloc] init] autorelease];
3893     if (canShowLinkPreview) {
3894         *type = UIPreviewItemTypeLink;
3895         if (useImageURLForLink)
3896             dataForPreview[UIPreviewDataLink] = [NSURL _web_URLWithWTFString:_positionInformation.imageURL];
3897         else
3898             dataForPreview[UIPreviewDataLink] = [NSURL _web_URLWithWTFString:_positionInformation.url];
3899         if (_positionInformation.isDataDetectorLink) {
3900             NSDictionary *context = nil;
3901             if ([uiDelegate respondsToSelector:@selector(_dataDetectionContextForWebView:)])
3902                 context = [uiDelegate _dataDetectionContextForWebView:_webView];
3903
3904             DDDetectionController *controller = [getDDDetectionControllerClass() sharedController];
3905             if ([controller respondsToSelector:@selector(resultForURL:identifier:selectedText:results:context:extendedContext:)]) {
3906                 NSDictionary *newContext = nil;
3907                 RetainPtr<NSMutableDictionary> extendedContext;
3908                 DDResultRef ddResult = [controller resultForURL:dataForPreview[UIPreviewDataLink] identifier:_positionInformation.dataDetectorIdentifier selectedText:[self selectedText] results:_positionInformation.dataDetectorResults.get() context:context extendedContext:&newContext];
3909                 if (ddResult)
3910                     dataForPreview[UIPreviewDataDDResult] = (__bridge id)ddResult;
3911                 if (!_positionInformation.textBefore.isEmpty() || !_positionInformation.textAfter.isEmpty()) {
3912                     extendedContext = adoptNS([@{
3913                         getkDataDetectorsLeadingText() : _positionInformation.textBefore,
3914                         getkDataDetectorsTrailingText() : _positionInformation.textAfter,
3915                     } mutableCopy]);
3916                     
3917                     if (newContext)
3918                         [extendedContext addEntriesFromDictionary:newContext];
3919                     newContext = extendedContext.get();