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