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