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