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