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