[iOS] Paste is missing from callout bar when pasteboard only contains custom data
[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     auto editorState = _page->editorState();
2280     if (action == @selector(_showTextStyleOptions:))
2281         return editorState.isContentRichlyEditable && editorState.selectionIsRange && !_showingTextStyleOptions;
2282     if (_showingTextStyleOptions)
2283         return (action == @selector(toggleBoldface:) || action == @selector(toggleItalics:) || action == @selector(toggleUnderline:));
2284     if (action == @selector(toggleBoldface:) || action == @selector(toggleItalics:) || action == @selector(toggleUnderline:))
2285         return editorState.isContentRichlyEditable;
2286     if (action == @selector(cut:))
2287         return !editorState.isInPasswordField && editorState.isContentEditable && editorState.selectionIsRange;
2288     
2289     if (action == @selector(paste:)) {
2290         if (editorState.selectionIsNone || !editorState.isContentEditable)
2291             return NO;
2292         UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
2293         NSArray *types = [self supportedPasteboardTypesForCurrentSelection];
2294         NSIndexSet *indices = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, [pasteboard numberOfItems])];
2295         if ([pasteboard containsPasteboardTypes:types inItemSet:indices])
2296             return YES;
2297
2298         auto focusedDocumentOrigin = editorState.originIdentifierForPasteboard;
2299         if (focusedDocumentOrigin.isEmpty())
2300             return NO;
2301
2302         NSArray *allCustomPasteboardData = [pasteboard dataForPasteboardType:@(PasteboardCustomData::cocoaType()) inItemSet:indices];
2303         for (NSData *data in allCustomPasteboardData) {
2304             auto buffer = SharedBuffer::create(data);
2305             if (PasteboardCustomData::fromSharedBuffer(buffer.get()).origin == focusedDocumentOrigin)
2306                 return YES;
2307         }
2308         return NO;
2309     }
2310
2311     if (action == @selector(copy:)) {
2312         if (editorState.isInPasswordField)
2313             return NO;
2314         return editorState.selectionIsRange;
2315     }
2316
2317     if (action == @selector(_define:)) {
2318         if (editorState.isInPasswordField || !editorState.selectionIsRange)
2319             return NO;
2320
2321         NSUInteger textLength = editorState.postLayoutData().selectedTextLength;
2322         // FIXME: We should be calling UIReferenceLibraryViewController to check if the length is
2323         // acceptable, but the interface takes a string.
2324         // <rdar://problem/15254406>
2325         if (!textLength || textLength > 200)
2326             return NO;
2327
2328 #if !PLATFORM(IOSMAC)
2329         if ([[getMCProfileConnectionClass() sharedConnection] effectiveBoolValueForSetting:MCFeatureDefinitionLookupAllowed] == MCRestrictedBoolExplicitNo)
2330             return NO;
2331 #endif
2332             
2333         return YES;
2334     }
2335
2336     if (action == @selector(_lookup:)) {
2337         if (editorState.isInPasswordField)
2338             return NO;
2339
2340 #if !PLATFORM(IOSMAC)
2341         if ([[getMCProfileConnectionClass() sharedConnection] effectiveBoolValueForSetting:MCFeatureDefinitionLookupAllowed] == MCRestrictedBoolExplicitNo)
2342             return NO;
2343 #endif
2344
2345         return editorState.selectionIsRange;
2346     }
2347
2348     if (action == @selector(_share:)) {
2349         if (editorState.isInPasswordField || !editorState.selectionIsRange)
2350             return NO;
2351
2352         return editorState.postLayoutData().selectedTextLength > 0;
2353     }
2354
2355     if (action == @selector(_addShortcut:)) {
2356         if (editorState.isInPasswordField || !editorState.selectionIsRange)
2357             return NO;
2358
2359         NSString *selectedText = [self selectedText];
2360         if (![selectedText length])
2361             return NO;
2362
2363         if (!UIKeyboardEnabledInputModesAllowOneToManyShortcuts())
2364             return NO;
2365         if (![selectedText _containsCJScripts])
2366             return NO;
2367         return YES;
2368     }
2369
2370     if (action == @selector(_promptForReplace:)) {
2371         if (!editorState.selectionIsRange || !editorState.postLayoutData().isReplaceAllowed || ![[UIKeyboardImpl activeInstance] autocorrectSpellingEnabled])
2372             return NO;
2373         if ([[self selectedText] _containsCJScriptsOnly])
2374             return NO;
2375         return YES;
2376     }
2377
2378     if (action == @selector(_transliterateChinese:)) {
2379         if (!editorState.selectionIsRange || !editorState.postLayoutData().isReplaceAllowed || ![[UIKeyboardImpl activeInstance] autocorrectSpellingEnabled])
2380             return NO;
2381         return UIKeyboardEnabledInputModesAllowChineseTransliterationForText([self selectedText]);
2382     }
2383
2384     if (action == @selector(select:)) {
2385         // Disable select in password fields so that you can't see word boundaries.
2386         return !editorState.isInPasswordField && [self hasContent] && !editorState.selectionIsNone && !editorState.selectionIsRange;
2387     }
2388
2389     if (action == @selector(selectAll:)) {
2390         if (!editorState.selectionIsNone && !editorState.selectionIsRange)
2391             return YES;
2392         return NO;
2393     }
2394
2395     if (action == @selector(replace:))
2396         return editorState.isContentEditable && !editorState.isInPasswordField;
2397
2398     return [super canPerformAction:action withSender:sender];
2399 }
2400
2401 - (id)targetForAction:(SEL)action withSender:(id)sender
2402 {
2403     return [_webView targetForAction:action withSender:sender];
2404 }
2405
2406 - (id)targetForActionForWebView:(SEL)action withSender:(id)sender
2407 {
2408     return [super targetForAction:action withSender:sender];
2409 }
2410
2411 - (void)_resetShowingTextStyle:(NSNotification *)notification
2412 {
2413     _showingTextStyleOptions = NO;
2414     [_textSelectionAssistant hideTextStyleOptions];
2415 }
2416
2417 - (void)copyForWebView:(id)sender
2418 {
2419     _page->executeEditCommand("copy"_s);
2420 }
2421
2422 - (void)cutForWebView:(id)sender
2423 {
2424     _page->executeEditCommand("cut"_s);
2425 }
2426
2427 - (void)pasteForWebView:(id)sender
2428 {
2429     _page->executeEditCommand("paste"_s);
2430 }
2431
2432 - (void)selectForWebView:(id)sender
2433 {
2434     [_textSelectionAssistant selectWord];
2435     // We cannot use selectWord command, because we want to be able to select the word even when it is the last in the paragraph.
2436     _page->extendSelection(WordGranularity);
2437 }
2438
2439 - (void)selectAllForWebView:(id)sender
2440 {
2441     [_textSelectionAssistant selectAll:sender];
2442     _page->executeEditCommand("selectAll"_s);
2443 }
2444
2445 - (void)toggleBoldfaceForWebView:(id)sender
2446 {
2447     if (!_page->editorState().isContentRichlyEditable)
2448         return;
2449
2450     [self executeEditCommandWithCallback:@"toggleBold"];
2451 }
2452
2453 - (void)toggleItalicsForWebView:(id)sender
2454 {
2455     if (!_page->editorState().isContentRichlyEditable)
2456         return;
2457
2458     [self executeEditCommandWithCallback:@"toggleItalic"];
2459 }
2460
2461 - (void)toggleUnderlineForWebView:(id)sender
2462 {
2463     if (!_page->editorState().isContentRichlyEditable)
2464         return;
2465
2466     [self executeEditCommandWithCallback:@"toggleUnderline"];
2467 }
2468
2469 - (void)_showTextStyleOptionsForWebView:(id)sender
2470 {
2471     _showingTextStyleOptions = YES;
2472     [_textSelectionAssistant showTextStyleOptions];
2473 }
2474
2475 - (void)_showDictionary:(NSString *)text
2476 {
2477     CGRect presentationRect = _page->editorState().postLayoutData().selectionRects[0].rect();
2478     if (_textSelectionAssistant)
2479         [_textSelectionAssistant showDictionaryFor:text fromRect:presentationRect];
2480     else
2481         [_webSelectionAssistant showDictionaryFor:text fromRect:presentationRect];
2482 }
2483
2484 - (void)_defineForWebView:(id)sender
2485 {
2486 #if !PLATFORM(IOSMAC)
2487     if ([[getMCProfileConnectionClass() sharedConnection] effectiveBoolValueForSetting:MCFeatureDefinitionLookupAllowed] == MCRestrictedBoolExplicitNo)
2488         return;
2489 #endif
2490
2491     RetainPtr<WKContentView> view = self;
2492     _page->getSelectionOrContentsAsString([view](const String& string, WebKit::CallbackBase::Error error) {
2493         if (error != WebKit::CallbackBase::Error::None)
2494             return;
2495         if (!string)
2496             return;
2497
2498         [view _showDictionary:string];
2499     });
2500 }
2501
2502 - (void)accessibilityRetrieveSpeakSelectionContent
2503 {
2504     RetainPtr<WKContentView> view = self;
2505     RetainPtr<WKWebView> webView = _webView;
2506     _page->getSelectionOrContentsAsString([view, webView](const String& string, WebKit::CallbackBase::Error error) {
2507         if (error != WebKit::CallbackBase::Error::None)
2508             return;
2509         [webView _accessibilityDidGetSpeakSelectionContent:string];
2510         if ([view respondsToSelector:@selector(accessibilitySpeakSelectionSetContent:)])
2511             [view accessibilitySpeakSelectionSetContent:string];
2512     });
2513 }
2514
2515 - (void)_accessibilityRetrieveRectsEnclosingSelectionOffset:(NSInteger)offset withGranularity:(UITextGranularity)granularity
2516 {
2517     RetainPtr<WKContentView> view = self;
2518     _page->requestRectsForGranularityWithSelectionOffset(toWKTextGranularity(granularity), offset , [view, offset, granularity](const Vector<WebCore::SelectionRect>& selectionRects, CallbackBase::Error error) {
2519         if (error != WebKit::CallbackBase::Error::None)
2520             return;
2521         if ([view respondsToSelector:@selector(_accessibilityDidGetSelectionRects:withGranularity:atOffset:)])
2522             [view _accessibilityDidGetSelectionRects:[view webSelectionRectsForSelectionRects:selectionRects] withGranularity:granularity atOffset:offset];
2523     });
2524 }
2525
2526 - (void)_accessibilityRetrieveRectsAtSelectionOffset:(NSInteger)offset withText:(NSString *)text
2527 {
2528     [self _accessibilityRetrieveRectsAtSelectionOffset:offset withText:text completionHandler:nil];
2529 }
2530
2531 - (void)_accessibilityRetrieveRectsAtSelectionOffset:(NSInteger)offset withText:(NSString *)text completionHandler:(void (^)(const Vector<SelectionRect>& rects))completionHandler
2532 {
2533     RetainPtr<WKContentView> view = self;
2534     _page->requestRectsAtSelectionOffsetWithText(offset, text, [view, offset, capturedCompletionHandler = makeBlockPtr(completionHandler)](const Vector<SelectionRect>& selectionRects, CallbackBase::Error error) {
2535         if (capturedCompletionHandler)
2536             capturedCompletionHandler(selectionRects);
2537
2538         if (error != WebKit::CallbackBase::Error::None)
2539             return;
2540         if ([view respondsToSelector:@selector(_accessibilityDidGetSelectionRects:withGranularity:atOffset:)])
2541             [view _accessibilityDidGetSelectionRects:[view webSelectionRectsForSelectionRects:selectionRects] withGranularity:UITextGranularityWord atOffset:offset];
2542     });
2543 }
2544
2545 - (void)_accessibilityStoreSelection
2546 {
2547     _page->storeSelectionForAccessibility(true);
2548 }
2549
2550 - (void)_accessibilityClearSelection
2551 {
2552     _page->storeSelectionForAccessibility(false);
2553 }
2554
2555 // UIWKInteractionViewProtocol
2556
2557 static inline GestureType toGestureType(UIWKGestureType gestureType)
2558 {
2559     switch (gestureType) {
2560     case UIWKGestureLoupe:
2561         return GestureType::Loupe;
2562     case UIWKGestureOneFingerTap:
2563         return GestureType::OneFingerTap;
2564     case UIWKGestureTapAndAHalf:
2565         return GestureType::TapAndAHalf;
2566     case UIWKGestureDoubleTap:
2567         return GestureType::DoubleTap;
2568     case UIWKGestureTapAndHalf:
2569         return GestureType::TapAndHalf;
2570     case UIWKGestureDoubleTapInUneditable:
2571         return GestureType::DoubleTapInUneditable;
2572     case UIWKGestureOneFingerTapInUneditable:
2573         return GestureType::OneFingerTapInUneditable;
2574     case UIWKGestureOneFingerTapSelectsAll:
2575         return GestureType::OneFingerTapSelectsAll;
2576     case UIWKGestureOneFingerDoubleTap:
2577         return GestureType::OneFingerDoubleTap;
2578     case UIWKGestureOneFingerTripleTap:
2579         return GestureType::OneFingerTripleTap;
2580     case UIWKGestureTwoFingerSingleTap:
2581         return GestureType::TwoFingerSingleTap;
2582     case UIWKGestureTwoFingerRangedSelectGesture:
2583         return GestureType::TwoFingerRangedSelectGesture;
2584     case UIWKGestureTapOnLinkWithGesture:
2585         return GestureType::TapOnLinkWithGesture;
2586     case UIWKGestureMakeWebSelection:
2587         return GestureType::MakeWebSelection;
2588     case UIWKGesturePhraseBoundary:
2589         return GestureType::PhraseBoundary;
2590     }
2591     ASSERT_NOT_REACHED();
2592     return GestureType::Loupe;
2593 }
2594
2595 static inline UIWKGestureType toUIWKGestureType(GestureType gestureType)
2596 {
2597     switch (gestureType) {
2598     case GestureType::Loupe:
2599         return UIWKGestureLoupe;
2600     case GestureType::OneFingerTap:
2601         return UIWKGestureOneFingerTap;
2602     case GestureType::TapAndAHalf:
2603         return UIWKGestureTapAndAHalf;
2604     case GestureType::DoubleTap:
2605         return UIWKGestureDoubleTap;
2606     case GestureType::TapAndHalf:
2607         return UIWKGestureTapAndHalf;
2608     case GestureType::DoubleTapInUneditable:
2609         return UIWKGestureDoubleTapInUneditable;
2610     case GestureType::OneFingerTapInUneditable:
2611         return UIWKGestureOneFingerTapInUneditable;
2612     case GestureType::OneFingerTapSelectsAll:
2613         return UIWKGestureOneFingerTapSelectsAll;
2614     case GestureType::OneFingerDoubleTap:
2615         return UIWKGestureOneFingerDoubleTap;
2616     case GestureType::OneFingerTripleTap:
2617         return UIWKGestureOneFingerTripleTap;
2618     case GestureType::TwoFingerSingleTap:
2619         return UIWKGestureTwoFingerSingleTap;
2620     case GestureType::TwoFingerRangedSelectGesture:
2621         return UIWKGestureTwoFingerRangedSelectGesture;
2622     case GestureType::TapOnLinkWithGesture:
2623         return UIWKGestureTapOnLinkWithGesture;
2624     case GestureType::MakeWebSelection:
2625         return UIWKGestureMakeWebSelection;
2626     case GestureType::PhraseBoundary:
2627         return UIWKGesturePhraseBoundary;
2628     }
2629 }
2630
2631 static inline SelectionTouch toSelectionTouch(UIWKSelectionTouch touch)
2632 {
2633     switch (touch) {
2634     case UIWKSelectionTouchStarted:
2635         return SelectionTouch::Started;
2636     case UIWKSelectionTouchMoved:
2637         return SelectionTouch::Moved;
2638     case UIWKSelectionTouchEnded:
2639         return SelectionTouch::Ended;
2640     case UIWKSelectionTouchEndedMovingForward:
2641         return SelectionTouch::EndedMovingForward;
2642     case UIWKSelectionTouchEndedMovingBackward:
2643         return SelectionTouch::EndedMovingBackward;
2644     case UIWKSelectionTouchEndedNotMoving:
2645         return SelectionTouch::EndedNotMoving;
2646     }
2647     ASSERT_NOT_REACHED();
2648     return SelectionTouch::Ended;
2649 }
2650
2651 static inline UIWKSelectionTouch toUIWKSelectionTouch(SelectionTouch touch)
2652 {
2653     switch (touch) {
2654     case SelectionTouch::Started:
2655         return UIWKSelectionTouchStarted;
2656     case SelectionTouch::Moved:
2657         return UIWKSelectionTouchMoved;
2658     case SelectionTouch::Ended:
2659         return UIWKSelectionTouchEnded;
2660     case SelectionTouch::EndedMovingForward:
2661         return UIWKSelectionTouchEndedMovingForward;
2662     case SelectionTouch::EndedMovingBackward:
2663         return UIWKSelectionTouchEndedMovingBackward;
2664     case SelectionTouch::EndedNotMoving:
2665         return UIWKSelectionTouchEndedNotMoving;
2666     }
2667 }
2668
2669 static inline GestureRecognizerState toGestureRecognizerState(UIGestureRecognizerState state)
2670 {
2671     switch (state) {
2672     case UIGestureRecognizerStatePossible:
2673         return GestureRecognizerState::Possible;
2674     case UIGestureRecognizerStateBegan:
2675         return GestureRecognizerState::Began;
2676     case UIGestureRecognizerStateChanged:
2677         return GestureRecognizerState::Changed;
2678     case UIGestureRecognizerStateCancelled:
2679         return GestureRecognizerState::Cancelled;
2680     case UIGestureRecognizerStateEnded:
2681         return GestureRecognizerState::Ended;
2682     case UIGestureRecognizerStateFailed:
2683         return GestureRecognizerState::Failed;
2684     }
2685 }
2686
2687 static inline UIGestureRecognizerState toUIGestureRecognizerState(GestureRecognizerState state)
2688 {
2689     switch (state) {
2690     case GestureRecognizerState::Possible:
2691         return UIGestureRecognizerStatePossible;
2692     case GestureRecognizerState::Began:
2693         return UIGestureRecognizerStateBegan;
2694     case GestureRecognizerState::Changed:
2695         return UIGestureRecognizerStateChanged;
2696     case GestureRecognizerState::Cancelled:
2697         return UIGestureRecognizerStateCancelled;
2698     case GestureRecognizerState::Ended:
2699         return UIGestureRecognizerStateEnded;
2700     case GestureRecognizerState::Failed:
2701         return UIGestureRecognizerStateFailed;
2702     }
2703 }
2704
2705 static inline UIWKSelectionFlags toUIWKSelectionFlags(SelectionFlags flags)
2706 {
2707     NSInteger uiFlags = UIWKNone;
2708     if (flags & WordIsNearTap)
2709         uiFlags |= UIWKWordIsNearTap;
2710     if (flags & PhraseBoundaryChanged)
2711         uiFlags |= UIWKPhraseBoundaryChanged;
2712
2713     return static_cast<UIWKSelectionFlags>(uiFlags);
2714 }
2715
2716 static inline WebCore::TextGranularity toWKTextGranularity(UITextGranularity granularity)
2717 {
2718     switch (granularity) {
2719     case UITextGranularityCharacter:
2720         return CharacterGranularity;
2721     case UITextGranularityWord:
2722         return WordGranularity;
2723     case UITextGranularitySentence:
2724         return SentenceGranularity;
2725     case UITextGranularityParagraph:
2726         return ParagraphGranularity;
2727     case UITextGranularityLine:
2728         return LineGranularity;
2729     case UITextGranularityDocument:
2730         return DocumentGranularity;
2731     }
2732 }
2733
2734 static inline WebCore::SelectionDirection toWKSelectionDirection(UITextDirection direction)
2735 {
2736     switch (direction) {
2737     case UITextLayoutDirectionDown:
2738     case UITextLayoutDirectionRight:
2739         return DirectionRight;
2740     case UITextLayoutDirectionUp:
2741     case UITextLayoutDirectionLeft:
2742         return DirectionLeft;
2743     default:
2744         // UITextDirection is not an enum, but we only want to accept values from UITextLayoutDirection.
2745         ASSERT_NOT_REACHED();
2746         return DirectionRight;
2747     }
2748 }
2749
2750 static void selectionChangedWithGesture(WKContentView *view, const WebCore::IntPoint& point, uint32_t gestureType, uint32_t gestureState, uint32_t flags, WebKit::CallbackBase::Error error)
2751 {
2752     if (error != WebKit::CallbackBase::Error::None) {
2753         ASSERT_NOT_REACHED();
2754         return;
2755     }
2756     if ([view webSelectionAssistant])
2757         [(UIWKSelectionAssistant *)[view webSelectionAssistant] selectionChangedWithGestureAt:(CGPoint)point withGesture:toUIWKGestureType((GestureType)gestureType) withState:toUIGestureRecognizerState(static_cast<GestureRecognizerState>(gestureState)) withFlags:(toUIWKSelectionFlags((SelectionFlags)flags))];
2758     else
2759         [(UIWKTextInteractionAssistant *)[view interactionAssistant] selectionChangedWithGestureAt:(CGPoint)point withGesture:toUIWKGestureType((GestureType)gestureType) withState:toUIGestureRecognizerState(static_cast<GestureRecognizerState>(gestureState)) withFlags:(toUIWKSelectionFlags((SelectionFlags)flags))];
2760 }
2761
2762 static void selectionChangedWithTouch(WKContentView *view, const WebCore::IntPoint& point, uint32_t touch, uint32_t flags, WebKit::CallbackBase::Error error)
2763 {
2764     if (error != WebKit::CallbackBase::Error::None) {
2765         ASSERT_NOT_REACHED();
2766         return;
2767     }
2768     if ([view webSelectionAssistant])
2769         [(UIWKSelectionAssistant *)[view webSelectionAssistant] selectionChangedWithTouchAt:(CGPoint)point withSelectionTouch:toUIWKSelectionTouch((SelectionTouch)touch) withFlags:static_cast<UIWKSelectionFlags>(flags)];
2770     else
2771         [(UIWKTextInteractionAssistant *)[view interactionAssistant] selectionChangedWithTouchAt:(CGPoint)point withSelectionTouch:toUIWKSelectionTouch((SelectionTouch)touch) withFlags:static_cast<UIWKSelectionFlags>(flags)];
2772 }
2773
2774 - (BOOL)_isInteractingWithAssistedNode
2775 {
2776     return hasAssistedNode(_assistedNodeInformation);
2777 }
2778
2779 - (void)changeSelectionWithGestureAt:(CGPoint)point withGesture:(UIWKGestureType)gestureType withState:(UIGestureRecognizerState)state
2780 {
2781     [self changeSelectionWithGestureAt:point withGesture:gestureType withState:state withFlags:UIWKNone];
2782 }
2783
2784 - (void)changeSelectionWithGestureAt:(CGPoint)point withGesture:(UIWKGestureType)gestureType withState:(UIGestureRecognizerState)state withFlags:(UIWKSelectionFlags)flags
2785 {
2786     _usingGestureForSelection = YES;
2787     _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) {
2788         selectionChangedWithGesture(self, point, gestureType, gestureState, flags | innerFlags, error);
2789         if (state == UIGestureRecognizerStateEnded || state == UIGestureRecognizerStateCancelled)
2790             _usingGestureForSelection = NO;
2791     });
2792 }
2793
2794 #if __IPHONE_OS_VERSION_MAX_ALLOWED < 120000
2795 - (void)changeSelectionWithTouchAt:(CGPoint)point withSelectionTouch:(UIWKSelectionTouch)touch baseIsStart:(BOOL)baseIsStart
2796 {
2797     [self changeSelectionWithTouchAt:point withSelectionTouch:touch baseIsStart:baseIsStart withFlags:UIWKNone];
2798 }
2799 #endif
2800
2801 - (void)changeSelectionWithTouchAt:(CGPoint)point withSelectionTouch:(UIWKSelectionTouch)touch baseIsStart:(BOOL)baseIsStart withFlags:(UIWKSelectionFlags)flags
2802 {
2803     _usingGestureForSelection = YES;
2804     _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) {
2805         selectionChangedWithTouch(self, point, touch, flags | innerFlags, error);
2806         if (touch != UIWKSelectionTouchStarted && touch != UIWKSelectionTouchMoved)
2807             _usingGestureForSelection = NO;
2808     });
2809 }
2810
2811 - (void)changeSelectionWithTouchesFrom:(CGPoint)from to:(CGPoint)to withGesture:(UIWKGestureType)gestureType withState:(UIGestureRecognizerState)gestureState
2812 {
2813     _usingGestureForSelection = YES;
2814     _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) {
2815         selectionChangedWithGesture(self, point, gestureType, gestureState, flags, error);
2816         if (gestureState == UIGestureRecognizerStateEnded || gestureState == UIGestureRecognizerStateCancelled)
2817             _usingGestureForSelection = NO;
2818     });
2819 }
2820
2821 - (void)moveByOffset:(NSInteger)offset
2822 {
2823     if (!offset)
2824         return;
2825     
2826     [self beginSelectionChange];
2827     RetainPtr<WKContentView> view = self;
2828     _page->moveSelectionByOffset(offset, [view](WebKit::CallbackBase::Error) {
2829         [view endSelectionChange];
2830     });
2831 }
2832
2833 - (const WKAutoCorrectionData&)autocorrectionData
2834 {
2835     return _autocorrectionData;
2836 }
2837
2838 // The completion handler can pass nil if input does not match the actual text preceding the insertion point.
2839 - (void)requestAutocorrectionRectsForString:(NSString *)input withCompletionHandler:(void (^)(UIWKAutocorrectionRects *rectsForInput))completionHandler
2840 {
2841     if (!input || ![input length]) {
2842         completionHandler(nil);
2843         return;
2844     }
2845
2846     RetainPtr<WKContentView> view = self;
2847     _autocorrectionData.autocorrectionHandler = [completionHandler copy];
2848     _page->requestAutocorrectionData(input, [view](const Vector<FloatRect>& rects, const String& fontName, double fontSize, uint64_t traits, WebKit::CallbackBase::Error) {
2849         CGRect firstRect = CGRectZero;
2850         CGRect lastRect = CGRectZero;
2851         if (rects.size()) {
2852             firstRect = rects[0];
2853             lastRect = rects[rects.size() - 1];
2854         }
2855         
2856         view->_autocorrectionData.fontName = fontName;
2857         view->_autocorrectionData.fontSize = fontSize;
2858         view->_autocorrectionData.fontTraits = traits;
2859         view->_autocorrectionData.textFirstRect = firstRect;
2860         view->_autocorrectionData.textLastRect = lastRect;
2861
2862         view->_autocorrectionData.autocorrectionHandler(rects.size() ? [WKAutocorrectionRects autocorrectionRectsWithRects:firstRect lastRect:lastRect] : nil);
2863         [view->_autocorrectionData.autocorrectionHandler release];
2864         view->_autocorrectionData.autocorrectionHandler = nil;
2865     });
2866 }
2867
2868 - (void)selectPositionAtPoint:(CGPoint)point completionHandler:(void (^)(void))completionHandler
2869 {
2870     _usingGestureForSelection = YES;
2871     UIWKSelectionCompletionHandler selectionHandler = [completionHandler copy];
2872     RetainPtr<WKContentView> view = self;
2873     
2874     _page->selectPositionAtPoint(WebCore::IntPoint(point), [self _isInteractingWithAssistedNode], [view, selectionHandler](WebKit::CallbackBase::Error error) {
2875         selectionHandler();
2876         view->_usingGestureForSelection = NO;
2877         [selectionHandler release];
2878     });
2879 }
2880
2881 - (void)selectPositionAtBoundary:(UITextGranularity)granularity inDirection:(UITextDirection)direction fromPoint:(CGPoint)point completionHandler:(void (^)(void))completionHandler
2882 {
2883     _usingGestureForSelection = YES;
2884     UIWKSelectionCompletionHandler selectionHandler = [completionHandler copy];
2885     RetainPtr<WKContentView> view = self;
2886     
2887     _page->selectPositionAtBoundaryWithDirection(WebCore::IntPoint(point), toWKTextGranularity(granularity), toWKSelectionDirection(direction), [self _isInteractingWithAssistedNode], [view, selectionHandler](WebKit::CallbackBase::Error error) {
2888         selectionHandler();
2889         view->_usingGestureForSelection = NO;
2890         [selectionHandler release];
2891     });
2892 }
2893
2894 - (void)moveSelectionAtBoundary:(UITextGranularity)granularity inDirection:(UITextDirection)direction completionHandler:(void (^)(void))completionHandler
2895 {
2896     _usingGestureForSelection = YES;
2897     UIWKSelectionCompletionHandler selectionHandler = [completionHandler copy];
2898     RetainPtr<WKContentView> view = self;
2899     
2900     _page->moveSelectionAtBoundaryWithDirection(toWKTextGranularity(granularity), toWKSelectionDirection(direction), [view, selectionHandler](WebKit::CallbackBase::Error error) {
2901         selectionHandler();
2902         view->_usingGestureForSelection = NO;
2903         [selectionHandler release];
2904     });
2905 }
2906
2907 - (void)selectTextWithGranularity:(UITextGranularity)granularity atPoint:(CGPoint)point completionHandler:(void (^)(void))completionHandler
2908 {
2909     _usingGestureForSelection = YES;
2910     UIWKSelectionCompletionHandler selectionHandler = [completionHandler copy];
2911     RetainPtr<WKContentView> view = self;
2912
2913     _page->selectTextWithGranularityAtPoint(WebCore::IntPoint(point), toWKTextGranularity(granularity), [self _isInteractingWithAssistedNode], [view, selectionHandler](WebKit::CallbackBase::Error error) {
2914         selectionHandler();
2915         view->_usingGestureForSelection = NO;
2916         [selectionHandler release];
2917     });
2918 }
2919
2920 - (void)beginSelectionInDirection:(UITextDirection)direction completionHandler:(void (^)(BOOL endIsMoving))completionHandler
2921 {
2922     UIWKSelectionWithDirectionCompletionHandler selectionHandler = [completionHandler copy];
2923
2924     _page->beginSelectionInDirection(toWKSelectionDirection(direction), [selectionHandler](bool endIsMoving, WebKit::CallbackBase::Error error) {
2925         selectionHandler(endIsMoving);
2926         [selectionHandler release];
2927     });
2928 }
2929
2930 - (void)updateSelectionWithExtentPoint:(CGPoint)point completionHandler:(void (^)(BOOL endIsMoving))completionHandler
2931 {
2932     UIWKSelectionWithDirectionCompletionHandler selectionHandler = [completionHandler copy];
2933     
2934     _page->updateSelectionWithExtentPoint(WebCore::IntPoint(point), [self _isInteractingWithAssistedNode], [selectionHandler](bool endIsMoving, WebKit::CallbackBase::Error error) {
2935         selectionHandler(endIsMoving);
2936         [selectionHandler release];
2937     });
2938 }
2939
2940 - (void)updateSelectionWithExtentPoint:(CGPoint)point withBoundary:(UITextGranularity)granularity completionHandler:(void (^)(BOOL selectionEndIsMoving))completionHandler
2941 {
2942     UIWKSelectionWithDirectionCompletionHandler selectionHandler = [completionHandler copy];
2943     
2944     _page->updateSelectionWithExtentPointAndBoundary(WebCore::IntPoint(point), toWKTextGranularity(granularity), [self _isInteractingWithAssistedNode], [selectionHandler](bool endIsMoving, WebKit::CallbackBase::Error error) {
2945         selectionHandler(endIsMoving);
2946         [selectionHandler release];
2947     });
2948 }
2949
2950 - (UTF32Char)_characterBeforeCaretSelection
2951 {
2952     return _page->editorState().postLayoutData().characterBeforeSelection;
2953 }
2954
2955 - (UTF32Char)_characterInRelationToCaretSelection:(int)amount
2956 {
2957     switch (amount) {
2958     case 0:
2959         return _page->editorState().postLayoutData().characterAfterSelection;
2960     case -1:
2961         return _page->editorState().postLayoutData().characterBeforeSelection;
2962     case -2:
2963         return _page->editorState().postLayoutData().twoCharacterBeforeSelection;
2964     default:
2965         return 0;
2966     }
2967 }
2968
2969 - (BOOL)_selectionAtDocumentStart
2970 {
2971     return !_page->editorState().postLayoutData().characterBeforeSelection;
2972 }
2973
2974 - (CGRect)textFirstRect
2975 {
2976     return (_page->editorState().hasComposition) ? _page->editorState().firstMarkedRect : _autocorrectionData.textFirstRect;
2977 }
2978
2979 - (CGRect)textLastRect
2980 {
2981     return (_page->editorState().hasComposition) ? _page->editorState().lastMarkedRect : _autocorrectionData.textLastRect;
2982 }
2983
2984 - (void)replaceDictatedText:(NSString*)oldText withText:(NSString *)newText
2985 {
2986     _page->replaceDictatedText(oldText, newText);
2987 }
2988
2989 - (void)requestDictationContext:(void (^)(NSString *selectedText, NSString *beforeText, NSString *afterText))completionHandler
2990 {
2991     UIWKDictationContextHandler dictationHandler = [completionHandler copy];
2992
2993     _page->requestDictationContext([dictationHandler](const String& selectedText, const String& beforeText, const String& afterText, WebKit::CallbackBase::Error) {
2994         dictationHandler(selectedText, beforeText, afterText);
2995         [dictationHandler release];
2996     });
2997 }
2998
2999 // 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.
3000 - (void)applyAutocorrection:(NSString *)correction toString:(NSString *)input withCompletionHandler:(void (^)(UIWKAutocorrectionRects *rectsForCorrection))completionHandler
3001 {
3002     // FIXME: Remove the synchronous call when <rdar://problem/16207002> is fixed.
3003     const bool useSyncRequest = true;
3004
3005     if (useSyncRequest) {
3006         completionHandler(_page->applyAutocorrection(correction, input) ? [WKAutocorrectionRects autocorrectionRectsWithRects:_autocorrectionData.textFirstRect lastRect:_autocorrectionData.textLastRect] : nil);
3007         return;
3008     }
3009     _autocorrectionData.autocorrectionHandler = [completionHandler copy];
3010     RetainPtr<WKContentView> view = self;
3011     _page->applyAutocorrection(correction, input, [view](const String& string, WebKit::CallbackBase::Error error) {
3012         view->_autocorrectionData.autocorrectionHandler(!string.isNull() ? [WKAutocorrectionRects autocorrectionRectsWithRects:view->_autocorrectionData.textFirstRect lastRect:view->_autocorrectionData.textLastRect] : nil);
3013         [view->_autocorrectionData.autocorrectionHandler release];
3014         view->_autocorrectionData.autocorrectionHandler = nil;
3015     });
3016 }
3017
3018 - (void)requestAutocorrectionContextWithCompletionHandler:(void (^)(UIWKAutocorrectionContext *autocorrectionContext))completionHandler
3019 {
3020     // FIXME: Remove the synchronous call when <rdar://problem/16207002> is fixed.
3021     const bool useSyncRequest = true;
3022
3023     if (useSyncRequest) {
3024         String beforeText;
3025         String markedText;
3026         String selectedText;
3027         String afterText;
3028         uint64_t location;
3029         uint64_t length;
3030         _page->getAutocorrectionContext(beforeText, markedText, selectedText, afterText, location, length);
3031         completionHandler([WKAutocorrectionContext autocorrectionContextWithData:beforeText markedText:markedText selectedText:selectedText afterText:afterText selectedRangeInMarkedText:NSMakeRange(location, length)]);
3032     } else {
3033         _autocorrectionData.autocorrectionContextHandler = [completionHandler copy];
3034         RetainPtr<WKContentView> view = self;
3035         _page->requestAutocorrectionContext([view](const String& beforeText, const String& markedText, const String& selectedText, const String& afterText, uint64_t location, uint64_t length, WebKit::CallbackBase::Error) {
3036             view->_autocorrectionData.autocorrectionContextHandler([WKAutocorrectionContext autocorrectionContextWithData:beforeText markedText:markedText selectedText:selectedText afterText:afterText selectedRangeInMarkedText:NSMakeRange(location, length)]);
3037         });
3038     }
3039 }
3040
3041 // UIWebFormAccessoryDelegate
3042 - (void)accessoryDone
3043 {
3044     [self resignFirstResponder];
3045 }
3046
3047 - (NSArray *)keyCommands
3048 {
3049     static NSArray* nonEditableKeyCommands = [@[
3050        [UIKeyCommand keyCommandWithInput:UIKeyInputUpArrow modifierFlags:0 action:@selector(_arrowKey:)],
3051        [UIKeyCommand keyCommandWithInput:UIKeyInputDownArrow modifierFlags:0 action:@selector(_arrowKey:)],
3052        [UIKeyCommand keyCommandWithInput:UIKeyInputLeftArrow modifierFlags:0 action:@selector(_arrowKey:)],
3053        [UIKeyCommand keyCommandWithInput:UIKeyInputRightArrow modifierFlags:0 action:@selector(_arrowKey:)],
3054        
3055        [UIKeyCommand keyCommandWithInput:UIKeyInputUpArrow modifierFlags:UIKeyModifierCommand action:@selector(_arrowKey:)],
3056        [UIKeyCommand keyCommandWithInput:UIKeyInputDownArrow modifierFlags:UIKeyModifierCommand action:@selector(_arrowKey:)],
3057        
3058        [UIKeyCommand keyCommandWithInput:UIKeyInputUpArrow modifierFlags:UIKeyModifierShift action:@selector(_arrowKey:)],
3059        [UIKeyCommand keyCommandWithInput:UIKeyInputDownArrow modifierFlags:UIKeyModifierShift action:@selector(_arrowKey:)],
3060        [UIKeyCommand keyCommandWithInput:UIKeyInputLeftArrow modifierFlags:UIKeyModifierShift action:@selector(_arrowKey:)],
3061        [UIKeyCommand keyCommandWithInput:UIKeyInputRightArrow modifierFlags:UIKeyModifierShift action:@selector(_arrowKey:)],
3062        
3063        [UIKeyCommand keyCommandWithInput:UIKeyInputUpArrow modifierFlags:UIKeyModifierAlternate action:@selector(_arrowKey:)],
3064        [UIKeyCommand keyCommandWithInput:UIKeyInputDownArrow modifierFlags:UIKeyModifierAlternate action:@selector(_arrowKey:)],
3065        [UIKeyCommand keyCommandWithInput:UIKeyInputLeftArrow modifierFlags:UIKeyModifierAlternate action:@selector(_arrowKey:)],
3066        [UIKeyCommand keyCommandWithInput:UIKeyInputRightArrow modifierFlags:UIKeyModifierAlternate action:@selector(_arrowKey:)],
3067        
3068        [UIKeyCommand keyCommandWithInput:@" " modifierFlags:0 action:@selector(_arrowKey:)],
3069        [UIKeyCommand keyCommandWithInput:@" " modifierFlags:UIKeyModifierShift action:@selector(_arrowKey:)],
3070        
3071        [UIKeyCommand keyCommandWithInput:UIKeyInputPageDown modifierFlags:0 action:@selector(_arrowKey:)],
3072        [UIKeyCommand keyCommandWithInput:UIKeyInputPageDown modifierFlags:0 action:@selector(_arrowKey:)],
3073     ] retain];
3074
3075     static NSArray* editableKeyCommands = [@[
3076        [UIKeyCommand keyCommandWithInput:@"\t" modifierFlags:0 action:@selector(_nextAccessoryTab:)],
3077        [UIKeyCommand keyCommandWithInput:@"\t" modifierFlags:UIKeyModifierShift action:@selector(_prevAccessoryTab:)]
3078     ] retain];
3079     
3080     return (_page->editorState().isContentEditable) ? editableKeyCommands : nonEditableKeyCommands;
3081 }
3082
3083 - (void)_arrowKeyForWebView:(id)sender
3084 {
3085     UIKeyCommand* command = sender;
3086     [self handleKeyEvent:command._triggeringEvent];
3087 }
3088
3089 - (void)_nextAccessoryTab:(id)sender
3090 {
3091     [self accessoryTab:YES];
3092 }
3093
3094 - (void)_prevAccessoryTab:(id)sender
3095 {
3096     [self accessoryTab:NO];
3097 }
3098
3099 - (void)accessoryTab:(BOOL)isNext
3100 {
3101     [_inputPeripheral endEditing];
3102     _inputPeripheral = nil;
3103
3104     _didAccessoryTabInitiateFocus = YES; // Will be cleared in either -_displayFormNodeInputView or -cleanupInteraction.
3105     [self beginSelectionChange];
3106     RetainPtr<WKContentView> view = self;
3107     _page->focusNextAssistedNode(isNext, [view](WebKit::CallbackBase::Error) {
3108         [view endSelectionChange];
3109         [view reloadInputViews];
3110     });
3111
3112 }
3113
3114 - (void)_becomeFirstResponderWithSelectionMovingForward:(BOOL)selectingForward completionHandler:(void (^)(BOOL didBecomeFirstResponder))completionHandler
3115 {
3116     auto completionHandlerCopy = Block_copy(completionHandler);
3117     RetainPtr<WKContentView> view = self;
3118     _page->setInitialFocus(selectingForward, false, WebKit::WebKeyboardEvent(), [view, completionHandlerCopy](WebKit::CallbackBase::Error) {
3119         BOOL didBecomeFirstResponder = view->_assistedNodeInformation.elementType != InputType::None && [view becomeFirstResponder];
3120         completionHandlerCopy(didBecomeFirstResponder);
3121         Block_release(completionHandlerCopy);
3122     });
3123 }
3124
3125 - (WebCore::Color)_tapHighlightColorForFastClick:(BOOL)forFastClick
3126 {
3127     ASSERT(_showDebugTapHighlightsForFastClicking);
3128     return forFastClick ? WebCore::Color(0, 225, 0, 127) : WebCore::Color(225, 0, 0, 127);
3129 }
3130
3131 - (void)_setDoubleTapGesturesEnabled:(BOOL)enabled
3132 {
3133     if (enabled && ![_doubleTapGestureRecognizer isEnabled]) {
3134         // The first tap recognized after re-enabling double tap gestures will not wait for the
3135         // second tap before committing. To fix this, we use a new double tap gesture recognizer.
3136         [self removeGestureRecognizer:_doubleTapGestureRecognizer.get()];
3137         [_doubleTapGestureRecognizer setDelegate:nil];
3138         [self _createAndConfigureDoubleTapGestureRecognizer];
3139     }
3140
3141     if (_showDebugTapHighlightsForFastClicking && !enabled)
3142         _tapHighlightInformation.color = [self _tapHighlightColorForFastClick:YES];
3143
3144     [_doubleTapGestureRecognizer setEnabled:enabled];
3145     [_nonBlockingDoubleTapGestureRecognizer setEnabled:!enabled];
3146     [self _resetIsDoubleTapPending];
3147 }
3148
3149 - (void)accessoryAutoFill
3150 {
3151     id <_WKInputDelegate> inputDelegate = [_webView _inputDelegate];
3152     if ([inputDelegate respondsToSelector:@selector(_webView:accessoryViewCustomButtonTappedInFormInputSession:)])
3153         [inputDelegate _webView:_webView accessoryViewCustomButtonTappedInFormInputSession:_formInputSession.get()];
3154 }
3155
3156 - (void)accessoryClear
3157 {
3158     _page->setAssistedNodeValue(String());
3159 }
3160
3161 - (void)_updateAccessory
3162 {
3163     [_formAccessoryView setNextEnabled:_assistedNodeInformation.hasNextNode];
3164     [_formAccessoryView setPreviousEnabled:_assistedNodeInformation.hasPreviousNode];
3165
3166     if (currentUserInterfaceIdiomIsPad())
3167         [_formAccessoryView setClearVisible:NO];
3168     else {
3169         switch (_assistedNodeInformation.elementType) {
3170         case InputType::Date:
3171         case InputType::Month:
3172         case InputType::DateTimeLocal:
3173         case InputType::Time:
3174             [_formAccessoryView setClearVisible:YES];
3175             break;
3176         default:
3177             [_formAccessoryView setClearVisible:NO];
3178             break;
3179         }
3180     }
3181
3182     // FIXME: hide or show the AutoFill button as needed.
3183 }
3184
3185 // Keyboard interaction
3186 // UITextInput protocol implementation
3187
3188 - (BOOL)_allowAnimatedUpdateSelectionRectViews
3189 {
3190     return NO;
3191 }
3192
3193 - (void)beginSelectionChange
3194 {
3195     [self.inputDelegate selectionWillChange:self];
3196 }
3197
3198 - (void)endSelectionChange
3199 {
3200     [self.inputDelegate selectionDidChange:self];
3201 }
3202
3203 - (void)insertTextSuggestion:(UITextSuggestion *)textSuggestion
3204 {
3205     // FIXME: Replace NSClassFromString with actual class as soon as UIKit submitted the new class into the iOS SDK.
3206     if ([textSuggestion isKindOfClass:[UITextAutofillSuggestion class]]) {
3207         _page->autofillLoginCredentials([(UITextAutofillSuggestion *)textSuggestion username], [(UITextAutofillSuggestion *)textSuggestion password]);
3208         return;
3209     }
3210     id <_WKInputDelegate> inputDelegate = [_webView _inputDelegate];
3211     if ([inputDelegate respondsToSelector:@selector(_webView:insertTextSuggestion:inInputSession:)])
3212         [inputDelegate _webView:_webView insertTextSuggestion:textSuggestion inInputSession:_formInputSession.get()];
3213 }
3214
3215 - (NSString *)textInRange:(UITextRange *)range
3216 {
3217     return nil;
3218 }
3219
3220 - (void)replaceRange:(UITextRange *)range withText:(NSString *)text
3221 {
3222 }
3223
3224 - (UITextRange *)selectedTextRange
3225 {
3226     if (_page->editorState().selectionIsNone || _page->editorState().isMissingPostLayoutData)
3227         return nil;
3228     // UIKit does not expect caret selections in noneditable content.
3229     if (!_page->editorState().isContentEditable && !_page->editorState().selectionIsRange)
3230         return nil;
3231     
3232     auto& postLayoutEditorStateData = _page->editorState().postLayoutData();
3233     FloatRect startRect = postLayoutEditorStateData.caretRectAtStart;
3234     FloatRect endRect = postLayoutEditorStateData.caretRectAtEnd;
3235     double inverseScale = [self inverseScale];
3236     // We want to keep the original caret width, while the height scales with
3237     // the content taking orientation into account.
3238     // We achieve this by scaling the width with the inverse
3239     // scale factor. This way, when it is converted from the content view
3240     // the width remains unchanged.
3241     if (startRect.width() < startRect.height())
3242         startRect.setWidth(startRect.width() * inverseScale);
3243     else
3244         startRect.setHeight(startRect.height() * inverseScale);
3245     if (endRect.width() < endRect.height()) {
3246         double delta = endRect.width();
3247         endRect.setWidth(endRect.width() * inverseScale);
3248         delta = endRect.width() - delta;
3249         endRect.move(delta, 0);
3250     } else {
3251         double delta = endRect.height();
3252         endRect.setHeight(endRect.height() * inverseScale);
3253         delta = endRect.height() - delta;
3254         endRect.move(0, delta);
3255     }
3256     return [WKTextRange textRangeWithState:_page->editorState().selectionIsNone
3257                                    isRange:_page->editorState().selectionIsRange
3258                                 isEditable:_page->editorState().isContentEditable
3259                                  startRect:startRect
3260                                    endRect:endRect
3261                             selectionRects:[self webSelectionRects]
3262                         selectedTextLength:postLayoutEditorStateData.selectedTextLength];
3263 }
3264
3265 - (CGRect)caretRectForPosition:(UITextPosition *)position
3266 {
3267     return ((WKTextPosition *)position).positionRect;
3268 }
3269
3270 - (NSArray *)selectionRectsForRange:(UITextRange *)range
3271 {
3272     return [WKTextSelectionRect textSelectionRectsWithWebRects:((WKTextRange *)range).selectionRects];
3273 }
3274
3275 - (void)setSelectedTextRange:(UITextRange *)range
3276 {
3277     if (range)
3278         return;
3279 #if !PLATFORM(IOSMAC)
3280     if (!hasAssistedNode(_assistedNodeInformation))
3281         return;
3282 #endif
3283     [self clearSelection];
3284 }
3285
3286 - (BOOL)hasMarkedText
3287 {
3288     return [_markedText length];
3289 }
3290
3291 - (NSString *)markedText
3292 {
3293     return _markedText.get();
3294 }
3295
3296 - (UITextRange *)markedTextRange
3297 {
3298     return nil;
3299 }
3300
3301 - (NSDictionary *)markedTextStyle
3302 {
3303     return nil;
3304 }
3305
3306 - (void)setMarkedTextStyle:(NSDictionary *)styleDictionary
3307 {
3308 }
3309
3310 - (void)setMarkedText:(NSString *)markedText selectedRange:(NSRange)selectedRange
3311 {
3312     _markedText = markedText;
3313     _page->setCompositionAsync(markedText, Vector<WebCore::CompositionUnderline>(), selectedRange, EditingRange());
3314 }
3315
3316 - (void)unmarkText
3317 {
3318     _markedText = nil;
3319     _page->confirmCompositionAsync();
3320 }
3321
3322 - (UITextPosition *)beginningOfDocument
3323 {
3324     return nil;
3325 }
3326
3327 - (UITextPosition *)endOfDocument
3328 {
3329     return nil;
3330 }
3331
3332 - (UITextRange *)textRangeFromPosition:(UITextPosition *)fromPosition toPosition:(UITextPosition *)toPosition
3333 {
3334     return nil;
3335 }
3336
3337 - (UITextPosition *)positionFromPosition:(UITextPosition *)position offset:(NSInteger)offset
3338 {
3339     return nil;
3340 }
3341
3342 - (UITextPosition *)positionFromPosition:(UITextPosition *)position inDirection:(UITextLayoutDirection)direction offset:(NSInteger)offset
3343 {
3344     return nil;
3345 }
3346
3347 - (NSComparisonResult)comparePosition:(UITextPosition *)position toPosition:(UITextPosition *)other
3348 {
3349     return NSOrderedSame;
3350 }
3351
3352 - (NSInteger)offsetFromPosition:(UITextPosition *)from toPosition:(UITextPosition *)toPosition
3353 {
3354     return 0;
3355 }
3356
3357 - (id <UITextInputTokenizer>)tokenizer
3358 {
3359     return nil;
3360 }
3361
3362 - (UITextPosition *)positionWithinRange:(UITextRange *)range farthestInDirection:(UITextLayoutDirection)direction
3363 {
3364     return nil;
3365 }
3366
3367 - (UITextRange *)characterRangeByExtendingPosition:(UITextPosition *)position inDirection:(UITextLayoutDirection)direction
3368 {
3369     return nil;
3370 }
3371
3372 - (UITextWritingDirection)baseWritingDirectionForPosition:(UITextPosition *)position inDirection:(UITextStorageDirection)direction
3373 {
3374     return UITextWritingDirectionLeftToRight;
3375 }
3376
3377 - (void)setBaseWritingDirection:(UITextWritingDirection)writingDirection forRange:(UITextRange *)range
3378 {
3379 }
3380
3381 - (CGRect)firstRectForRange:(UITextRange *)range
3382 {
3383     return CGRectZero;
3384 }
3385
3386 /* Hit testing. */
3387 - (UITextPosition *)closestPositionToPoint:(CGPoint)point
3388 {
3389 #if PLATFORM(IOSMAC)
3390     InteractionInformationRequest request(roundedIntPoint(point));
3391     [self requestAsynchronousPositionInformationUpdate:request];
3392     if ([self _currentPositionInformationIsApproximatelyValidForRequest:request] && _positionInformation.isSelectable)
3393         return [WKTextPosition textPositionWithRect:_positionInformation.caretRect];
3394 #endif
3395     return nil;