[WTF] Add makeUnique<T>, which ensures T is fast-allocated, makeUnique / makeUniqueWi...
[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 = makeUnique<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 = makeUnique<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 = makeUnique<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     if (!self.isFocusingElement) {
2254         if (gesture == UIWKGestureDoubleTap) {
2255             // Don't allow double tap text gestures in noneditable content.
2256             return NO;
2257         }
2258
2259         if (gesture == UIWKGestureOneFingerTap) {
2260             ASSERT(_suppressNonEditableSingleTapTextInteractionCount >= 0);
2261             if (_suppressNonEditableSingleTapTextInteractionCount > 0)
2262                 return NO;
2263
2264             switch ([_textSelectionAssistant loupeGesture].state) {
2265             case UIGestureRecognizerStateBegan:
2266             case UIGestureRecognizerStateChanged:
2267             case UIGestureRecognizerStateEnded: {
2268                 // Avoid handling one-finger taps while the web process is processing certain selection changes.
2269                 // This works around a scenario where UIKeyboardImpl blocks the main thread while handling a one-
2270                 // finger tap, which subsequently prevents the UI process from handling any incoming IPC messages.
2271                 return NO;
2272             }
2273             default:
2274                 break;
2275             }
2276         }
2277     }
2278
2279     WebKit::InteractionInformationRequest request(WebCore::roundedIntPoint(point));
2280     if (![self ensurePositionInformationIsUpToDate:request])
2281         return NO;
2282
2283 #if ENABLE(DRAG_SUPPORT)
2284     if (_positionInformation.hasSelectionAtPosition && gesture == UIWKGestureLoupe && self._allowedDragSourceActions & WebCore::DragSourceActionSelection) {
2285         // If the position might initiate a drag, we don't want to change the selection.
2286         return NO;
2287     }
2288 #endif
2289
2290 #if ENABLE(DATALIST_ELEMENT)
2291     if (_positionInformation.preventTextInteraction)
2292         return NO;
2293 #endif
2294
2295     // If we're currently focusing an editable element, only allow the selection to move within that focused element.
2296     if (self.isFocusingElement)
2297         return _positionInformation.nodeAtPositionIsFocusedElement;
2298
2299     // If we're selecting something, don't activate highlight.
2300     if (gesture == UIWKGestureLoupe && [self hasSelectablePositionAtPoint:point])
2301         [self _cancelLongPressGestureRecognizer];
2302     
2303     // Otherwise, if we're using a text interaction assistant outside of editing purposes (e.g. the selection mode
2304     // is character granularity) then allow text selection.
2305     return YES;
2306 }
2307
2308 - (NSArray *)webSelectionRectsForSelectionRects:(const Vector<WebCore::SelectionRect>&)selectionRects
2309 {
2310     unsigned size = selectionRects.size();
2311     if (!size)
2312         return nil;
2313
2314     NSMutableArray *webRects = [NSMutableArray arrayWithCapacity:size];
2315     for (unsigned i = 0; i < size; i++) {
2316         const WebCore::SelectionRect& coreRect = selectionRects[i];
2317         WebSelectionRect *webRect = [WebSelectionRect selectionRect];
2318         webRect.rect = coreRect.rect();
2319         webRect.writingDirection = coreRect.direction() == WebCore::TextDirection::LTR ? WKWritingDirectionLeftToRight : WKWritingDirectionRightToLeft;
2320         webRect.isLineBreak = coreRect.isLineBreak();
2321         webRect.isFirstOnLine = coreRect.isFirstOnLine();
2322         webRect.isLastOnLine = coreRect.isLastOnLine();
2323         webRect.containsStart = coreRect.containsStart();
2324         webRect.containsEnd = coreRect.containsEnd();
2325         webRect.isInFixedPosition = coreRect.isInFixedPosition();
2326         webRect.isHorizontal = coreRect.isHorizontal();
2327         [webRects addObject:webRect];
2328     }
2329
2330     return webRects;
2331 }
2332
2333 - (NSArray *)webSelectionRects
2334 {
2335     if (_page->editorState().isMissingPostLayoutData || _page->editorState().selectionIsNone)
2336         return nil;
2337     const auto& selectionRects = _page->editorState().postLayoutData().selectionRects;
2338     return [self webSelectionRectsForSelectionRects:selectionRects];
2339 }
2340
2341 - (void)_highlightLongPressRecognized:(UILongPressGestureRecognizer *)gestureRecognizer
2342 {
2343     ASSERT(gestureRecognizer == _highlightLongPressGestureRecognizer);
2344     [self _resetIsDoubleTapPending];
2345
2346     _lastInteractionLocation = gestureRecognizer.startPoint;
2347
2348     switch ([gestureRecognizer state]) {
2349     case UIGestureRecognizerStateBegan:
2350         _longPressCanClick = YES;
2351         cancelPotentialTapIfNecessary(self);
2352         _page->tapHighlightAtPosition([gestureRecognizer startPoint], ++_latestTapID);
2353         _isTapHighlightIDValid = YES;
2354         break;
2355     case UIGestureRecognizerStateEnded:
2356         if (_longPressCanClick && _positionInformation.isElement) {
2357             [self _attemptClickAtLocation:gestureRecognizer.startPoint modifierFlags:gestureRecognizerModifierFlags(gestureRecognizer)];
2358             [self _finishInteraction];
2359         } else
2360             [self _cancelInteraction];
2361         _longPressCanClick = NO;
2362         break;
2363     case UIGestureRecognizerStateCancelled:
2364         [self _cancelInteraction];
2365         _longPressCanClick = NO;
2366         break;
2367     default:
2368         break;
2369     }
2370 }
2371
2372 - (void)_doubleTapRecognizedForDoubleClick:(UITapGestureRecognizer *)gestureRecognizer
2373 {
2374     _page->handleDoubleTapForDoubleClickAtPoint(WebCore::IntPoint(gestureRecognizer.location), WebKit::webEventModifierFlags(gestureRecognizerModifierFlags(gestureRecognizer)), _layerTreeTransactionIdAtLastTouchStart);
2375 }
2376
2377 - (void)_twoFingerSingleTapGestureRecognized:(UITapGestureRecognizer *)gestureRecognizer
2378 {
2379     _isTapHighlightIDValid = YES;
2380     _isExpectingFastSingleTapCommit = YES;
2381     _page->handleTwoFingerTapAtPoint(WebCore::roundedIntPoint(gestureRecognizer.centroid), WebKit::webEventModifierFlags(gestureRecognizerModifierFlags(gestureRecognizer)), ++_latestTapID);
2382 }
2383
2384 - (void)_stylusSingleTapRecognized:(UITapGestureRecognizer *)gestureRecognizer
2385 {
2386     ASSERT(_webView._stylusTapGestureShouldCreateEditableImage);
2387     ASSERT(gestureRecognizer == _stylusSingleTapGestureRecognizer);
2388     _page->handleStylusSingleTapAtPoint(WebCore::roundedIntPoint(gestureRecognizer.location), ++_latestTapID);
2389 }
2390
2391 - (void)_longPressRecognized:(UILongPressGestureRecognizer *)gestureRecognizer
2392 {
2393     ASSERT(gestureRecognizer == _longPressGestureRecognizer);
2394     [self _resetIsDoubleTapPending];
2395     [self _cancelTouchEventGestureRecognizer];
2396     _page->didRecognizeLongPress();
2397
2398     _lastInteractionLocation = gestureRecognizer.startPoint;
2399
2400     if ([gestureRecognizer state] == UIGestureRecognizerStateBegan) {
2401         SEL action = [self _actionForLongPress];
2402         if (action) {
2403             [self performSelector:action];
2404             [self _cancelLongPressGestureRecognizer];
2405         }
2406     }
2407 }
2408
2409 - (void)_endPotentialTapAndEnableDoubleTapGesturesIfNecessary
2410 {
2411     if (_webView._allowsDoubleTapGestures)
2412         [self _setDoubleTapGesturesEnabled:YES];
2413
2414     _potentialTapInProgress = NO;
2415 }
2416
2417 - (void)_singleTapIdentified:(UITapGestureRecognizer *)gestureRecognizer
2418 {
2419     ASSERT(gestureRecognizer == _singleTapGestureRecognizer);
2420     ASSERT(!_potentialTapInProgress);
2421     [self _resetIsDoubleTapPending];
2422
2423     bool shouldRequestMagnificationInformation = _page->preferences().fasterClicksEnabled();
2424     if (shouldRequestMagnificationInformation)
2425         RELEASE_LOG(ViewGestures, "Single tap identified. Request details on potential zoom. (%p)", self);
2426
2427     _page->potentialTapAtPosition(gestureRecognizer.location, shouldRequestMagnificationInformation, ++_latestTapID);
2428     _potentialTapInProgress = YES;
2429     _isTapHighlightIDValid = YES;
2430     _isExpectingFastSingleTapCommit = !_doubleTapGestureRecognizer.get().enabled;
2431 }
2432
2433 static void cancelPotentialTapIfNecessary(WKContentView* contentView)
2434 {
2435     if (contentView->_potentialTapInProgress) {
2436         [contentView _endPotentialTapAndEnableDoubleTapGesturesIfNecessary];
2437         [contentView _cancelInteraction];
2438         contentView->_page->cancelPotentialTap();
2439     }
2440 }
2441
2442 - (void)_singleTapDidReset:(UITapGestureRecognizer *)gestureRecognizer
2443 {
2444     ASSERT(gestureRecognizer == _singleTapGestureRecognizer);
2445     cancelPotentialTapIfNecessary(self);
2446 #if ENABLE(POINTER_EVENTS)
2447     if (auto* singleTapTouchIdentifier = [_singleTapGestureRecognizer lastActiveTouchIdentifier]) {
2448         WebCore::PointerID pointerId = [singleTapTouchIdentifier unsignedIntValue];
2449         if (m_commitPotentialTapPointerId != pointerId)
2450             _page->touchWithIdentifierWasRemoved(pointerId);
2451     }
2452 #endif
2453 }
2454
2455 - (void)_doubleTapDidFail:(UITapGestureRecognizer *)gestureRecognizer
2456 {
2457     RELEASE_LOG(ViewGestures, "Double tap was not recognized. (%p)", self);
2458     ASSERT(gestureRecognizer == _doubleTapGestureRecognizer);
2459 }
2460
2461 - (void)_commitPotentialTapFailed
2462 {
2463 #if ENABLE(POINTER_EVENTS)
2464     _page->touchWithIdentifierWasRemoved(m_commitPotentialTapPointerId);
2465     m_commitPotentialTapPointerId = 0;
2466 #endif
2467
2468     [self _cancelInteraction];
2469     
2470     [self _resetInputViewDeferral];
2471 }
2472
2473 - (void)_didNotHandleTapAsClick:(const WebCore::IntPoint&)point
2474 {
2475     [self _resetInputViewDeferral];
2476
2477     // FIXME: we should also take into account whether or not the UI delegate
2478     // has handled this notification.
2479 #if ENABLE(DATA_DETECTION)
2480     if (_hasValidPositionInformation && point == _positionInformation.request.point && _positionInformation.isDataDetectorLink) {
2481         [self _showDataDetectorsSheet];
2482         return;
2483     }
2484 #endif
2485
2486     if (!_isDoubleTapPending)
2487         return;
2488
2489     _smartMagnificationController->handleSmartMagnificationGesture(_lastInteractionLocation);
2490     _isDoubleTapPending = NO;
2491 }
2492
2493 - (void)_didCompleteSyntheticClick
2494 {
2495 #if ENABLE(POINTER_EVENTS)
2496     _page->touchWithIdentifierWasRemoved(m_commitPotentialTapPointerId);
2497     m_commitPotentialTapPointerId = 0;
2498 #endif
2499
2500     RELEASE_LOG(ViewGestures, "Synthetic click completed. (%p)", self);
2501     [self _resetInputViewDeferral];
2502 }
2503
2504 - (void)_singleTapRecognized:(UITapGestureRecognizer *)gestureRecognizer
2505 {
2506     ASSERT(gestureRecognizer == _singleTapGestureRecognizer);
2507
2508     if (![self isFirstResponder])
2509         [self becomeFirstResponder];
2510
2511     ASSERT(_potentialTapInProgress);
2512
2513     _lastInteractionLocation = gestureRecognizer.location;
2514
2515     [self _endPotentialTapAndEnableDoubleTapGesturesIfNecessary];
2516
2517     if (_hasTapHighlightForPotentialTap) {
2518         [self _showTapHighlight];
2519         _hasTapHighlightForPotentialTap = NO;
2520     }
2521
2522     [_inputPeripheral endEditing];
2523
2524     RELEASE_LOG(ViewGestures, "Single tap recognized - commit potential tap (%p)", self);
2525
2526     WebCore::PointerID pointerId = WebCore::mousePointerID;
2527 #if ENABLE(POINTER_EVENTS)
2528     if (auto* singleTapTouchIdentifier = [_singleTapGestureRecognizer lastActiveTouchIdentifier]) {
2529         pointerId = [singleTapTouchIdentifier unsignedIntValue];
2530         m_commitPotentialTapPointerId = pointerId;
2531     }
2532 #endif
2533     _page->commitPotentialTap(WebKit::webEventModifierFlags(gestureRecognizerModifierFlags(gestureRecognizer)), _layerTreeTransactionIdAtLastTouchStart, pointerId);
2534
2535     if (!_isExpectingFastSingleTapCommit)
2536         [self _finishInteraction];
2537 }
2538
2539 - (void)_doubleTapRecognized:(UITapGestureRecognizer *)gestureRecognizer
2540 {
2541     RELEASE_LOG(ViewGestures, "Identified a double tap (%p)", self);
2542
2543     [self _resetIsDoubleTapPending];
2544     _lastInteractionLocation = gestureRecognizer.location;
2545
2546     _smartMagnificationController->handleSmartMagnificationGesture(gestureRecognizer.location);
2547 }
2548
2549 - (void)_resetIsDoubleTapPending
2550 {
2551     _isDoubleTapPending = NO;
2552 }
2553
2554 - (void)_nonBlockingDoubleTapRecognized:(UITapGestureRecognizer *)gestureRecognizer
2555 {
2556     _lastInteractionLocation = gestureRecognizer.location;
2557     _isDoubleTapPending = YES;
2558 }
2559
2560 - (void)_twoFingerDoubleTapRecognized:(UITapGestureRecognizer *)gestureRecognizer
2561 {
2562     [self _resetIsDoubleTapPending];
2563     _lastInteractionLocation = gestureRecognizer.location;
2564
2565     _smartMagnificationController->handleResetMagnificationGesture(gestureRecognizer.location);
2566 }
2567
2568 - (void)_attemptClickAtLocation:(CGPoint)location modifierFlags:(UIKeyModifierFlags)modifierFlags
2569 {
2570     if (![self isFirstResponder])
2571         [self becomeFirstResponder];
2572
2573     [_inputPeripheral endEditing];
2574     _page->handleTap(location, WebKit::webEventModifierFlags(modifierFlags), _layerTreeTransactionIdAtLastTouchStart);
2575 }
2576
2577 - (void)setUpTextSelectionAssistant
2578 {
2579     if (!_textSelectionAssistant)
2580         _textSelectionAssistant = adoptNS([[UIWKTextInteractionAssistant alloc] initWithView:self]);
2581     else {
2582         // Reset the gesture recognizers in case editability has changed.
2583         [_textSelectionAssistant setGestureRecognizers];
2584     }
2585 }
2586
2587 - (void)pasteWithCompletionHandler:(void (^)(void))completionHandler
2588 {
2589     _page->executeEditCommand("Paste"_s, { }, [completion = makeBlockPtr(completionHandler)] (auto) {
2590         if (completion)
2591             completion();
2592     });
2593 }
2594
2595 - (void)clearSelection
2596 {
2597     [self _elementDidBlur];
2598     _page->clearSelection();
2599 }
2600
2601 - (void)_positionInformationDidChange:(const WebKit::InteractionInformationAtPosition&)info
2602 {
2603     _outstandingPositionInformationRequest = WTF::nullopt;
2604
2605     WebKit::InteractionInformationAtPosition newInfo = info;
2606     newInfo.mergeCompatibleOptionalInformation(_positionInformation);
2607
2608     _positionInformation = newInfo;
2609     _hasValidPositionInformation = _positionInformation.canBeValid;
2610     if (_actionSheetAssistant)
2611         [_actionSheetAssistant updateSheetPosition];
2612     [self _invokeAndRemovePendingHandlersValidForCurrentPositionInformation];
2613 }
2614
2615 - (void)_willStartScrollingOrZooming
2616 {
2617     [_textSelectionAssistant willStartScrollingOverflow];
2618     _page->setIsScrollingOrZooming(true);
2619
2620 #if PLATFORM(WATCHOS)
2621     [_focusedFormControlView disengageFocusedFormControlNavigation];
2622 #endif
2623 }
2624
2625 - (void)scrollViewWillStartPanOrPinchGesture
2626 {
2627     _page->hideValidationMessage();
2628
2629     [_keyboardScrollingAnimator willStartInteractiveScroll];
2630
2631     _canSendTouchEventsAsynchronously = YES;
2632 }
2633
2634 - (void)_didEndScrollingOrZooming
2635 {
2636     if (!_needsDeferredEndScrollingSelectionUpdate) {
2637         [_textSelectionAssistant didEndScrollingOverflow];
2638     }
2639     _page->setIsScrollingOrZooming(false);
2640
2641 #if ENABLE(POINTER_EVENTS)
2642     [self _resetPanningPreventionFlags];
2643 #endif
2644
2645 #if PLATFORM(WATCHOS)
2646     [_focusedFormControlView engageFocusedFormControlNavigation];
2647 #endif
2648 }
2649
2650 - (BOOL)requiresAccessoryView
2651 {
2652     if ([_formInputSession accessoryViewShouldNotShow])
2653         return NO;
2654
2655     if ([_formInputSession customInputAccessoryView])
2656         return YES;
2657
2658     switch (_focusedElementInformation.elementType) {
2659     case WebKit::InputType::None:
2660     case WebKit::InputType::Drawing:
2661         return NO;
2662     case WebKit::InputType::Text:
2663     case WebKit::InputType::Password:
2664     case WebKit::InputType::Search:
2665     case WebKit::InputType::Email:
2666     case WebKit::InputType::URL:
2667     case WebKit::InputType::Phone:
2668     case WebKit::InputType::Number:
2669     case WebKit::InputType::NumberPad:
2670     case WebKit::InputType::ContentEditable:
2671     case WebKit::InputType::TextArea:
2672     case WebKit::InputType::Select:
2673     case WebKit::InputType::Date:
2674     case WebKit::InputType::DateTime:
2675     case WebKit::InputType::DateTimeLocal:
2676     case WebKit::InputType::Month:
2677     case WebKit:: InputType::Week:
2678     case WebKit::InputType::Time:
2679 #if ENABLE(INPUT_TYPE_COLOR)
2680     case WebKit::InputType::Color:
2681 #endif
2682         return !currentUserInterfaceIdiomIsPad();
2683     }
2684 }
2685
2686 - (UITextInputAssistantItem *)inputAssistantItem
2687 {
2688     return [_webView inputAssistantItem];
2689 }
2690
2691 - (UITextInputAssistantItem *)inputAssistantItemForWebView
2692 {
2693     return [super inputAssistantItem];
2694 }
2695
2696 - (UIView *)inputAccessoryView
2697 {
2698     return [_webView inputAccessoryView];
2699 }
2700
2701 - (UIView *)inputAccessoryViewForWebView
2702 {
2703     if (![self requiresAccessoryView])
2704         return nil;
2705
2706     return [_formInputSession customInputAccessoryView] ?: self.formAccessoryView;
2707 }
2708
2709 - (NSArray *)supportedPasteboardTypesForCurrentSelection
2710 {
2711     if (_page->editorState().selectionIsNone)
2712         return nil;
2713     
2714     static NSMutableArray *richTypes = nil;
2715     static NSMutableArray *plainTextTypes = nil;
2716     if (!plainTextTypes) {
2717         plainTextTypes = [[NSMutableArray alloc] init];
2718         [plainTextTypes addObject:(id)kUTTypeURL];
2719         [plainTextTypes addObjectsFromArray:UIPasteboardTypeListString];
2720
2721         richTypes = [[NSMutableArray alloc] init];
2722         [richTypes addObject:WebCore::WebArchivePboardType];
2723         [richTypes addObjectsFromArray:UIPasteboardTypeListImage];
2724         [richTypes addObjectsFromArray:plainTextTypes];
2725     }
2726
2727     return (_page->editorState().isContentRichlyEditable) ? richTypes : plainTextTypes;
2728 }
2729
2730 #define FORWARD_ACTION_TO_WKWEBVIEW(_action) \
2731     - (void)_action:(id)sender \
2732     { \
2733         SEL action = @selector(_action:);\
2734         [self _willPerformAction:action sender:sender];\
2735         [_webView _action:sender]; \
2736         [self _didPerformAction:action sender:sender];\
2737     }
2738
2739 FOR_EACH_WKCONTENTVIEW_ACTION(FORWARD_ACTION_TO_WKWEBVIEW)
2740 FOR_EACH_PRIVATE_WKCONTENTVIEW_ACTION(FORWARD_ACTION_TO_WKWEBVIEW)
2741
2742 #undef FORWARD_ACTION_TO_WKWEBVIEW
2743
2744 - (void)_lookupForWebView:(id)sender
2745 {
2746     RetainPtr<WKContentView> view = self;
2747     _page->getSelectionContext([view](const String& selectedText, const String& textBefore, const String& textAfter, WebKit::CallbackBase::Error error) {
2748         if (error != WebKit::CallbackBase::Error::None)
2749             return;
2750         if (!selectedText)
2751             return;
2752
2753         auto& editorState = view->_page->editorState();
2754         auto& postLayoutData = editorState.postLayoutData();
2755         CGRect presentationRect;
2756         if (editorState.selectionIsRange && !postLayoutData.selectionRects.isEmpty())
2757             presentationRect = postLayoutData.selectionRects[0].rect();
2758         else
2759             presentationRect = postLayoutData.caretRectAtStart;
2760         
2761         String selectionContext = textBefore + selectedText + textAfter;
2762         NSRange selectedRangeInContext = NSMakeRange(textBefore.length(), selectedText.length());
2763
2764         if (auto textSelectionAssistant = view->_textSelectionAssistant)
2765             [textSelectionAssistant lookup:selectionContext withRange:selectedRangeInContext fromRect:presentationRect];
2766     });
2767 }
2768
2769 - (void)_shareForWebView:(id)sender
2770 {
2771     RetainPtr<WKContentView> view = self;
2772     _page->getSelectionOrContentsAsString([view](const String& string, WebKit::CallbackBase::Error error) {
2773         if (error != WebKit::CallbackBase::Error::None)
2774             return;
2775         if (!string)
2776             return;
2777
2778         CGRect presentationRect = view->_page->editorState().postLayoutData().selectionRects[0].rect();
2779
2780         if (view->_textSelectionAssistant)
2781             [view->_textSelectionAssistant showShareSheetFor:string fromRect:presentationRect];
2782     });
2783 }
2784
2785 - (void)_addShortcutForWebView:(id)sender
2786 {
2787     if (_textSelectionAssistant)
2788         [_textSelectionAssistant showTextServiceFor:[self selectedText] fromRect:_page->editorState().postLayoutData().selectionRects[0].rect()];
2789 }
2790
2791 - (NSString *)selectedText
2792 {
2793     return (NSString *)_page->editorState().postLayoutData().wordAtSelection;
2794 }
2795
2796 - (void)makeTextWritingDirectionNaturalForWebView:(id)sender
2797 {
2798     // Match platform behavior on iOS as well as legacy WebKit behavior by modifying the
2799     // base (paragraph) writing direction rather than the inline direction.
2800     _page->setBaseWritingDirection(WebCore::WritingDirection::Natural);
2801 }
2802
2803 - (void)makeTextWritingDirectionLeftToRightForWebView:(id)sender
2804 {
2805     _page->setBaseWritingDirection(WebCore::WritingDirection::LeftToRight);
2806 }
2807
2808 - (void)makeTextWritingDirectionRightToLeftForWebView:(id)sender
2809 {
2810     _page->setBaseWritingDirection(WebCore::WritingDirection::RightToLeft);
2811 }
2812
2813 - (BOOL)isReplaceAllowed
2814 {
2815     return _page->editorState().postLayoutData().isReplaceAllowed;
2816 }
2817
2818 - (void)replaceText:(NSString *)text withText:(NSString *)word
2819 {
2820     _page->replaceSelectedText(text, word);
2821 }
2822
2823 - (void)selectWordBackward
2824 {
2825     _page->selectWordBackward();
2826 }
2827
2828 - (void)_promptForReplaceForWebView:(id)sender
2829 {
2830     const auto& wordAtSelection = _page->editorState().postLayoutData().wordAtSelection;
2831     if (wordAtSelection.isEmpty())
2832         return;
2833
2834     [_textSelectionAssistant scheduleReplacementsForText:wordAtSelection];
2835 }
2836
2837 - (void)_transliterateChineseForWebView:(id)sender
2838 {
2839     [_textSelectionAssistant scheduleChineseTransliterationForText:_page->editorState().postLayoutData().wordAtSelection];
2840 }
2841
2842 - (void)replaceForWebView:(id)sender
2843 {
2844     [[UIKeyboardImpl sharedInstance] replaceText:sender];
2845 }
2846
2847 #define WEBCORE_COMMAND_FOR_WEBVIEW(command) \
2848     - (void)_ ## command ## ForWebView:(id)sender { _page->executeEditCommand(#command ## _s); } \
2849     - (void)command ## ForWebView:(id)sender { [self _ ## command ## ForWebView:sender]; }
2850 WEBCORE_COMMAND_FOR_WEBVIEW(insertOrderedList);
2851 WEBCORE_COMMAND_FOR_WEBVIEW(insertUnorderedList);
2852 WEBCORE_COMMAND_FOR_WEBVIEW(insertNestedOrderedList);
2853 WEBCORE_COMMAND_FOR_WEBVIEW(insertNestedUnorderedList);
2854 WEBCORE_COMMAND_FOR_WEBVIEW(indent);
2855 WEBCORE_COMMAND_FOR_WEBVIEW(outdent);
2856 WEBCORE_COMMAND_FOR_WEBVIEW(alignLeft);
2857 WEBCORE_COMMAND_FOR_WEBVIEW(alignRight);
2858 WEBCORE_COMMAND_FOR_WEBVIEW(alignCenter);
2859 WEBCORE_COMMAND_FOR_WEBVIEW(alignJustified);
2860 WEBCORE_COMMAND_FOR_WEBVIEW(pasteAndMatchStyle);
2861 #undef WEBCORE_COMMAND_FOR_WEBVIEW
2862
2863 - (void)_increaseListLevelForWebView:(id)sender
2864 {
2865     _page->increaseListLevel();
2866 }
2867
2868 - (void)_decreaseListLevelForWebView:(id)sender
2869 {
2870     _page->decreaseListLevel();
2871 }
2872
2873 - (void)_changeListTypeForWebView:(id)sender
2874 {
2875     _page->changeListType();
2876 }
2877
2878 - (void)_toggleStrikeThroughForWebView:(id)sender
2879 {
2880     _page->executeEditCommand("StrikeThrough"_s);
2881 }
2882
2883 - (void)increaseSizeForWebView:(id)sender
2884 {
2885     _page->executeEditCommand("FontSizeDelta"_s, "1"_s);
2886 }
2887
2888 - (void)decreaseSizeForWebView:(id)sender
2889 {
2890     _page->executeEditCommand("FontSizeDelta"_s, "-1"_s);
2891 }
2892
2893 - (void)_setFontForWebView:(UIFont *)font sender:(id)sender
2894 {
2895     WebCore::FontChanges changes;
2896     changes.setFontFamily(font.familyName);
2897     changes.setFontName(font.fontName);
2898     changes.setFontSize(font.pointSize);
2899     changes.setBold(font.traits & UIFontTraitBold);
2900     changes.setItalic(font.traits & UIFontTraitItalic);
2901     _page->changeFont(WTFMove(changes));
2902 }
2903
2904 - (void)_setFontSizeForWebView:(CGFloat)fontSize sender:(id)sender
2905 {
2906     WebCore::FontChanges changes;
2907     changes.setFontSize(fontSize);
2908     _page->changeFont(WTFMove(changes));
2909 }
2910
2911 - (void)_setTextColorForWebView:(UIColor *)color sender:(id)sender
2912 {
2913     _page->executeEditCommand("ForeColor"_s, WebCore::Color(color.CGColor).serialized());
2914 }
2915
2916 - (void)toggleStrikeThroughForWebView:(id)sender
2917 {
2918     [self _toggleStrikeThroughForWebView:sender];
2919 }
2920
2921 - (NSDictionary *)textStylingAtPosition:(UITextPosition *)position inDirection:(UITextStorageDirection)direction
2922 {
2923     if (!position || !_page->editorState().isContentRichlyEditable)
2924         return nil;
2925
2926     NSMutableDictionary* result = [NSMutableDictionary dictionary];
2927
2928     auto typingAttributes = _page->editorState().postLayoutData().typingAttributes;
2929     CTFontSymbolicTraits symbolicTraits = 0;
2930     if (typingAttributes & WebKit::AttributeBold)
2931         symbolicTraits |= kCTFontBoldTrait;
2932     if (typingAttributes & WebKit::AttributeItalics)
2933         symbolicTraits |= kCTFontTraitItalic;
2934
2935     // We chose a random font family and size.
2936     // What matters are the traits but the caller expects a font object
2937     // in the dictionary for NSFontAttributeName.
2938     RetainPtr<CTFontDescriptorRef> fontDescriptor = adoptCF(CTFontDescriptorCreateWithNameAndSize(CFSTR("Helvetica"), 10));
2939     if (symbolicTraits)
2940         fontDescriptor = adoptCF(CTFontDescriptorCreateCopyWithSymbolicTraits(fontDescriptor.get(), symbolicTraits, symbolicTraits));
2941     
2942     RetainPtr<CTFontRef> font = adoptCF(CTFontCreateWithFontDescriptor(fontDescriptor.get(), 10, nullptr));
2943     if (font)
2944         [result setObject:(id)font.get() forKey:NSFontAttributeName];
2945     
2946     if (typingAttributes & WebKit::AttributeUnderline)
2947         [result setObject:[NSNumber numberWithInt:NSUnderlineStyleSingle] forKey:NSUnderlineStyleAttributeName];
2948
2949     return result;
2950 }
2951
2952 - (UIColor *)insertionPointColor
2953 {
2954     return [self.textInputTraits insertionPointColor];
2955 }
2956
2957 - (UIColor *)selectionBarColor
2958 {
2959     return [self.textInputTraits selectionBarColor];
2960 }
2961
2962 - (UIColor *)selectionHighlightColor
2963 {
2964     return [self.textInputTraits selectionHighlightColor];
2965 }
2966
2967 - (void)_updateInteractionTintColor
2968 {
2969     UIColor *tintColor = ^{
2970         if (!_webView.configuration._textInteractionGesturesEnabled)
2971             return [UIColor clearColor];
2972
2973         if (!_page->editorState().isMissingPostLayoutData) {
2974             WebCore::Color caretColor = _page->editorState().postLayoutData().caretColor;
2975             if (caretColor.isValid())
2976                 return [UIColor colorWithCGColor:cachedCGColor(caretColor)];
2977         }
2978         
2979         return [self _inheritedInteractionTintColor];    
2980     }();
2981
2982     [_traits _setColorsToMatchTintColor:tintColor];
2983 }
2984
2985 - (void)tintColorDidChange
2986 {
2987     [super tintColorDidChange];
2988
2989     BOOL shouldUpdateTextSelection = self.isFirstResponder && [self canShowNonEmptySelectionView];
2990     if (shouldUpdateTextSelection)
2991         [_textSelectionAssistant deactivateSelection];
2992     [self _updateInteractionTintColor];
2993     if (shouldUpdateTextSelection)
2994         [_textSelectionAssistant activateSelection];
2995 }
2996
2997 - (BOOL)canPerformAction:(SEL)action withSender:(id)sender
2998 {
2999     if (_domPasteRequestHandler)
3000         return action == @selector(paste:);
3001
3002     // These are UIKit IPI selectors. We don't want to forward them to the web view.
3003     auto editorState = _page->editorState();
3004     if (action == @selector(_moveDown:withHistory:) || action == @selector(_moveLeft:withHistory:) || action == @selector(_moveRight:withHistory:)
3005         || action == @selector(_moveToEndOfDocument:withHistory:) || action == @selector(_moveToEndOfLine:withHistory:) || action == @selector(_moveToEndOfParagraph:withHistory:)
3006         || action == @selector(_moveToEndOfWord:withHistory:) || action == @selector(_moveToStartOfDocument:withHistory:) || action == @selector(_moveToStartOfLine:withHistory:)
3007         || action == @selector(_moveToStartOfParagraph:withHistory:) || action == @selector(_moveToStartOfWord:withHistory:) || action == @selector(_moveUp:withHistory:))
3008         return !editorState.selectionIsNone;
3009
3010     if (action == @selector(_deleteByWord) || action == @selector(_deleteForwardAndNotify:) || action == @selector(_deleteToEndOfParagraph) || action == @selector(_deleteToStartOfLine)
3011         || action == @selector(_transpose))
3012         return editorState.isContentEditable;
3013
3014     return [_webView canPerformAction:action withSender:sender];
3015 }
3016
3017 - (BOOL)canPerformActionForWebView:(SEL)action withSender:(id)sender
3018 {
3019     if (_domPasteRequestHandler)
3020         return action == @selector(paste:);
3021
3022     if (action == @selector(_nextAccessoryTab:))
3023         return hasFocusedElement(_focusedElementInformation) && _focusedElementInformation.hasNextNode;
3024     if (action == @selector(_previousAccessoryTab:))
3025         return hasFocusedElement(_focusedElementInformation) && _focusedElementInformation.hasPreviousNode;
3026
3027     auto editorState = _page->editorState();
3028     if (action == @selector(_showTextStyleOptions:))
3029         return editorState.isContentRichlyEditable && editorState.selectionIsRange && !_showingTextStyleOptions;
3030     if (_showingTextStyleOptions)
3031         return (action == @selector(toggleBoldface:) || action == @selector(toggleItalics:) || action == @selector(toggleUnderline:));
3032     // FIXME: Some of the following checks should be removed once internal clients move to the underscore-prefixed versions.
3033     if (action == @selector(toggleBoldface:) || action == @selector(toggleItalics:) || action == @selector(toggleUnderline:) || action == @selector(_toggleStrikeThrough:)
3034         || action == @selector(_alignLeft:) || action == @selector(_alignRight:) || action == @selector(_alignCenter:) || action == @selector(_alignJustified:)
3035         || action == @selector(_setTextColor:sender:) || action == @selector(_setFont:sender:) || action == @selector(_setFontSize:sender:)
3036         || action == @selector(_insertOrderedList:) || action == @selector(_insertUnorderedList:) || action == @selector(_insertNestedOrderedList:) || action == @selector(_insertNestedUnorderedList:)
3037         || action == @selector(_increaseListLevel:) || action == @selector(_decreaseListLevel:) || action == @selector(_changeListType:) || action == @selector(_indent:) || action == @selector(_outdent:)
3038         || action == @selector(increaseSize:) || action == @selector(decreaseSize:) || action == @selector(makeTextWritingDirectionNatural:)) {
3039         // 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
3040         // at the outermost indentation level.
3041         return editorState.isContentRichlyEditable;
3042     }
3043     if (action == @selector(cut:))
3044         return !editorState.isInPasswordField && editorState.isContentEditable && editorState.selectionIsRange;
3045     
3046     if (action == @selector(paste:) || action == @selector(_pasteAsQuotation:) || action == @selector(_pasteAndMatchStyle:) || action == @selector(pasteAndMatchStyle:)) {
3047         if (editorState.selectionIsNone || !editorState.isContentEditable)
3048             return NO;
3049         UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
3050         NSArray *types = [self supportedPasteboardTypesForCurrentSelection];
3051         NSIndexSet *indices = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, [pasteboard numberOfItems])];
3052         if ([pasteboard containsPasteboardTypes:types inItemSet:indices])
3053             return YES;
3054
3055 #if PLATFORM(IOS)
3056         if (editorState.isContentRichlyEditable && _webView.configuration._attachmentElementEnabled) {
3057             for (NSItemProvider *itemProvider in pasteboard.itemProviders) {
3058                 auto preferredPresentationStyle = itemProvider.preferredPresentationStyle;
3059                 if (preferredPresentationStyle == UIPreferredPresentationStyleInline)
3060                     continue;
3061
3062                 if (preferredPresentationStyle == UIPreferredPresentationStyleUnspecified && !itemProvider.suggestedName.length)
3063                     continue;
3064
3065                 if (itemProvider.web_fileUploadContentTypes.count)
3066                     return YES;
3067             }
3068         }
3069 #endif // PLATFORM(IOS)
3070
3071         auto focusedDocumentOrigin = editorState.originIdentifierForPasteboard;
3072         if (focusedDocumentOrigin.isEmpty())
3073             return NO;
3074
3075         NSArray *allCustomPasteboardData = [pasteboard dataForPasteboardType:@(WebCore::PasteboardCustomData::cocoaType()) inItemSet:indices];
3076         for (NSData *data in allCustomPasteboardData) {
3077             auto buffer = WebCore::SharedBuffer::create(data);
3078             if (WebCore::PasteboardCustomData::fromSharedBuffer(buffer.get()).origin == focusedDocumentOrigin)
3079                 return YES;
3080         }
3081         return NO;
3082     }
3083
3084     if (action == @selector(copy:)) {
3085         if (editorState.isInPasswordField)
3086             return NO;
3087         return editorState.selectionIsRange;
3088     }
3089
3090     if (action == @selector(_define:)) {
3091         if (editorState.isInPasswordField || !editorState.selectionIsRange)
3092             return NO;
3093
3094         NSUInteger textLength = editorState.postLayoutData().selectedTextLength;
3095         // FIXME: We should be calling UIReferenceLibraryViewController to check if the length is
3096         // acceptable, but the interface takes a string.
3097         // <rdar://problem/15254406>
3098         if (!textLength || textLength > 200)
3099             return NO;
3100
3101 #if !PLATFORM(MACCATALYST)
3102         if ([[getMCProfileConnectionClass() sharedConnection] effectiveBoolValueForSetting:getMCFeatureDefinitionLookupAllowed()] == MCRestrictedBoolExplicitNo)
3103             return NO;
3104 #endif
3105             
3106         return YES;
3107     }
3108
3109     if (action == @selector(_lookup:)) {
3110         if (editorState.isInPasswordField)
3111             return NO;
3112
3113 #if !PLATFORM(MACCATALYST)
3114         if ([[getMCProfileConnectionClass() sharedConnection] effectiveBoolValueForSetting:getMCFeatureDefinitionLookupAllowed()] == MCRestrictedBoolExplicitNo)
3115             return NO;
3116 #endif
3117
3118         return editorState.selectionIsRange;
3119     }
3120
3121     if (action == @selector(_share:)) {
3122         if (editorState.isInPasswordField || !editorState.selectionIsRange)
3123             return NO;
3124
3125         return editorState.postLayoutData().selectedTextLength > 0;
3126     }
3127
3128     if (action == @selector(_addShortcut:)) {
3129         if (editorState.isInPasswordField || !editorState.selectionIsRange)
3130             return NO;
3131
3132         NSString *selectedText = [self selectedText];
3133         if (![selectedText length])
3134             return NO;
3135
3136         if (!UIKeyboardEnabledInputModesAllowOneToManyShortcuts())
3137             return NO;
3138         if (![selectedText _containsCJScripts])
3139             return NO;
3140         return YES;
3141     }
3142
3143     if (action == @selector(_promptForReplace:)) {
3144         if (!editorState.selectionIsRange || !editorState.postLayoutData().isReplaceAllowed || ![[UIKeyboardImpl activeInstance] autocorrectSpellingEnabled])
3145             return NO;
3146         if ([[self selectedText] _containsCJScriptsOnly])
3147             return NO;
3148         return YES;
3149     }
3150
3151     if (action == @selector(_transliterateChinese:)) {
3152         if (!editorState.selectionIsRange || !editorState.postLayoutData().isReplaceAllowed || ![[UIKeyboardImpl activeInstance] autocorrectSpellingEnabled])
3153             return NO;
3154         return UIKeyboardEnabledInputModesAllowChineseTransliterationForText([self selectedText]);
3155     }
3156
3157     if (action == @selector(select:)) {
3158         // Disable select in password fields so that you can't see word boundaries.
3159         return !editorState.isInPasswordField && [self hasContent] && !editorState.selectionIsNone && !editorState.selectionIsRange;
3160     }
3161
3162     if (action == @selector(selectAll:)) {
3163         // By platform convention we don't show Select All in the callout menu for a range selection.
3164         if ([sender isKindOfClass:UIMenuController.class])
3165             return !editorState.selectionIsNone && !editorState.selectionIsRange;
3166 #if USE(UIKIT_KEYBOARD_ADDITIONS)
3167         return YES;
3168 #else
3169         return !editorState.selectionIsNone && self.hasContent;
3170 #endif
3171     }
3172
3173     if (action == @selector(replace:))
3174         return editorState.isContentEditable && !editorState.isInPasswordField;
3175
3176     if (action == @selector(makeTextWritingDirectionLeftToRight:) || action == @selector(makeTextWritingDirectionRightToLeft:)) {
3177         if (!editorState.isContentEditable)
3178             return NO;
3179
3180         auto baseWritingDirection = editorState.postLayoutData().baseWritingDirection;
3181         if (baseWritingDirection == WebCore::WritingDirection::LeftToRight && !UIKeyboardIsRightToLeftInputModeActive()) {
3182             // A keyboard is considered "active" if it is available for the user to switch to. As such, this check prevents
3183             // text direction actions from showing up in the case where a user has only added left-to-right keyboards, and
3184             // is also not editing right-to-left content.
3185             return NO;
3186         }
3187
3188         if (action == @selector(makeTextWritingDirectionLeftToRight:))
3189             return baseWritingDirection != WebCore::WritingDirection::LeftToRight;
3190
3191         return baseWritingDirection != WebCore::WritingDirection::RightToLeft;
3192     }
3193
3194     return [super canPerformAction:action withSender:sender];
3195 }
3196
3197 - (id)targetForAction:(SEL)action withSender:(id)sender
3198 {
3199     return [_webView targetForAction:action withSender:sender];
3200 }
3201
3202 - (id)targetForActionForWebView:(SEL)action withSender:(id)sender
3203 {
3204     return [super targetForAction:action withSender:sender];
3205 }
3206
3207 - (void)_willHideMenu:(NSNotification *)notification
3208 {
3209     [self _handleDOMPasteRequestWithResult:WebCore::DOMPasteAccessResponse::DeniedForGesture];
3210 }
3211
3212 - (void)_didHideMenu:(NSNotification *)notification
3213 {
3214     _showingTextStyleOptions = NO;
3215     [_textSelectionAssistant hideTextStyleOptions];
3216 }
3217
3218 - (void)_keyboardDidRequestDismissal:(NSNotification *)notification
3219 {
3220     if (![self isFirstResponder])
3221         return;
3222     _keyboardDidRequestDismissal = YES;
3223 }
3224
3225 - (void)copyForWebView:(id)sender
3226 {
3227     _page->executeEditCommand("copy"_s);
3228 }
3229
3230 - (void)cutForWebView:(id)sender
3231 {
3232     [self executeEditCommandWithCallback:@"cut"];
3233 }
3234
3235 - (void)pasteForWebView:(id)sender
3236 {
3237     if (sender == UIMenuController.sharedMenuController && [self _handleDOMPasteRequestWithResult:WebCore::DOMPasteAccessResponse::GrantedForGesture])
3238         return;
3239
3240     _page->executeEditCommand("paste"_s);
3241 }
3242
3243 - (void)_pasteAsQuotationForWebView:(id)sender
3244 {
3245     _page->executeEditCommand("PasteAsQuotation"_s);
3246 }
3247
3248 - (void)selectForWebView:(id)sender
3249 {
3250     [_textSelectionAssistant selectWord];
3251     // We cannot use selectWord command, because we want to be able to select the word even when it is the last in the paragraph.
3252     _page->extendSelection(WebCore::WordGranularity);
3253 }
3254
3255 - (void)selectAllForWebView:(id)sender
3256 {
3257     [_textSelectionAssistant selectAll:sender];
3258     _page->selectAll();
3259 }
3260
3261 - (BOOL)shouldSynthesizeKeyEvents
3262 {
3263     if (_focusedElementInformation.shouldSynthesizeKeyEventsForEditing && self.hasHiddenContentEditable)
3264         return true;
3265     return false;
3266 }
3267
3268 - (void)toggleBoldfaceForWebView:(id)sender
3269 {
3270     if (!_page->editorState().isContentRichlyEditable)
3271         return;
3272
3273     [self executeEditCommandWithCallback:@"toggleBold"];
3274
3275     if (self.shouldSynthesizeKeyEvents)
3276         _page->generateSyntheticEditingCommand(WebKit::SyntheticEditingCommandType::ToggleBoldface);
3277 }
3278
3279 - (void)toggleItalicsForWebView:(id)sender
3280 {
3281     if (!_page->editorState().isContentRichlyEditable)
3282         return;
3283
3284     [self executeEditCommandWithCallback:@"toggleItalic"];
3285
3286     if (self.shouldSynthesizeKeyEvents)
3287         _page->generateSyntheticEditingCommand(WebKit::SyntheticEditingCommandType::ToggleItalic);
3288 }
3289
3290 - (void)toggleUnderlineForWebView:(id)sender
3291 {
3292     if (!_page->editorState().isContentRichlyEditable)
3293         return;
3294
3295     [self executeEditCommandWithCallback:@"toggleUnderline"];
3296
3297     if (self.shouldSynthesizeKeyEvents)
3298         _page->generateSyntheticEditingCommand(WebKit::SyntheticEditingCommandType::ToggleUnderline);
3299 }
3300
3301 - (void)_showTextStyleOptionsForWebView:(id)sender
3302 {
3303     _showingTextStyleOptions = YES;
3304     [_textSelectionAssistant showTextStyleOptions];
3305 }
3306
3307 - (void)_showDictionary:(NSString *)text
3308 {
3309     CGRect presentationRect = _page->editorState().postLayoutData().selectionRects[0].rect();
3310     if (_textSelectionAssistant)
3311         [_textSelectionAssistant showDictionaryFor:text fromRect:presentationRect];
3312 }
3313
3314 - (void)_defineForWebView:(id)sender
3315 {
3316 #if !PLATFORM(MACCATALYST)
3317     if ([[getMCProfileConnectionClass() sharedConnection] effectiveBoolValueForSetting:getMCFeatureDefinitionLookupAllowed()] == MCRestrictedBoolExplicitNo)
3318         return;
3319 #endif
3320
3321     RetainPtr<WKContentView> view = self;
3322     _page->getSelectionOrContentsAsString([view](const String& string, WebKit::CallbackBase::Error error) {
3323         if (error != WebKit::CallbackBase::Error::None)
3324             return;
3325         if (!string)
3326             return;
3327
3328         [view _showDictionary:string];
3329     });
3330 }
3331
3332 - (void)accessibilityRetrieveSpeakSelectionContent
3333 {
3334     RetainPtr<WKContentView> view = self;
3335     RetainPtr<WKWebView> webView = _webView;
3336     _page->getSelectionOrContentsAsString([view, webView](const String& string, WebKit::CallbackBase::Error error) {
3337         if (error != WebKit::CallbackBase::Error::None)
3338             return;
3339         [webView _accessibilityDidGetSpeakSelectionContent:string];
3340         if ([view respondsToSelector:@selector(accessibilitySpeakSelectionSetContent:)])
3341             [view accessibilitySpeakSelectionSetContent:string];
3342     });
3343 }
3344
3345 - (void)_accessibilityRetrieveRectsEnclosingSelectionOffset:(NSInteger)offset withGranularity:(UITextGranularity)granularity
3346 {
3347     RetainPtr<WKContentView> view = self;
3348     _page->requestRectsForGranularityWithSelectionOffset(toWKTextGranularity(granularity), offset , [view, offset, granularity](const Vector<WebCore::SelectionRect>& selectionRects, WebKit::CallbackBase::Error error) {
3349         if (error != WebKit::CallbackBase::Error::None)
3350             return;
3351         if ([view respondsToSelector:@selector(_accessibilityDidGetSelectionRects:withGranularity:atOffset:)])
3352             [view _accessibilityDidGetSelectionRects:[view webSelectionRectsForSelectionRects:selectionRects] withGranularity:granularity atOffset:offset];
3353     });
3354 }
3355
3356 - (void)_accessibilityRetrieveRectsAtSelectionOffset:(NSInteger)offset withText:(NSString *)text
3357 {
3358     [self _accessibilityRetrieveRectsAtSelectionOffset:offset withText:text completionHandler:nil];
3359 }
3360
3361 - (void)_accessibilityRetrieveRectsAtSelectionOffset:(NSInteger)offset withText:(NSString *)text completionHandler:(void (^)(const Vector<WebCore::SelectionRect>& rects))completionHandler
3362 {
3363     RetainPtr<WKContentView> view = self;
3364     _page->requestRectsAtSelectionOffsetWithText(offset, text, [view, offset, capturedCompletionHandler = makeBlockPtr(completionHandler)](const Vector<WebCore::SelectionRect>& selectionRects, WebKit::CallbackBase::Error error) {
3365         if (capturedCompletionHandler)
3366             capturedCompletionHandler(selectionRects);
3367
3368         if (error != WebKit::CallbackBase::Error::None)
3369             return;
3370         if ([view respondsToSelector:@selector(_accessibilityDidGetSelectionRects:withGranularity:atOffset:)])
3371             [view _accessibilityDidGetSelectionRects:[view webSelectionRectsForSelectionRects:selectionRects] withGranularity:UITextGranularityWord atOffset:offset];
3372     });
3373 }
3374
3375 - (void)_accessibilityStoreSelection
3376 {
3377     _page->storeSelectionForAccessibility(true);
3378 }
3379
3380 - (void)_accessibilityClearSelection
3381 {
3382     _page->storeSelectionForAccessibility(false);
3383 }
3384
3385 - (BOOL)_handleDOMPasteRequestWithResult:(WebCore::DOMPasteAccessResponse)response
3386 {
3387     if (auto pasteHandler = WTFMove(_domPasteRequestHandler)) {
3388         [self hideGlobalMenuController];
3389         pasteHandler(response);