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