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