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