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