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