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