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