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