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