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