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