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