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