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