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