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