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