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