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