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