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