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