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