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