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