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