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