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