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