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