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