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