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