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