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