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