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