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