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