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