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