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