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