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