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