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