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