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