Tapping and holding a link should have a share option
[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/WebEvent.h>
73 #import <WebKit/WebSelectionRect.h> // FIXME: WK2 should not include WebKit headers!
74 #import <WebKitSystemInterfaceIOS.h>
75 #import <wtf/RetainPtr.h>
76
77 @interface UIEvent(UIEventInternal)
78 @property (nonatomic, assign) UIKeyboardInputFlags _inputFlags;
79 @end
80
81 using namespace WebCore;
82 using namespace WebKit;
83
84 namespace WebKit {
85
86 WKSelectionDrawingInfo::WKSelectionDrawingInfo()
87     : type(SelectionType::None)
88 {
89 }
90
91 WKSelectionDrawingInfo::WKSelectionDrawingInfo(const EditorState& editorState)
92 {
93     if (editorState.selectionIsNone) {
94         type = SelectionType::None;
95         return;
96     }
97
98     if (editorState.isInPlugin) {
99         type = SelectionType::Plugin;
100         return;
101     }
102
103     type = SelectionType::Range;
104     auto& postLayoutData = editorState.postLayoutData();
105     caretRect = postLayoutData.caretRectAtEnd;
106     selectionRects = postLayoutData.selectionRects;
107 }
108
109 inline bool operator==(const WKSelectionDrawingInfo& a, const WKSelectionDrawingInfo& b)
110 {
111     if (a.type != b.type)
112         return false;
113
114     if (a.type == WKSelectionDrawingInfo::SelectionType::Range) {
115         if (a.caretRect != b.caretRect)
116             return false;
117
118         if (a.selectionRects.size() != b.selectionRects.size())
119             return false;
120
121         for (unsigned i = 0; i < a.selectionRects.size(); ++i) {
122             if (a.selectionRects[i].rect() != b.selectionRects[i].rect())
123                 return false;
124         }
125     }
126
127     return true;
128 }
129
130 inline bool operator!=(const WKSelectionDrawingInfo& a, const WKSelectionDrawingInfo& b)
131 {
132     return !(a == b);
133 }
134
135 } // namespace WebKit
136
137 static const float highlightDelay = 0.12;
138 static const float tapAndHoldDelay  = 0.75;
139 const CGFloat minimumTapHighlightRadius = 2.0;
140
141 @interface WKTextRange : UITextRange {
142     CGRect _startRect;
143     CGRect _endRect;
144     BOOL _isNone;
145     BOOL _isRange;
146     BOOL _isEditable;
147     NSArray *_selectionRects;
148     NSUInteger _selectedTextLength;
149 }
150 @property (nonatomic) CGRect startRect;
151 @property (nonatomic) CGRect endRect;
152 @property (nonatomic) BOOL isNone;
153 @property (nonatomic) BOOL isRange;
154 @property (nonatomic) BOOL isEditable;
155 @property (nonatomic) NSUInteger selectedTextLength;
156 @property (copy, nonatomic) NSArray *selectionRects;
157
158 + (WKTextRange *)textRangeWithState:(BOOL)isNone isRange:(BOOL)isRange isEditable:(BOOL)isEditable startRect:(CGRect)startRect endRect:(CGRect)endRect selectionRects:(NSArray *)selectionRects selectedTextLength:(NSUInteger)selectedTextLength;
159
160 @end
161
162 @interface WKTextPosition : UITextPosition {
163     CGRect _positionRect;
164 }
165
166 @property (nonatomic) CGRect positionRect;
167
168 + (WKTextPosition *)textPositionWithRect:(CGRect)positionRect;
169
170 @end
171
172 @interface WKTextSelectionRect : UITextSelectionRect
173
174 @property (nonatomic, retain) WebSelectionRect *webRect;
175
176 + (NSArray *)textSelectionRectsWithWebRects:(NSArray *)webRects;
177
178 @end
179
180 @interface WKAutocorrectionRects : UIWKAutocorrectionRects
181 + (WKAutocorrectionRects *)autocorrectionRectsWithRects:(CGRect)firstRect lastRect:(CGRect)lastRect;
182 @end
183
184 @interface WKAutocorrectionContext : UIWKAutocorrectionContext
185 + (WKAutocorrectionContext *)autocorrectionContextWithData:(NSString *)beforeText markedText:(NSString *)markedText selectedText:(NSString *)selectedText afterText:(NSString *)afterText selectedRangeInMarkedText:(NSRange)range;
186 @end
187
188 @interface UITextInteractionAssistant (UITextInteractionAssistant_Internal)
189 // FIXME: this needs to be moved from the internal header to the private.
190 - (id)initWithView:(UIResponder <UITextInput> *)view;
191 - (void)selectWord;
192 - (void)scheduleReanalysis;
193 @end
194
195 @interface UITextInteractionAssistant (StagingToRemove)
196 - (void)scheduleReplacementsForText:(NSString *)text;
197 - (void)showTextServiceFor:(NSString *)selectedTerm fromRect:(CGRect)presentationRect;
198 - (void)scheduleChineseTransliterationForText:(NSString *)text;
199 - (void)showShareSheetFor:(NSString *)selectedTerm fromRect:(CGRect)presentationRect;
200 - (void)lookup:(NSString *)textWithContext fromRect:(CGRect)presentationRect;
201 @end
202
203 @interface UIWKSelectionAssistant (StagingToRemove)
204 - (void)showTextServiceFor:(NSString *)selectedTerm fromRect:(CGRect)presentationRect;
205 - (void)showShareSheetFor:(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 (_positionInformation.clickableElementName == "IMG")
1053         return @selector(_showImageSheet);
1054     else if (_positionInformation.clickableElementName == "A") {
1055         NSURL *targetURL = [NSURL URLWithString:_positionInformation.url];
1056         if ([[getDDDetectionControllerClass() tapAndHoldSchemes] containsObject:[targetURL scheme]])
1057             return @selector(_showDataDetectorsSheet);
1058         return @selector(_showLinkSheet);
1059     }
1060     return nil;
1061 }
1062
1063 - (void)ensurePositionInformationIsUpToDate:(CGPoint)point
1064 {
1065     if (!_hasValidPositionInformation || roundedIntPoint(point) != _positionInformation.point) {
1066         _page->getPositionInformation(roundedIntPoint(point), _positionInformation);
1067         _hasValidPositionInformation = YES;
1068     }
1069 }
1070
1071 - (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
1072 {
1073     CGPoint point = [gestureRecognizer locationInView:self];
1074
1075     if (gestureRecognizer == _highlightLongPressGestureRecognizer
1076         || gestureRecognizer == _doubleTapGestureRecognizer
1077         || gestureRecognizer == _twoFingerDoubleTapGestureRecognizer
1078         || gestureRecognizer == _singleTapGestureRecognizer) {
1079
1080         if (_textSelectionAssistant) {
1081             // Request information about the position with sync message.
1082             // If the assisted node is the same, prevent the gesture.
1083             _page->getPositionInformation(roundedIntPoint(point), _positionInformation);
1084             _hasValidPositionInformation = YES;
1085             if (_positionInformation.nodeAtPositionIsAssistedNode)
1086                 return NO;
1087         }
1088     }
1089
1090     if (gestureRecognizer == _highlightLongPressGestureRecognizer) {
1091         if (_textSelectionAssistant) {
1092             // This is a different node than the assisted one.
1093             // Prevent the gesture if there is no node.
1094             // Allow the gesture if it is a node that wants highlight or if there is an action for it.
1095             if (_positionInformation.clickableElementName.isNull())
1096                 return NO;
1097             return [self _actionForLongPress] != nil;
1098         } else {
1099             // We still have no idea about what is at the location.
1100             // Send and async message to find out.
1101             _hasValidPositionInformation = NO;
1102             _page->requestPositionInformation(roundedIntPoint(point));
1103             return YES;
1104         }
1105     }
1106
1107     if (gestureRecognizer == _longPressGestureRecognizer) {
1108         // Use the information retrieved with one of the previous calls
1109         // to gestureRecognizerShouldBegin.
1110         // Force a sync call if not ready yet.
1111         [self ensurePositionInformationIsUpToDate:point];
1112
1113         if (_textSelectionAssistant) {
1114             // Prevent the gesture if it is the same node.
1115             if (_positionInformation.nodeAtPositionIsAssistedNode)
1116                 return NO;
1117         } else {
1118             // Prevent the gesture if there is no action for the node.
1119             return [self _actionForLongPress] != nil;
1120         }
1121     }
1122
1123     return YES;
1124 }
1125
1126 - (void)_cancelInteraction
1127 {
1128     _isTapHighlightIDValid = NO;
1129     [_highlightView removeFromSuperview];
1130 }
1131
1132 - (void)_finishInteraction
1133 {
1134     _isTapHighlightIDValid = NO;
1135     CGFloat tapHighlightFadeDuration = _showDebugTapHighlightsForFastClicking ? 0.25 : 0.1;
1136     [UIView animateWithDuration:tapHighlightFadeDuration
1137                      animations:^{
1138                          [_highlightView layer].opacity = 0;
1139                      }
1140                      completion:^(BOOL finished){
1141                          if (finished)
1142                              [_highlightView removeFromSuperview];
1143                      }];
1144 }
1145
1146 - (BOOL)hasSelectablePositionAtPoint:(CGPoint)point
1147 {
1148     if (_inspectorNodeSearchEnabled)
1149         return NO;
1150
1151     [self ensurePositionInformationIsUpToDate:point];
1152     return _positionInformation.isSelectable;
1153 }
1154
1155 - (BOOL)pointIsNearMarkedText:(CGPoint)point
1156 {
1157     [self ensurePositionInformationIsUpToDate:point];
1158     return _positionInformation.isNearMarkedText;
1159 }
1160
1161 - (BOOL)pointIsInAssistedNode:(CGPoint)point
1162 {
1163     [self ensurePositionInformationIsUpToDate:point];
1164     return _positionInformation.nodeAtPositionIsAssistedNode;
1165 }
1166
1167 - (NSArray *)webSelectionRects
1168 {
1169     if (_page->editorState().selectionIsNone)
1170         return nil;
1171     const auto& selectionRects = _page->editorState().postLayoutData().selectionRects;
1172     unsigned size = selectionRects.size();
1173     if (!size)
1174         return nil;
1175
1176     NSMutableArray *webRects = [NSMutableArray arrayWithCapacity:size];
1177     for (unsigned i = 0; i < size; i++) {
1178         const WebCore::SelectionRect& coreRect = selectionRects[i];
1179         WebSelectionRect *webRect = [WebSelectionRect selectionRect];
1180         webRect.rect = coreRect.rect();
1181         webRect.writingDirection = coreRect.direction() == LTR ? WKWritingDirectionLeftToRight : WKWritingDirectionRightToLeft;
1182         webRect.isLineBreak = coreRect.isLineBreak();
1183         webRect.isFirstOnLine = coreRect.isFirstOnLine();
1184         webRect.isLastOnLine = coreRect.isLastOnLine();
1185         webRect.containsStart = coreRect.containsStart();
1186         webRect.containsEnd = coreRect.containsEnd();
1187         webRect.isInFixedPosition = coreRect.isInFixedPosition();
1188         webRect.isHorizontal = coreRect.isHorizontal();
1189         [webRects addObject:webRect];
1190     }
1191
1192     return webRects;
1193 }
1194
1195 - (void)_highlightLongPressRecognized:(UILongPressGestureRecognizer *)gestureRecognizer
1196 {
1197     ASSERT(gestureRecognizer == _highlightLongPressGestureRecognizer);
1198
1199     _lastInteractionLocation = gestureRecognizer.startPoint;
1200
1201     switch ([gestureRecognizer state]) {
1202     case UIGestureRecognizerStateBegan:
1203         _highlightLongPressCanClick = YES;
1204         cancelPotentialTapIfNecessary(self);
1205         _page->tapHighlightAtPosition([gestureRecognizer startPoint], ++_latestTapID);
1206         _isTapHighlightIDValid = YES;
1207         break;
1208     case UIGestureRecognizerStateEnded:
1209         if (_highlightLongPressCanClick && !_positionInformation.clickableElementName.isEmpty()) {
1210             [self _attemptClickAtLocation:[gestureRecognizer startPoint]];
1211             [self _finishInteraction];
1212         } else
1213             [self _cancelInteraction];
1214         _highlightLongPressCanClick = NO;
1215         break;
1216     case UIGestureRecognizerStateCancelled:
1217         [self _cancelInteraction];
1218         _highlightLongPressCanClick = NO;
1219         break;
1220     default:
1221         break;
1222     }
1223 }
1224
1225 - (void)_longPressRecognized:(UILongPressGestureRecognizer *)gestureRecognizer
1226 {
1227     ASSERT(gestureRecognizer == _longPressGestureRecognizer);
1228
1229     _lastInteractionLocation = gestureRecognizer.startPoint;
1230
1231     if ([gestureRecognizer state] == UIGestureRecognizerStateBegan) {
1232         SEL action = [self _actionForLongPress];
1233         if (action) {
1234             [self performSelector:action];
1235             [self _cancelLongPressGestureRecognizer];
1236         }
1237     }
1238 }
1239
1240 - (void)_endPotentialTapAndEnableDoubleTapGesturesIfNecessary
1241 {
1242     if (_webView._allowsDoubleTapGestures)
1243         [self _setDoubleTapGesturesEnabled:YES];
1244
1245     _potentialTapInProgress = NO;
1246 }
1247
1248 - (void)_singleTapRecognized:(UITapGestureRecognizer *)gestureRecognizer
1249 {
1250     ASSERT(gestureRecognizer == _singleTapGestureRecognizer);
1251     ASSERT(!_potentialTapInProgress);
1252
1253     _page->potentialTapAtPosition(gestureRecognizer.location, ++_latestTapID);
1254     _potentialTapInProgress = YES;
1255     _isTapHighlightIDValid = YES;
1256     _isExpectingFastSingleTapCommit = !_doubleTapGestureRecognizer.get().enabled;
1257 }
1258
1259 static void cancelPotentialTapIfNecessary(WKContentView* contentView)
1260 {
1261     if (contentView->_potentialTapInProgress) {
1262         [contentView _endPotentialTapAndEnableDoubleTapGesturesIfNecessary];
1263         [contentView _cancelInteraction];
1264         contentView->_page->cancelPotentialTap();
1265     }
1266 }
1267
1268 - (void)_singleTapDidReset:(UITapGestureRecognizer *)gestureRecognizer
1269 {
1270     ASSERT(gestureRecognizer == _singleTapGestureRecognizer);
1271     cancelPotentialTapIfNecessary(self);
1272 }
1273
1274 - (void)_commitPotentialTapFailed
1275 {
1276     [self _cancelInteraction];
1277 }
1278
1279 - (void)_singleTapCommited:(UITapGestureRecognizer *)gestureRecognizer
1280 {
1281     ASSERT(gestureRecognizer == _singleTapGestureRecognizer);
1282
1283     if (![self isFirstResponder])
1284         [self becomeFirstResponder];
1285
1286     if (_webSelectionAssistant && ![_webSelectionAssistant shouldHandleSingleTapAtPoint:gestureRecognizer.location]) {
1287         [self _singleTapDidReset:gestureRecognizer];
1288         return;
1289     }
1290
1291     ASSERT(_potentialTapInProgress);
1292
1293     // We don't want to clear the selection if it is in editable content.
1294     // The selection could have been set by autofocusing on page load and not
1295     // reflected in the UI process since the user was not interacting with the page.
1296     if (!_page->editorState().isContentEditable)
1297         [_webSelectionAssistant clearSelection];
1298
1299     _lastInteractionLocation = gestureRecognizer.location;
1300
1301     [self _endPotentialTapAndEnableDoubleTapGesturesIfNecessary];
1302
1303     if (_hasTapHighlightForPotentialTap) {
1304         [self _showTapHighlight];
1305         _hasTapHighlightForPotentialTap = NO;
1306     }
1307
1308     _page->commitPotentialTap();
1309
1310     if (!_isExpectingFastSingleTapCommit)
1311         [self _finishInteraction];
1312 }
1313
1314 - (void)_doubleTapRecognized:(UITapGestureRecognizer *)gestureRecognizer
1315 {
1316     _lastInteractionLocation = gestureRecognizer.location;
1317
1318     _smartMagnificationController->handleSmartMagnificationGesture(gestureRecognizer.location);
1319 }
1320
1321 - (void)_twoFingerDoubleTapRecognized:(UITapGestureRecognizer *)gestureRecognizer
1322 {
1323     _lastInteractionLocation = gestureRecognizer.location;
1324
1325     _smartMagnificationController->handleResetMagnificationGesture(gestureRecognizer.location);
1326 }
1327
1328 - (void)_attemptClickAtLocation:(CGPoint)location
1329 {
1330     if (![self isFirstResponder])
1331         [self becomeFirstResponder];
1332
1333     _page->handleTap(location);
1334 }
1335
1336 - (void)useSelectionAssistantWithMode:(UIWebSelectionMode)selectionMode
1337 {
1338     if (selectionMode == UIWebSelectionModeWeb) {
1339         if (_textSelectionAssistant) {
1340             [_textSelectionAssistant deactivateSelection];
1341             _textSelectionAssistant = nil;
1342         }
1343         if (!_webSelectionAssistant)
1344             _webSelectionAssistant = adoptNS([[UIWKSelectionAssistant alloc] initWithView:self]);
1345     } else if (selectionMode == UIWebSelectionModeTextOnly) {
1346         if (_webSelectionAssistant)
1347             _webSelectionAssistant = nil;
1348
1349         if (!_textSelectionAssistant)
1350             _textSelectionAssistant = adoptNS([[UIWKTextInteractionAssistant alloc] initWithView:self]);
1351         else {
1352             // Reset the gesture recognizers in case editibility has changed.
1353             [_textSelectionAssistant setGestureRecognizers];
1354         }
1355
1356         if (self.isFirstResponder)
1357             [_textSelectionAssistant activateSelection];
1358     }
1359 }
1360
1361 - (void)clearSelection
1362 {
1363     _page->clearSelection();
1364 }
1365
1366 - (void)_positionInformationDidChange:(const InteractionInformationAtPosition&)info
1367 {
1368     _positionInformation = info;
1369     _hasValidPositionInformation = YES;
1370     if (_actionSheetAssistant)
1371         [_actionSheetAssistant updateSheetPosition];
1372 }
1373
1374 - (void)_willStartScrollingOrZooming
1375 {
1376     [_webSelectionAssistant willStartScrollingOrZoomingPage];
1377     [_textSelectionAssistant willStartScrollingOverflow];
1378 }
1379
1380 - (void)scrollViewWillStartPanOrPinchGesture
1381 {
1382     _canSendTouchEventsAsynchronously = YES;
1383 }
1384
1385 - (void)_didEndScrollingOrZooming
1386 {
1387     [_webSelectionAssistant didEndScrollingOrZoomingPage];
1388     [_textSelectionAssistant didEndScrollingOverflow];
1389 }
1390
1391 - (BOOL)requiresAccessoryView
1392 {
1393     switch (_assistedNodeInformation.elementType) {
1394     case InputType::None:
1395         return NO;
1396     case InputType::Text:
1397     case InputType::Password:
1398     case InputType::Search:
1399     case InputType::Email:
1400     case InputType::URL:
1401     case InputType::Phone:
1402     case InputType::Number:
1403     case InputType::NumberPad:
1404         return !UICurrentUserInterfaceIdiomIsPad();
1405     case InputType::ContentEditable:
1406     case InputType::TextArea:
1407         return !UICurrentUserInterfaceIdiomIsPad();
1408     case InputType::Select:
1409     case InputType::Date:
1410     case InputType::DateTime:
1411     case InputType::DateTimeLocal:
1412     case InputType::Month:
1413     case InputType::Week:
1414     case InputType::Time:
1415         return !UICurrentUserInterfaceIdiomIsPad();
1416     }
1417 }
1418
1419 - (void)_ensureFormAccessoryView
1420 {
1421     if (_formAccessoryView)
1422         return;
1423
1424     _formAccessoryView = adoptNS([[UIWebFormAccessory alloc] initWithInputAssistantItem:self.inputAssistantItem]);
1425     [_formAccessoryView setDelegate:self];
1426 }
1427
1428 - (UIView *)inputAccessoryView
1429 {
1430     if (![self requiresAccessoryView])
1431         return nil;
1432
1433     return self.formAccessoryView;
1434 }
1435
1436 - (NSArray *)supportedPasteboardTypesForCurrentSelection
1437 {
1438     if (_page->editorState().selectionIsNone)
1439         return nil;
1440     
1441     static NSMutableArray *richTypes = nil;
1442     static NSMutableArray *plainTextTypes = nil;
1443     if (!plainTextTypes) {
1444         plainTextTypes = [[NSMutableArray alloc] init];
1445         [plainTextTypes addObject:(id)kUTTypeURL];
1446         [plainTextTypes addObjectsFromArray:UIPasteboardTypeListString];
1447
1448         richTypes = [[NSMutableArray alloc] init];
1449         [richTypes addObject:WebArchivePboardType];
1450         [richTypes addObjectsFromArray:UIPasteboardTypeListImage];
1451         [richTypes addObjectsFromArray:plainTextTypes];
1452     }
1453
1454     return (_page->editorState().isContentRichlyEditable) ? richTypes : plainTextTypes;
1455 }
1456
1457 - (void)_lookup:(CGPoint)point
1458 {
1459     RetainPtr<WKContentView> view = self;
1460     _page->getLookupContextAtPoint(WebCore::IntPoint(point), [view](const String& string, CallbackBase::Error error) {
1461         if (error != CallbackBase::Error::None)
1462             return;
1463         if (!string)
1464             return;
1465         
1466         CGRect presentationRect = view->_page->editorState().selectionIsRange ? view->_page->editorState().postLayoutData().selectionRects[0].rect() : view->_page->editorState().postLayoutData().caretRectAtStart;
1467         
1468         if (view->_textSelectionAssistant && [view->_textSelectionAssistant respondsToSelector:@selector(lookup:fromRect:)])
1469             [view->_textSelectionAssistant lookup:string fromRect:presentationRect];
1470         else if (view->_webSelectionAssistant && [view->_webSelectionAssistant respondsToSelector:@selector(lookup:fromRect:)])
1471             [view->_webSelectionAssistant lookup:string fromRect:presentationRect];
1472     });
1473 }
1474
1475 - (void)_share:(id)sender
1476 {
1477     RetainPtr<WKContentView> view = self;
1478     _page->getSelectionOrContentsAsString([view](const String& string, CallbackBase::Error error) {
1479         if (error != CallbackBase::Error::None)
1480             return;
1481         if (!string)
1482             return;
1483
1484         CGRect presentationRect = view->_page->editorState().postLayoutData().selectionRects[0].rect();
1485
1486         if (view->_textSelectionAssistant && [view->_textSelectionAssistant respondsToSelector:@selector(showShareSheetFor:fromRect:)])
1487             [view->_textSelectionAssistant showShareSheetFor:string fromRect:presentationRect];
1488         else if (view->_webSelectionAssistant && [view->_webSelectionAssistant respondsToSelector:@selector(showShareSheetFor:fromRect:)])
1489             [view->_webSelectionAssistant showShareSheetFor:string fromRect:presentationRect];
1490     });
1491 }
1492
1493 - (void)_addShortcut:(id)sender
1494 {
1495     if (_textSelectionAssistant && [_textSelectionAssistant respondsToSelector:@selector(showTextServiceFor:fromRect:)])
1496         [_textSelectionAssistant showTextServiceFor:[self selectedText] fromRect:_page->editorState().postLayoutData().selectionRects[0].rect()];
1497     else if (_webSelectionAssistant && [_webSelectionAssistant respondsToSelector:@selector(showTextServiceFor:fromRect:)])
1498         [_webSelectionAssistant showTextServiceFor:[self selectedText] fromRect:_page->editorState().postLayoutData().selectionRects[0].rect()];
1499 }
1500
1501 - (NSString *)selectedText
1502 {
1503     return (NSString *)_page->editorState().postLayoutData().wordAtSelection;
1504 }
1505
1506 - (BOOL)isReplaceAllowed
1507 {
1508     return _page->editorState().postLayoutData().isReplaceAllowed;
1509 }
1510
1511 - (void)replaceText:(NSString *)text withText:(NSString *)word
1512 {
1513     _page->replaceSelectedText(text, word);
1514 }
1515
1516 - (void)selectWordBackward
1517 {
1518     _page->selectWordBackward();
1519 }
1520
1521 - (void)_promptForReplace:(id)sender
1522 {
1523     const auto& wordAtSelection = _page->editorState().postLayoutData().wordAtSelection;
1524     if (wordAtSelection.isEmpty())
1525         return;
1526
1527     if ([_textSelectionAssistant respondsToSelector:@selector(scheduleReplacementsForText:)])
1528         [_textSelectionAssistant scheduleReplacementsForText:wordAtSelection];
1529 }
1530
1531 - (void)_transliterateChinese:(id)sender
1532 {
1533     if ([_textSelectionAssistant respondsToSelector:@selector(scheduleChineseTransliterationForText:)])
1534         [_textSelectionAssistant scheduleChineseTransliterationForText:_page->editorState().postLayoutData().wordAtSelection];
1535 }
1536
1537 - (void)_reanalyze:(id)sender
1538 {
1539     [_textSelectionAssistant scheduleReanalysis];
1540 }
1541
1542 - (void)replace:(id)sender
1543 {
1544     [[UIKeyboardImpl sharedInstance] replaceText:sender];
1545 }
1546
1547 - (NSDictionary *)textStylingAtPosition:(UITextPosition *)position inDirection:(UITextStorageDirection)direction
1548 {
1549     if (!position || !_page->editorState().isContentRichlyEditable)
1550         return nil;
1551
1552     NSMutableDictionary* result = [NSMutableDictionary dictionary];
1553
1554     auto typingAttributes = _page->editorState().postLayoutData().typingAttributes;
1555     CTFontSymbolicTraits symbolicTraits = 0;
1556     if (typingAttributes & AttributeBold)
1557         symbolicTraits |= kCTFontBoldTrait;
1558     if (typingAttributes & AttributeItalics)
1559         symbolicTraits |= kCTFontTraitItalic;
1560
1561     // We chose a random font family and size.
1562     // What matters are the traits but the caller expects a font object
1563     // in the dictionary for NSFontAttributeName.
1564     RetainPtr<CTFontDescriptorRef> fontDescriptor = adoptCF(CTFontDescriptorCreateWithNameAndSize(CFSTR("Helvetica"), 10));
1565     if (symbolicTraits)
1566         fontDescriptor = adoptCF(CTFontDescriptorCreateCopyWithSymbolicTraits(fontDescriptor.get(), symbolicTraits, symbolicTraits));
1567     
1568     RetainPtr<CTFontRef> font = adoptCF(CTFontCreateWithFontDescriptor(fontDescriptor.get(), 10, nullptr));
1569     if (font)
1570         [result setObject:(id)font.get() forKey:NSFontAttributeName];
1571     
1572     if (typingAttributes & AttributeUnderline)
1573         [result setObject:[NSNumber numberWithInt:NSUnderlineStyleSingle] forKey:NSUnderlineStyleAttributeName];
1574
1575     return result;
1576 }
1577
1578 - (BOOL)canPerformAction:(SEL)action withSender:(id)sender
1579 {
1580     BOOL hasWebSelection = _webSelectionAssistant && !CGRectIsEmpty(_webSelectionAssistant.get().selectionFrame);
1581
1582     if (action == @selector(_arrowKey:))
1583         return [self isFirstResponder];
1584         
1585     if (action == @selector(_showTextStyleOptions:))
1586         return _page->editorState().isContentRichlyEditable && _page->editorState().selectionIsRange && !_showingTextStyleOptions;
1587     if (_showingTextStyleOptions)
1588         return (action == @selector(toggleBoldface:) || action == @selector(toggleItalics:) || action == @selector(toggleUnderline:));
1589     if (action == @selector(toggleBoldface:) || action == @selector(toggleItalics:) || action == @selector(toggleUnderline:))
1590         return _page->editorState().isContentRichlyEditable;
1591     if (action == @selector(cut:))
1592         return !_page->editorState().isInPasswordField && _page->editorState().isContentEditable && _page->editorState().selectionIsRange;
1593     
1594     if (action == @selector(paste:)) {
1595         if (_page->editorState().selectionIsNone || !_page->editorState().isContentEditable)
1596             return NO;
1597         UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
1598         NSArray *types = [self supportedPasteboardTypesForCurrentSelection];
1599         NSIndexSet *indices = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, [pasteboard numberOfItems])];
1600         return [pasteboard containsPasteboardTypes:types inItemSet:indices];
1601     }
1602
1603     if (action == @selector(copy:)) {
1604         if (_page->editorState().isInPasswordField)
1605             return NO;
1606         return hasWebSelection || _page->editorState().selectionIsRange;
1607     }
1608
1609     if (action == @selector(_define:)) {
1610         if (_page->editorState().isInPasswordField || !(hasWebSelection || _page->editorState().selectionIsRange))
1611             return NO;
1612
1613         NSUInteger textLength = _page->editorState().postLayoutData().selectedTextLength;
1614         // FIXME: We should be calling UIReferenceLibraryViewController to check if the length is
1615         // acceptable, but the interface takes a string.
1616         // <rdar://problem/15254406>
1617         if (!textLength || textLength > 200)
1618             return NO;
1619
1620         if ([[getMCProfileConnectionClass() sharedConnection] effectiveBoolValueForSetting:MCFeatureDefinitionLookupAllowed] == MCRestrictedBoolExplicitNo)
1621             return NO;
1622             
1623         return YES;
1624     }
1625
1626     if (action == @selector(_lookup:)) {
1627         if (_page->editorState().isInPasswordField)
1628             return NO;
1629         
1630         if ([[getMCProfileConnectionClass() sharedConnection] effectiveBoolValueForSetting:MCFeatureDefinitionLookupAllowed] == MCRestrictedBoolExplicitNo)
1631             return NO;
1632         
1633         return YES;
1634     }
1635
1636     if (action == @selector(_share:)) {
1637         if (_page->editorState().isInPasswordField || !(hasWebSelection || _page->editorState().selectionIsRange))
1638             return NO;
1639
1640         NSUInteger textLength = _page->editorState().postLayoutData().selectedTextLength;
1641         // See FIXME above for _define.
1642         if (!textLength || textLength > 200)
1643             return NO;
1644         
1645         return YES;
1646     }
1647
1648     if (action == @selector(_addShortcut:)) {
1649         if (_page->editorState().isInPasswordField || !(hasWebSelection || _page->editorState().selectionIsRange))
1650             return NO;
1651
1652         NSString *selectedText = [self selectedText];
1653         if (![selectedText length])
1654             return NO;
1655
1656         if (!UIKeyboardEnabledInputModesAllowOneToManyShortcuts())
1657             return NO;
1658         if (![selectedText _containsCJScripts])
1659             return NO;
1660         return YES;
1661     }
1662
1663     if (action == @selector(_promptForReplace:)) {
1664         if (!_page->editorState().selectionIsRange || !_page->editorState().postLayoutData().isReplaceAllowed || ![[UIKeyboardImpl activeInstance] autocorrectSpellingEnabled])
1665             return NO;
1666         if ([[self selectedText] _containsCJScriptsOnly])
1667             return NO;
1668         return YES;
1669     }
1670
1671     if (action == @selector(_transliterateChinese:)) {
1672         if (!_page->editorState().selectionIsRange || !_page->editorState().postLayoutData().isReplaceAllowed || ![[UIKeyboardImpl activeInstance] autocorrectSpellingEnabled])
1673             return NO;
1674         return UIKeyboardEnabledInputModesAllowChineseTransliterationForText([self selectedText]);
1675     }
1676
1677     if (action == @selector(_reanalyze:)) {
1678         if (!_page->editorState().selectionIsRange || !_page->editorState().postLayoutData().isReplaceAllowed || ![[UIKeyboardImpl activeInstance] autocorrectSpellingEnabled])
1679             return NO;
1680         return UIKeyboardCurrentInputModeAllowsChineseOrJapaneseReanalysisForText([self selectedText]);
1681     }
1682
1683     if (action == @selector(select:)) {
1684         // Disable select in password fields so that you can't see word boundaries.
1685         return !_page->editorState().isInPasswordField && [self hasContent] && !_page->editorState().selectionIsNone && !_page->editorState().selectionIsRange;
1686     }
1687
1688     if (action == @selector(selectAll:)) {
1689         if (_page->editorState().selectionIsNone || ![self hasContent])
1690             return NO;
1691         if (!_page->editorState().selectionIsRange)
1692             return YES;
1693         // Enable selectAll for non-editable text, where the user can't access
1694         // this command via long-press to get a caret.
1695         if (_page->editorState().isContentEditable)
1696             return NO;
1697         // Don't attempt selectAll with general web content.
1698         if (hasWebSelection)
1699             return NO;
1700         // FIXME: Only enable if the selection doesn't already span the entire document.
1701         return YES;
1702     }
1703
1704     if (action == @selector(replace:))
1705         return _page->editorState().isContentEditable && !_page->editorState().isInPasswordField;
1706
1707     return [super canPerformAction:action withSender:sender];
1708 }
1709
1710 - (void)_resetShowingTextStyle:(NSNotification *)notification
1711 {
1712     _showingTextStyleOptions = NO;
1713     [_textSelectionAssistant hideTextStyleOptions];
1714 }
1715
1716 - (void)copy:(id)sender
1717 {
1718     _page->executeEditCommand(ASCIILiteral("copy"));
1719 }
1720
1721 - (void)cut:(id)sender
1722 {
1723     _page->executeEditCommand(ASCIILiteral("cut"));
1724 }
1725
1726 - (void)paste:(id)sender
1727 {
1728     _page->executeEditCommand(ASCIILiteral("paste"));
1729 }
1730
1731 - (void)select:(id)sender
1732 {
1733     [_textSelectionAssistant selectWord];
1734     // We cannot use selectWord command, because we want to be able to select the word even when it is the last in the paragraph.
1735     _page->extendSelection(WordGranularity);
1736 }
1737
1738 - (void)selectAll:(id)sender
1739 {
1740     [_textSelectionAssistant selectAll:sender];
1741     _page->executeEditCommand(ASCIILiteral("selectAll"));
1742 }
1743
1744 - (void)toggleBoldface:(id)sender
1745 {
1746     if (!_page->editorState().isContentRichlyEditable)
1747         return;
1748
1749     [self executeEditCommandWithCallback:@"toggleBold"];
1750 }
1751
1752 - (void)toggleItalics:(id)sender
1753 {
1754     if (!_page->editorState().isContentRichlyEditable)
1755         return;
1756
1757     [self executeEditCommandWithCallback:@"toggleItalic"];
1758 }
1759
1760 - (void)toggleUnderline:(id)sender
1761 {
1762     if (!_page->editorState().isContentRichlyEditable)
1763         return;
1764
1765     [self executeEditCommandWithCallback:@"toggleUnderline"];
1766 }
1767
1768 - (void)_showTextStyleOptions:(id)sender
1769 {
1770     _showingTextStyleOptions = YES;
1771     [_textSelectionAssistant showTextStyleOptions];
1772 }
1773
1774 - (void)_showDictionary:(NSString *)text
1775 {
1776     CGRect presentationRect = _page->editorState().postLayoutData().selectionRects[0].rect();
1777     if (_textSelectionAssistant)
1778         [_textSelectionAssistant showDictionaryFor:text fromRect:presentationRect];
1779     else
1780         [_webSelectionAssistant showDictionaryFor:text fromRect:presentationRect];
1781 }
1782
1783 - (void)_define:(id)sender
1784 {
1785     if ([[getMCProfileConnectionClass() sharedConnection] effectiveBoolValueForSetting:MCFeatureDefinitionLookupAllowed] == MCRestrictedBoolExplicitNo)
1786         return;
1787
1788     RetainPtr<WKContentView> view = self;
1789     _page->getSelectionOrContentsAsString([view](const String& string, WebKit::CallbackBase::Error error) {
1790         if (error != WebKit::CallbackBase::Error::None)
1791             return;
1792         if (!string)
1793             return;
1794
1795         [view _showDictionary:string];
1796     });
1797 }
1798
1799 - (void)accessibilityRetrieveSpeakSelectionContent
1800 {
1801     RetainPtr<WKContentView> view = self;
1802     _page->getSelectionOrContentsAsString([view](const String& string, WebKit::CallbackBase::Error error) {
1803         if (error != WebKit::CallbackBase::Error::None)
1804             return;
1805         if ([view respondsToSelector:@selector(accessibilitySpeakSelectionSetContent:)])
1806             [view accessibilitySpeakSelectionSetContent:string];
1807     });
1808 }
1809
1810 // UIWKInteractionViewProtocol
1811
1812 static inline GestureType toGestureType(UIWKGestureType gestureType)
1813 {
1814     switch (gestureType) {
1815     case UIWKGestureLoupe:
1816         return GestureType::Loupe;
1817     case UIWKGestureOneFingerTap:
1818         return GestureType::OneFingerTap;
1819     case UIWKGestureTapAndAHalf:
1820         return GestureType::TapAndAHalf;
1821     case UIWKGestureDoubleTap:
1822         return GestureType::DoubleTap;
1823     case UIWKGestureTapAndHalf:
1824         return GestureType::TapAndHalf;
1825     case UIWKGestureDoubleTapInUneditable:
1826         return GestureType::DoubleTapInUneditable;
1827     case UIWKGestureOneFingerTapInUneditable:
1828         return GestureType::OneFingerTapInUneditable;
1829     case UIWKGestureOneFingerTapSelectsAll:
1830         return GestureType::OneFingerTapSelectsAll;
1831     case UIWKGestureOneFingerDoubleTap:
1832         return GestureType::OneFingerDoubleTap;
1833     case UIWKGestureOneFingerTripleTap:
1834         return GestureType::OneFingerTripleTap;
1835     case UIWKGestureTwoFingerSingleTap:
1836         return GestureType::TwoFingerSingleTap;
1837     case UIWKGestureTwoFingerRangedSelectGesture:
1838         return GestureType::TwoFingerRangedSelectGesture;
1839     case UIWKGestureTapOnLinkWithGesture:
1840         return GestureType::TapOnLinkWithGesture;
1841     case UIWKGestureMakeWebSelection:
1842         return GestureType::MakeWebSelection;
1843     case UIWKGesturePhraseBoundary:
1844         return GestureType::PhraseBoundary;
1845     }
1846     ASSERT_NOT_REACHED();
1847     return GestureType::Loupe;
1848 }
1849
1850 static inline UIWKGestureType toUIWKGestureType(GestureType gestureType)
1851 {
1852     switch (gestureType) {
1853     case GestureType::Loupe:
1854         return UIWKGestureLoupe;
1855     case GestureType::OneFingerTap:
1856         return UIWKGestureOneFingerTap;
1857     case GestureType::TapAndAHalf:
1858         return UIWKGestureTapAndAHalf;
1859     case GestureType::DoubleTap:
1860         return UIWKGestureDoubleTap;
1861     case GestureType::TapAndHalf:
1862         return UIWKGestureTapAndHalf;
1863     case GestureType::DoubleTapInUneditable:
1864         return UIWKGestureDoubleTapInUneditable;
1865     case GestureType::OneFingerTapInUneditable:
1866         return UIWKGestureOneFingerTapInUneditable;
1867     case GestureType::OneFingerTapSelectsAll:
1868         return UIWKGestureOneFingerTapSelectsAll;
1869     case GestureType::OneFingerDoubleTap:
1870         return UIWKGestureOneFingerDoubleTap;
1871     case GestureType::OneFingerTripleTap:
1872         return UIWKGestureOneFingerTripleTap;
1873     case GestureType::TwoFingerSingleTap:
1874         return UIWKGestureTwoFingerSingleTap;
1875     case GestureType::TwoFingerRangedSelectGesture:
1876         return UIWKGestureTwoFingerRangedSelectGesture;
1877     case GestureType::TapOnLinkWithGesture:
1878         return UIWKGestureTapOnLinkWithGesture;
1879     case GestureType::MakeWebSelection:
1880         return UIWKGestureMakeWebSelection;
1881     case GestureType::PhraseBoundary:
1882         return UIWKGesturePhraseBoundary;
1883     }
1884 }
1885
1886 static inline SelectionTouch toSelectionTouch(UIWKSelectionTouch touch)
1887 {
1888     switch (touch) {
1889     case UIWKSelectionTouchStarted:
1890         return SelectionTouch::Started;
1891     case UIWKSelectionTouchMoved:
1892         return SelectionTouch::Moved;
1893     case UIWKSelectionTouchEnded:
1894         return SelectionTouch::Ended;
1895     case UIWKSelectionTouchEndedMovingForward:
1896         return SelectionTouch::EndedMovingForward;
1897     case UIWKSelectionTouchEndedMovingBackward:
1898         return SelectionTouch::EndedMovingBackward;
1899     case UIWKSelectionTouchEndedNotMoving:
1900         return SelectionTouch::EndedNotMoving;
1901     }
1902     ASSERT_NOT_REACHED();
1903     return SelectionTouch::Ended;
1904 }
1905
1906 static inline UIWKSelectionTouch toUIWKSelectionTouch(SelectionTouch touch)
1907 {
1908     switch (touch) {
1909     case SelectionTouch::Started:
1910         return UIWKSelectionTouchStarted;
1911     case SelectionTouch::Moved:
1912         return UIWKSelectionTouchMoved;
1913     case SelectionTouch::Ended:
1914         return UIWKSelectionTouchEnded;
1915     case SelectionTouch::EndedMovingForward:
1916         return UIWKSelectionTouchEndedMovingForward;
1917     case SelectionTouch::EndedMovingBackward:
1918         return UIWKSelectionTouchEndedMovingBackward;
1919     case SelectionTouch::EndedNotMoving:
1920         return UIWKSelectionTouchEndedNotMoving;
1921     }
1922 }
1923
1924 static inline GestureRecognizerState toGestureRecognizerState(UIGestureRecognizerState state)
1925 {
1926     switch (state) {
1927     case UIGestureRecognizerStatePossible:
1928         return GestureRecognizerState::Possible;
1929     case UIGestureRecognizerStateBegan:
1930         return GestureRecognizerState::Began;
1931     case UIGestureRecognizerStateChanged:
1932         return GestureRecognizerState::Changed;
1933     case UIGestureRecognizerStateCancelled:
1934         return GestureRecognizerState::Cancelled;
1935     case UIGestureRecognizerStateEnded:
1936         return GestureRecognizerState::Ended;
1937     case UIGestureRecognizerStateFailed:
1938         return GestureRecognizerState::Failed;
1939     }
1940 }
1941
1942 static inline UIGestureRecognizerState toUIGestureRecognizerState(GestureRecognizerState state)
1943 {
1944     switch (state) {
1945     case GestureRecognizerState::Possible:
1946         return UIGestureRecognizerStatePossible;
1947     case GestureRecognizerState::Began:
1948         return UIGestureRecognizerStateBegan;
1949     case GestureRecognizerState::Changed:
1950         return UIGestureRecognizerStateChanged;
1951     case GestureRecognizerState::Cancelled:
1952         return UIGestureRecognizerStateCancelled;
1953     case GestureRecognizerState::Ended:
1954         return UIGestureRecognizerStateEnded;
1955     case GestureRecognizerState::Failed:
1956         return UIGestureRecognizerStateFailed;
1957     }
1958 }
1959
1960 static inline UIWKSelectionFlags toUIWKSelectionFlags(SelectionFlags flags)
1961 {
1962     NSInteger uiFlags = UIWKNone;
1963     if (flags & WordIsNearTap)
1964         uiFlags |= UIWKWordIsNearTap;
1965     if (flags & IsBlockSelection)
1966         uiFlags |= UIWKIsBlockSelection;
1967     if (flags & PhraseBoundaryChanged)
1968         uiFlags |= UIWKPhraseBoundaryChanged;
1969
1970     return static_cast<UIWKSelectionFlags>(uiFlags);
1971 }
1972
1973 static inline SelectionHandlePosition toSelectionHandlePosition(UIWKHandlePosition position)
1974 {
1975     switch (position) {
1976     case UIWKHandleTop:
1977         return SelectionHandlePosition::Top;
1978     case UIWKHandleRight:
1979         return SelectionHandlePosition::Right;
1980     case UIWKHandleBottom:
1981         return SelectionHandlePosition::Bottom;
1982     case UIWKHandleLeft:
1983         return SelectionHandlePosition::Left;
1984     }
1985 }
1986
1987 static inline WebCore::TextGranularity toWKTextGranularity(UITextGranularity granularity)
1988 {
1989     switch (granularity) {
1990     case UITextGranularityCharacter:
1991         return CharacterGranularity;
1992     case UITextGranularityWord:
1993         return WordGranularity;
1994     case UITextGranularitySentence:
1995         return SentenceGranularity;
1996     case UITextGranularityParagraph:
1997         return ParagraphGranularity;
1998     case UITextGranularityLine:
1999         return LineGranularity;
2000     case UITextGranularityDocument:
2001         return DocumentGranularity;
2002     }
2003 }
2004
2005 static inline WebCore::SelectionDirection toWKSelectionDirection(UITextDirection direction)
2006 {
2007     switch (direction) {
2008     case UITextLayoutDirectionDown:
2009     case UITextLayoutDirectionRight:
2010         return DirectionRight;
2011     case UITextLayoutDirectionUp:
2012     case UITextLayoutDirectionLeft:
2013         return DirectionLeft;
2014     default:
2015         // UITextDirection is not an enum, but we only want to accept values from UITextLayoutDirection.
2016         ASSERT_NOT_REACHED();
2017         return DirectionRight;
2018     }
2019 }
2020
2021 static void selectionChangedWithGesture(WKContentView *view, const WebCore::IntPoint& point, uint32_t gestureType, uint32_t gestureState, uint32_t flags, WebKit::CallbackBase::Error error)
2022 {
2023     if (error != WebKit::CallbackBase::Error::None) {
2024         ASSERT_NOT_REACHED();
2025         return;
2026     }
2027     if ([view webSelectionAssistant])
2028         [(UIWKSelectionAssistant *)[view webSelectionAssistant] selectionChangedWithGestureAt:(CGPoint)point withGesture:toUIWKGestureType((GestureType)gestureType) withState:toUIGestureRecognizerState(static_cast<GestureRecognizerState>(gestureState)) withFlags:(toUIWKSelectionFlags((SelectionFlags)flags))];
2029     else
2030         [(UIWKTextInteractionAssistant *)[view interactionAssistant] selectionChangedWithGestureAt:(CGPoint)point withGesture:toUIWKGestureType((GestureType)gestureType) withState:toUIGestureRecognizerState(static_cast<GestureRecognizerState>(gestureState)) withFlags:(toUIWKSelectionFlags((SelectionFlags)flags))];
2031 }
2032
2033 static void selectionChangedWithTouch(WKContentView *view, const WebCore::IntPoint& point, uint32_t touch, WebKit::CallbackBase::Error error)
2034 {
2035     if (error != WebKit::CallbackBase::Error::None) {
2036         ASSERT_NOT_REACHED();
2037         return;
2038     }
2039     if ([view webSelectionAssistant])
2040         [(UIWKSelectionAssistant *)[view webSelectionAssistant] selectionChangedWithTouchAt:(CGPoint)point withSelectionTouch:toUIWKSelectionTouch((SelectionTouch)touch)];
2041     else
2042         [(UIWKTextInteractionAssistant *)[view interactionAssistant] selectionChangedWithTouchAt:(CGPoint)point withSelectionTouch:toUIWKSelectionTouch((SelectionTouch)touch)];
2043 }
2044
2045 - (void)_didUpdateBlockSelectionWithTouch:(SelectionTouch)touch withFlags:(SelectionFlags)flags growThreshold:(CGFloat)growThreshold shrinkThreshold:(CGFloat)shrinkThreshold
2046 {
2047     [_webSelectionAssistant blockSelectionChangedWithTouch:toUIWKSelectionTouch(touch) withFlags:toUIWKSelectionFlags(flags) growThreshold:growThreshold shrinkThreshold:shrinkThreshold];
2048     if (touch != SelectionTouch::Started && touch != SelectionTouch::Moved)
2049         _usingGestureForSelection = NO;
2050 }
2051
2052 - (BOOL)_isInteractingWithAssistedNode
2053 {
2054     return _textSelectionAssistant != nil;
2055 }
2056
2057 - (void)changeSelectionWithGestureAt:(CGPoint)point withGesture:(UIWKGestureType)gestureType withState:(UIGestureRecognizerState)state
2058 {
2059     _usingGestureForSelection = YES;
2060     _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) {
2061         selectionChangedWithGesture(self, point, gestureType, gestureState, flags, error);
2062         if (state == UIGestureRecognizerStateEnded || state == UIGestureRecognizerStateCancelled)
2063             _usingGestureForSelection = NO;
2064     });
2065 }
2066
2067 - (void)changeSelectionWithTouchAt:(CGPoint)point withSelectionTouch:(UIWKSelectionTouch)touch baseIsStart:(BOOL)baseIsStart
2068 {
2069     _usingGestureForSelection = YES;
2070     _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) {
2071         selectionChangedWithTouch(self, point, touch, error);
2072         if (touch != UIWKSelectionTouchStarted && touch != UIWKSelectionTouchMoved)
2073             _usingGestureForSelection = NO;
2074     });
2075 }
2076
2077 - (void)changeSelectionWithTouchesFrom:(CGPoint)from to:(CGPoint)to withGesture:(UIWKGestureType)gestureType withState:(UIGestureRecognizerState)gestureState
2078 {
2079     _usingGestureForSelection = YES;
2080     _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) {
2081         selectionChangedWithGesture(self, point, gestureType, gestureState, flags, error);
2082         if (gestureState == UIGestureRecognizerStateEnded || gestureState == UIGestureRecognizerStateCancelled)
2083             _usingGestureForSelection = NO;
2084     });
2085 }
2086
2087 - (void)changeBlockSelectionWithTouchAt:(CGPoint)point withSelectionTouch:(UIWKSelectionTouch)touch forHandle:(UIWKHandlePosition)handle
2088 {
2089     _usingGestureForSelection = YES;
2090     _page->updateBlockSelectionWithTouch(WebCore::IntPoint(point), static_cast<uint32_t>(toSelectionTouch(touch)), static_cast<uint32_t>(toSelectionHandlePosition(handle)));
2091 }
2092
2093 - (void)moveByOffset:(NSInteger)offset
2094 {
2095     if (!offset)
2096         return;
2097     
2098     [self beginSelectionChange];
2099     RetainPtr<WKContentView> view = self;
2100     _page->moveSelectionByOffset(offset, [view](WebKit::CallbackBase::Error) {
2101         [view endSelectionChange];
2102     });
2103 }
2104
2105 - (const WKAutoCorrectionData&)autocorrectionData
2106 {
2107     return _autocorrectionData;
2108 }
2109
2110 // The completion handler can pass nil if input does not match the actual text preceding the insertion point.
2111 - (void)requestAutocorrectionRectsForString:(NSString *)input withCompletionHandler:(void (^)(UIWKAutocorrectionRects *rectsForInput))completionHandler
2112 {
2113     if (!input || ![input length]) {
2114         completionHandler(nil);
2115         return;
2116     }
2117
2118     RetainPtr<WKContentView> view = self;
2119     _autocorrectionData.autocorrectionHandler = [completionHandler copy];
2120     _page->requestAutocorrectionData(input, [view](const Vector<FloatRect>& rects, const String& fontName, double fontSize, uint64_t traits, WebKit::CallbackBase::Error) {
2121         CGRect firstRect = CGRectZero;
2122         CGRect lastRect = CGRectZero;
2123         if (rects.size()) {
2124             firstRect = rects[0];
2125             lastRect = rects[rects.size() - 1];
2126         }
2127         
2128         view->_autocorrectionData.fontName = fontName;
2129         view->_autocorrectionData.fontSize = fontSize;
2130         view->_autocorrectionData.fontTraits = traits;
2131         view->_autocorrectionData.textFirstRect = firstRect;
2132         view->_autocorrectionData.textLastRect = lastRect;
2133
2134         view->_autocorrectionData.autocorrectionHandler(rects.size() ? [WKAutocorrectionRects autocorrectionRectsWithRects:firstRect lastRect:lastRect] : nil);
2135         [view->_autocorrectionData.autocorrectionHandler release];
2136         view->_autocorrectionData.autocorrectionHandler = nil;
2137     });
2138 }
2139
2140 - (void)selectPositionAtPoint:(CGPoint)point completionHandler:(void (^)(void))completionHandler
2141 {
2142     _usingGestureForSelection = YES;
2143     UIWKSelectionCompletionHandler selectionHandler = [completionHandler copy];
2144     RetainPtr<WKContentView> view = self;
2145     
2146     _page->selectPositionAtPoint(WebCore::IntPoint(point), [self _isInteractingWithAssistedNode], [view, selectionHandler](WebKit::CallbackBase::Error error) {
2147         selectionHandler();
2148         view->_usingGestureForSelection = NO;
2149         [selectionHandler release];
2150     });
2151 }
2152
2153 - (void)selectPositionAtBoundary:(UITextGranularity)granularity inDirection:(UITextDirection)direction fromPoint:(CGPoint)point completionHandler:(void (^)(void))completionHandler
2154 {
2155     _usingGestureForSelection = YES;
2156     UIWKSelectionCompletionHandler selectionHandler = [completionHandler copy];
2157     RetainPtr<WKContentView> view = self;
2158     
2159     _page->selectPositionAtBoundaryWithDirection(WebCore::IntPoint(point), toWKTextGranularity(granularity), toWKSelectionDirection(direction), [self _isInteractingWithAssistedNode], [view, selectionHandler](WebKit::CallbackBase::Error error) {
2160         selectionHandler();
2161         view->_usingGestureForSelection = NO;
2162         [selectionHandler release];
2163     });
2164 }
2165
2166 - (void)moveSelectionAtBoundary:(UITextGranularity)granularity inDirection:(UITextDirection)direction completionHandler:(void (^)(void))completionHandler
2167 {
2168     _usingGestureForSelection = YES;
2169     UIWKSelectionCompletionHandler selectionHandler = [completionHandler copy];
2170     RetainPtr<WKContentView> view = self;
2171     
2172     _page->moveSelectionAtBoundaryWithDirection(toWKTextGranularity(granularity), toWKSelectionDirection(direction), [view, selectionHandler](WebKit::CallbackBase::Error error) {
2173         selectionHandler();
2174         view->_usingGestureForSelection = NO;
2175         [selectionHandler release];
2176     });
2177 }
2178
2179 - (void)selectTextWithGranularity:(UITextGranularity)granularity atPoint:(CGPoint)point completionHandler:(void (^)(void))completionHandler
2180 {
2181     _usingGestureForSelection = YES;
2182     UIWKSelectionCompletionHandler selectionHandler = [completionHandler copy];
2183     RetainPtr<WKContentView> view = self;
2184
2185     _page->selectTextWithGranularityAtPoint(WebCore::IntPoint(point), toWKTextGranularity(granularity), [self _isInteractingWithAssistedNode], [view, selectionHandler](WebKit::CallbackBase::Error error) {
2186         selectionHandler();
2187         view->_usingGestureForSelection = NO;
2188         [selectionHandler release];
2189     });
2190 }
2191
2192 - (void)beginSelectionInDirection:(UITextDirection)direction completionHandler:(void (^)(BOOL endIsMoving))completionHandler
2193 {
2194     UIWKSelectionWithDirectionCompletionHandler selectionHandler = [completionHandler copy];
2195
2196     _page->beginSelectionInDirection(toWKSelectionDirection(direction), [selectionHandler](bool endIsMoving, WebKit::CallbackBase::Error error) {
2197         selectionHandler(endIsMoving);
2198         [selectionHandler release];
2199     });
2200 }
2201
2202 - (void)updateSelectionWithExtentPoint:(CGPoint)point completionHandler:(void (^)(BOOL endIsMoving))completionHandler
2203 {
2204     UIWKSelectionWithDirectionCompletionHandler selectionHandler = [completionHandler copy];
2205     
2206     _page->updateSelectionWithExtentPoint(WebCore::IntPoint(point), [self _isInteractingWithAssistedNode], [selectionHandler](bool endIsMoving, WebKit::CallbackBase::Error error) {
2207         selectionHandler(endIsMoving);
2208         [selectionHandler release];
2209     });
2210 }
2211
2212 - (void)updateSelectionWithExtentPoint:(CGPoint)point withBoundary:(UITextGranularity)granularity completionHandler:(void (^)(BOOL selectionEndIsMoving))completionHandler
2213 {
2214     UIWKSelectionWithDirectionCompletionHandler selectionHandler = [completionHandler copy];
2215     
2216     _page->updateSelectionWithExtentPointAndBoundary(WebCore::IntPoint(point), toWKTextGranularity(granularity), [self _isInteractingWithAssistedNode], [selectionHandler](bool endIsMoving, WebKit::CallbackBase::Error error) {
2217         selectionHandler(endIsMoving);
2218         [selectionHandler release];
2219     });
2220 }
2221
2222 - (UTF32Char)_characterBeforeCaretSelection
2223 {
2224     return _page->editorState().postLayoutData().characterBeforeSelection;
2225 }
2226
2227 - (UTF32Char)_characterInRelationToCaretSelection:(int)amount
2228 {
2229     switch (amount) {
2230     case 0:
2231         return _page->editorState().postLayoutData().characterAfterSelection;
2232     case -1:
2233         return _page->editorState().postLayoutData().characterBeforeSelection;
2234     case -2:
2235         return _page->editorState().postLayoutData().twoCharacterBeforeSelection;
2236     default:
2237         return 0;
2238     }
2239 }
2240
2241 - (BOOL)_selectionAtDocumentStart
2242 {
2243     return !_page->editorState().postLayoutData().characterBeforeSelection;
2244 }
2245
2246 - (CGRect)textFirstRect
2247 {
2248     return (_page->editorState().hasComposition) ? _page->editorState().firstMarkedRect : _autocorrectionData.textFirstRect;
2249 }
2250
2251 - (CGRect)textLastRect
2252 {
2253     return (_page->editorState().hasComposition) ? _page->editorState().lastMarkedRect : _autocorrectionData.textLastRect;
2254 }
2255
2256 - (void)replaceDictatedText:(NSString*)oldText withText:(NSString *)newText
2257 {
2258     _page->replaceDictatedText(oldText, newText);
2259 }
2260
2261 - (void)requestDictationContext:(void (^)(NSString *selectedText, NSString *beforeText, NSString *afterText))completionHandler
2262 {
2263     UIWKDictationContextHandler dictationHandler = [completionHandler copy];
2264
2265     _page->requestDictationContext([dictationHandler](const String& selectedText, const String& beforeText, const String& afterText, WebKit::CallbackBase::Error) {
2266         dictationHandler(selectedText, beforeText, afterText);
2267         [dictationHandler release];
2268     });
2269 }
2270
2271 // 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.
2272 - (void)applyAutocorrection:(NSString *)correction toString:(NSString *)input withCompletionHandler:(void (^)(UIWKAutocorrectionRects *rectsForCorrection))completionHandler
2273 {
2274     // FIXME: Remove the synchronous call when <rdar://problem/16207002> is fixed.
2275     const bool useSyncRequest = true;
2276
2277     if (useSyncRequest) {
2278         completionHandler(_page->applyAutocorrection(correction, input) ? [WKAutocorrectionRects autocorrectionRectsWithRects:_autocorrectionData.textFirstRect lastRect:_autocorrectionData.textLastRect] : nil);
2279         return;
2280     }
2281     _autocorrectionData.autocorrectionHandler = [completionHandler copy];
2282     RetainPtr<WKContentView> view = self;
2283     _page->applyAutocorrection(correction, input, [view](const String& string, WebKit::CallbackBase::Error error) {
2284         view->_autocorrectionData.autocorrectionHandler(!string.isNull() ? [WKAutocorrectionRects autocorrectionRectsWithRects:view->_autocorrectionData.textFirstRect lastRect:view->_autocorrectionData.textLastRect] : nil);
2285         [view->_autocorrectionData.autocorrectionHandler release];
2286         view->_autocorrectionData.autocorrectionHandler = nil;
2287     });
2288 }
2289
2290 - (void)requestAutocorrectionContextWithCompletionHandler:(void (^)(UIWKAutocorrectionContext *autocorrectionContext))completionHandler
2291 {
2292     // FIXME: Remove the synchronous call when <rdar://problem/16207002> is fixed.
2293     const bool useSyncRequest = true;
2294
2295     if (useSyncRequest) {
2296         String beforeText;
2297         String markedText;
2298         String selectedText;
2299         String afterText;
2300         uint64_t location;
2301         uint64_t length;
2302         _page->getAutocorrectionContext(beforeText, markedText, selectedText, afterText, location, length);
2303         completionHandler([WKAutocorrectionContext autocorrectionContextWithData:beforeText markedText:markedText selectedText:selectedText afterText:afterText selectedRangeInMarkedText:NSMakeRange(location, length)]);
2304     } else {
2305         _autocorrectionData.autocorrectionContextHandler = [completionHandler copy];
2306         RetainPtr<WKContentView> view = self;
2307         _page->requestAutocorrectionContext([view](const String& beforeText, const String& markedText, const String& selectedText, const String& afterText, uint64_t location, uint64_t length, WebKit::CallbackBase::Error) {
2308             view->_autocorrectionData.autocorrectionContextHandler([WKAutocorrectionContext autocorrectionContextWithData:beforeText markedText:markedText selectedText:selectedText afterText:afterText selectedRangeInMarkedText:NSMakeRange(location, length)]);
2309         });
2310     }
2311 }
2312
2313 // UIWebFormAccessoryDelegate
2314 - (void)accessoryDone
2315 {
2316     [self resignFirstResponder];
2317 }
2318
2319 - (NSArray *)keyCommands
2320 {
2321     static NSArray* nonEditableKeyCommands = [@[
2322        [UIKeyCommand keyCommandWithInput:UIKeyInputUpArrow modifierFlags:0 action:@selector(_arrowKey:)],
2323        [UIKeyCommand keyCommandWithInput:UIKeyInputDownArrow modifierFlags:0 action:@selector(_arrowKey:)],
2324        [UIKeyCommand keyCommandWithInput:UIKeyInputLeftArrow modifierFlags:0 action:@selector(_arrowKey:)],
2325        [UIKeyCommand keyCommandWithInput:UIKeyInputRightArrow modifierFlags:0 action:@selector(_arrowKey:)],
2326        
2327        [UIKeyCommand keyCommandWithInput:UIKeyInputUpArrow modifierFlags:UIKeyModifierCommand action:@selector(_arrowKey:)],
2328        [UIKeyCommand keyCommandWithInput:UIKeyInputDownArrow modifierFlags:UIKeyModifierCommand action:@selector(_arrowKey:)],
2329        
2330        [UIKeyCommand keyCommandWithInput:UIKeyInputUpArrow modifierFlags:UIKeyModifierShift action:@selector(_arrowKey:)],
2331        [UIKeyCommand keyCommandWithInput:UIKeyInputDownArrow modifierFlags:UIKeyModifierShift action:@selector(_arrowKey:)],
2332        [UIKeyCommand keyCommandWithInput:UIKeyInputLeftArrow modifierFlags:UIKeyModifierShift action:@selector(_arrowKey:)],
2333        [UIKeyCommand keyCommandWithInput:UIKeyInputRightArrow modifierFlags:UIKeyModifierShift action:@selector(_arrowKey:)],
2334        
2335        [UIKeyCommand keyCommandWithInput:UIKeyInputUpArrow modifierFlags:UIKeyModifierAlternate action:@selector(_arrowKey:)],
2336        [UIKeyCommand keyCommandWithInput:UIKeyInputDownArrow modifierFlags:UIKeyModifierAlternate action:@selector(_arrowKey:)],
2337        [UIKeyCommand keyCommandWithInput:UIKeyInputLeftArrow modifierFlags:UIKeyModifierAlternate action:@selector(_arrowKey:)],
2338        [UIKeyCommand keyCommandWithInput:UIKeyInputRightArrow modifierFlags:UIKeyModifierAlternate action:@selector(_arrowKey:)],
2339        
2340        [UIKeyCommand keyCommandWithInput:@" " modifierFlags:0 action:@selector(_arrowKey:)],
2341        [UIKeyCommand keyCommandWithInput:@" " modifierFlags:UIKeyModifierShift action:@selector(_arrowKey:)],
2342        
2343        [UIKeyCommand keyCommandWithInput:UIKeyInputPageDown modifierFlags:0 action:@selector(_arrowKey:)],
2344        [UIKeyCommand keyCommandWithInput:UIKeyInputPageDown modifierFlags:0 action:@selector(_arrowKey:)],
2345     ] retain];
2346
2347     static NSArray* editableKeyCommands = [@[
2348        [UIKeyCommand keyCommandWithInput:@"\t" modifierFlags:0 action:@selector(_nextAccessoryTab:)],
2349        [UIKeyCommand keyCommandWithInput:@"\t" modifierFlags:UIKeyModifierShift action:@selector(_prevAccessoryTab:)]
2350     ] retain];
2351     
2352     return (_page->editorState().isContentEditable) ? editableKeyCommands : nonEditableKeyCommands;
2353 }
2354
2355 - (void)_arrowKey:(id)sender
2356 {
2357     UIKeyCommand* command = sender;
2358     [self handleKeyEvent:command._triggeringEvent];
2359 }
2360
2361 - (void)_nextAccessoryTab:(id)sender
2362 {
2363     [self accessoryTab:YES];
2364 }
2365
2366 - (void)_prevAccessoryTab:(id)sender
2367 {
2368     [self accessoryTab:NO];
2369 }
2370
2371 - (void)accessoryTab:(BOOL)isNext
2372 {
2373     [_inputPeripheral endEditing];
2374     _inputPeripheral = nil;
2375
2376     _didAccessoryTabInitiateFocus = YES; // Will be cleared in either -_displayFormNodeInputView or -cleanupInteraction.
2377     [self beginSelectionChange];
2378     RetainPtr<WKContentView> view = self;
2379     _page->focusNextAssistedNode(isNext, [view](WebKit::CallbackBase::Error) {
2380         [view endSelectionChange];
2381         [view reloadInputViews];
2382     });
2383
2384 }
2385
2386 - (void)_becomeFirstResponderWithSelectionMovingForward:(BOOL)selectingForward completionHandler:(void (^)(BOOL didBecomeFirstResponder))completionHandler
2387 {
2388     auto completionHandlerCopy = Block_copy(completionHandler);
2389     RetainPtr<WKContentView> view = self;
2390     _page->setInitialFocus(selectingForward, false, WebKit::WebKeyboardEvent(), [view, completionHandlerCopy](WebKit::CallbackBase::Error) {
2391         BOOL didBecomeFirstResponder = view->_assistedNodeInformation.elementType != InputType::None && [view becomeFirstResponder];
2392         completionHandlerCopy(didBecomeFirstResponder);
2393         Block_release(completionHandlerCopy);
2394     });
2395 }
2396
2397 - (WebCore::Color)_tapHighlightColorForFastClick:(BOOL)forFastClick
2398 {
2399     ASSERT(_showDebugTapHighlightsForFastClicking);
2400     return forFastClick ? WebCore::Color(0, 225, 0, 127) : WebCore::Color(225, 0, 0, 127);
2401 }
2402
2403 - (void)_setDoubleTapGesturesEnabled:(BOOL)enabled
2404 {
2405     if (enabled && ![_doubleTapGestureRecognizer isEnabled]) {
2406         // The first tap recognized after re-enabling double tap gestures will not wait for the
2407         // second tap before committing. To fix this, we use a new double tap gesture recognizer.
2408         [self removeGestureRecognizer:_doubleTapGestureRecognizer.get()];
2409         [_doubleTapGestureRecognizer setDelegate:nil];
2410         [self _createAndConfigureDoubleTapGestureRecognizer];
2411     }
2412
2413     if (_showDebugTapHighlightsForFastClicking && !enabled)
2414         _tapHighlightInformation.color = [self _tapHighlightColorForFastClick:YES];
2415
2416     [_doubleTapGestureRecognizer setEnabled:enabled];
2417 }
2418
2419 - (void)accessoryAutoFill
2420 {
2421     id <_WKInputDelegate> inputDelegate = [_webView _inputDelegate];
2422     if ([inputDelegate respondsToSelector:@selector(_webView:accessoryViewCustomButtonTappedInFormInputSession:)])
2423         [inputDelegate _webView:_webView accessoryViewCustomButtonTappedInFormInputSession:_formInputSession.get()];
2424 }
2425
2426 - (void)accessoryClear
2427 {
2428     _page->setAssistedNodeValue(String());
2429 }
2430
2431 - (void)_updateAccessory
2432 {
2433     [_formAccessoryView setNextEnabled:_assistedNodeInformation.hasNextNode];
2434     [_formAccessoryView setPreviousEnabled:_assistedNodeInformation.hasPreviousNode];
2435
2436     if (UICurrentUserInterfaceIdiomIsPad())
2437         [_formAccessoryView setClearVisible:NO];
2438     else {
2439         switch (_assistedNodeInformation.elementType) {
2440         case InputType::Date:
2441         case InputType::Month:
2442         case InputType::DateTimeLocal:
2443         case InputType::Time:
2444             [_formAccessoryView setClearVisible:YES];
2445             break;
2446         default:
2447             [_formAccessoryView setClearVisible:NO];
2448             break;
2449         }
2450     }
2451
2452     // FIXME: hide or show the AutoFill button as needed.
2453 }
2454
2455 // Keyboard interaction
2456 // UITextInput protocol implementation
2457
2458 - (void)beginSelectionChange
2459 {
2460     [self.inputDelegate selectionWillChange:self];
2461 }
2462
2463 - (void)endSelectionChange
2464 {
2465     [self.inputDelegate selectionDidChange:self];
2466 }
2467
2468 - (NSString *)textInRange:(UITextRange *)range
2469 {
2470     return nil;
2471 }
2472
2473 - (void)replaceRange:(UITextRange *)range withText:(NSString *)text
2474 {
2475 }
2476
2477 - (UITextRange *)selectedTextRange
2478 {
2479     if (_page->editorState().selectionIsNone)
2480         return nil;
2481     auto& postLayoutEditorStateData = _page->editorState().postLayoutData();
2482     FloatRect startRect = postLayoutEditorStateData.caretRectAtStart;
2483     FloatRect endRect = postLayoutEditorStateData.caretRectAtEnd;
2484     double inverseScale = [self inverseScale];
2485     // We want to keep the original caret width, while the height scales with
2486     // the content taking orientation into account.
2487     // We achieve this by scaling the width with the inverse
2488     // scale factor. This way, when it is converted from the content view
2489     // the width remains unchanged.
2490     if (startRect.width() < startRect.height())
2491         startRect.setWidth(startRect.width() * inverseScale);
2492     else
2493         startRect.setHeight(startRect.height() * inverseScale);
2494     if (endRect.width() < endRect.height()) {
2495         double delta = endRect.width();
2496         endRect.setWidth(endRect.width() * inverseScale);
2497         delta = endRect.width() - delta;
2498         endRect.move(delta, 0);
2499     } else {
2500         double delta = endRect.height();
2501         endRect.setHeight(endRect.height() * inverseScale);
2502         delta = endRect.height() - delta;
2503         endRect.move(0, delta);
2504     }
2505     return [WKTextRange textRangeWithState:_page->editorState().selectionIsNone
2506                                    isRange:_page->editorState().selectionIsRange
2507                                 isEditable:_page->editorState().isContentEditable
2508                                  startRect:startRect
2509                                    endRect:endRect
2510                             selectionRects:[self webSelectionRects]
2511                         selectedTextLength:postLayoutEditorStateData.selectedTextLength];
2512 }
2513
2514 - (CGRect)caretRectForPosition:(UITextPosition *)position
2515 {
2516     return ((WKTextPosition *)position).positionRect;
2517 }
2518
2519 - (NSArray *)selectionRectsForRange:(UITextRange *)range
2520 {
2521     return [WKTextSelectionRect textSelectionRectsWithWebRects:((WKTextRange *)range).selectionRects];
2522 }
2523
2524 - (void)setSelectedTextRange:(UITextRange *)range
2525 {
2526 }
2527
2528 - (BOOL)hasMarkedText
2529 {
2530     return [_markedText length];
2531 }
2532
2533 - (NSString *)markedText
2534 {
2535     return _markedText.get();
2536 }
2537
2538 - (UITextRange *)markedTextRange
2539 {
2540     return nil;
2541 }
2542
2543 - (NSDictionary *)markedTextStyle
2544 {
2545     return nil;
2546 }
2547
2548 - (void)setMarkedTextStyle:(NSDictionary *)styleDictionary
2549 {
2550 }
2551
2552 - (void)setMarkedText:(NSString *)markedText selectedRange:(NSRange)selectedRange
2553 {
2554     _markedText = markedText;
2555     _page->setCompositionAsync(markedText, Vector<WebCore::CompositionUnderline>(), selectedRange, EditingRange());
2556 }
2557
2558 - (void)unmarkText
2559 {
2560     _markedText = nil;
2561     _page->confirmCompositionAsync();
2562 }
2563
2564 - (UITextPosition *)beginningOfDocument
2565 {
2566     return nil;
2567 }
2568
2569 - (UITextPosition *)endOfDocument
2570 {
2571     return nil;
2572 }
2573
2574 - (UITextRange *)textRangeFromPosition:(UITextPosition *)fromPosition toPosition:(UITextPosition *)toPosition
2575 {
2576     return nil;
2577 }
2578
2579 - (UITextPosition *)positionFromPosition:(UITextPosition *)position offset:(NSInteger)offset
2580 {
2581     return nil;
2582 }
2583
2584 - (UITextPosition *)positionFromPosition:(UITextPosition *)position inDirection:(UITextLayoutDirection)direction offset:(NSInteger)offset
2585 {
2586     return nil;
2587 }
2588
2589 - (NSComparisonResult)comparePosition:(UITextPosition *)position toPosition:(UITextPosition *)other
2590 {
2591     return NSOrderedSame;
2592 }
2593
2594 - (NSInteger)offsetFromPosition:(UITextPosition *)from toPosition:(UITextPosition *)toPosition
2595 {
2596     return 0;
2597 }
2598
2599 - (id <UITextInputTokenizer>)tokenizer
2600 {
2601     return nil;
2602 }
2603
2604 - (UITextPosition *)positionWithinRange:(UITextRange *)range farthestInDirection:(UITextLayoutDirection)direction
2605 {
2606     return nil;
2607 }
2608
2609 - (UITextRange *)characterRangeByExtendingPosition:(UITextPosition *)position inDirection:(UITextLayoutDirection)direction
2610 {
2611     return nil;
2612 }
2613
2614 - (UITextWritingDirection)baseWritingDirectionForPosition:(UITextPosition *)position inDirection:(UITextStorageDirection)direction
2615 {
2616     return UITextWritingDirectionLeftToRight;
2617 }
2618
2619 - (void)setBaseWritingDirection:(UITextWritingDirection)writingDirection forRange:(UITextRange *)range
2620 {
2621 }
2622
2623 - (CGRect)firstRectForRange:(UITextRange *)range
2624 {
2625     return CGRectZero;
2626 }
2627
2628 /* Hit testing. */
2629 - (UITextPosition *)closestPositionToPoint:(CGPoint)point
2630 {
2631     return nil;
2632 }
2633
2634 - (UITextPosition *)closestPositionToPoint:(CGPoint)point withinRange:(UITextRange *)range
2635 {
2636     return nil;
2637 }
2638
2639 - (UITextRange *)characterRangeAtPoint:(CGPoint)point
2640 {
2641     return nil;
2642 }
2643
2644 - (void)deleteBackward
2645 {
2646     _page->executeEditCommand(ASCIILiteral("deleteBackward"));
2647 }
2648
2649 // Inserts the given string, replacing any selected or marked text.
2650 - (void)insertText:(NSString *)aStringValue
2651 {
2652     _page->insertTextAsync(aStringValue, EditingRange());
2653 }
2654
2655 - (BOOL)hasText
2656 {
2657     return YES;
2658 }
2659
2660 // end of UITextInput protocol implementation
2661
2662 static UITextAutocapitalizationType toUITextAutocapitalize(WebAutocapitalizeType webkitType)
2663 {
2664     switch (webkitType) {
2665     case WebAutocapitalizeTypeDefault:
2666         return UITextAutocapitalizationTypeSentences;
2667     case WebAutocapitalizeTypeNone:
2668         return UITextAutocapitalizationTypeNone;
2669     case WebAutocapitalizeTypeWords:
2670         return UITextAutocapitalizationTypeWords;
2671     case WebAutocapitalizeTypeSentences:
2672         return UITextAutocapitalizationTypeSentences;
2673     case WebAutocapitalizeTypeAllCharacters:
2674         return UITextAutocapitalizationTypeAllCharacters;
2675     }
2676
2677     return UITextAutocapitalizationTypeSentences;
2678 }
2679
2680 // UITextInputPrivate protocol
2681 // Direct access to the (private) UITextInputTraits object.
2682 - (UITextInputTraits *)textInputTraits
2683 {
2684     if (!_traits)
2685         _traits = adoptNS([[UITextInputTraits alloc] init]);
2686
2687     [_traits setSecureTextEntry:_assistedNodeInformation.elementType == InputType::Password];
2688     [_traits setShortcutConversionType:_assistedNodeInformation.elementType == InputType::Password ? UITextShortcutConversionTypeNo : UITextShortcutConversionTypeDefault];
2689
2690     if (!_assistedNodeInformation.formAction.isEmpty())
2691         [_traits setReturnKeyType:(_assistedNodeInformation.elementType == InputType::Search) ? UIReturnKeySearch : UIReturnKeyGo];
2692
2693     if (_assistedNodeInformation.elementType == InputType::Password || _assistedNodeInformation.elementType == InputType::Email || _assistedNodeInformation.elementType == InputType::URL || _assistedNodeInformation.formAction.contains("login")) {
2694         [_traits setAutocapitalizationType:UITextAutocapitalizationTypeNone];
2695         [_traits setAutocorrectionType:UITextAutocorrectionTypeNo];
2696     } else {
2697         [_traits setAutocapitalizationType:toUITextAutocapitalize(_assistedNodeInformation.autocapitalizeType)];
2698         [_traits setAutocorrectionType:_assistedNodeInformation.isAutocorrect ? UITextAutocorrectionTypeYes : UITextAutocorrectionTypeNo];
2699     }
2700
2701     switch (_assistedNodeInformation.elementType) {
2702     case InputType::Phone:
2703          [_traits setKeyboardType:UIKeyboardTypePhonePad];
2704          break;
2705     case InputType::URL:
2706          [_traits setKeyboardType:UIKeyboardTypeURL];
2707          break;
2708     case InputType::Email:
2709          [_traits setKeyboardType:UIKeyboardTypeEmailAddress];
2710           break;
2711     case InputType::Number:
2712          [_traits setKeyboardType:UIKeyboardTypeNumbersAndPunctuation];
2713          break;
2714     case InputType::NumberPad:
2715          [_traits setKeyboardType:UIKeyboardTypeNumberPad];
2716          break;
2717     default:
2718          [_traits setKeyboardType:UIKeyboardTypeDefault];
2719     }
2720
2721     return _traits.get();
2722 }
2723
2724 - (UITextInteractionAssistant *)interactionAssistant
2725 {
2726     return _textSelectionAssistant.get();
2727 }
2728
2729 - (UIWebSelectionAssistant *)webSelectionAssistant
2730 {
2731     return _webSelectionAssistant.get();
2732 }
2733
2734 - (id<UISelectionInteractionAssistant>)selectionInteractionAssistant
2735 {
2736     if ([_webSelectionAssistant conformsToProtocol:@protocol(UISelectionInteractionAssistant)])
2737         return (id<UISelectionInteractionAssistant>)_webSelectionAssistant.get();
2738     return nil;
2739 }
2740
2741 // NSRange support.  Would like to deprecate to the extent possible, although some support
2742 // (i.e. selectionRange) has shipped as API.
2743 - (NSRange)selectionRange
2744 {
2745     return NSMakeRange(NSNotFound, 0);
2746 }
2747
2748 - (CGRect)rectForNSRange:(NSRange)range
2749 {
2750     return CGRectZero;
2751 }
2752
2753 - (NSRange)_markedTextNSRange
2754 {
2755     return NSMakeRange(NSNotFound, 0);
2756 }
2757
2758 // DOM range support.
2759 - (DOMRange *)selectedDOMRange
2760 {
2761     return nil;
2762 }
2763
2764 - (void)setSelectedDOMRange:(DOMRange *)range affinityDownstream:(BOOL)affinityDownstream
2765 {
2766 }
2767
2768 // Modify text without starting a new undo grouping.
2769 - (void)replaceRangeWithTextWithoutClosingTyping:(UITextRange *)range replacementText:(NSString *)text
2770 {
2771 }
2772
2773 // Caret rect support.  Shouldn't be necessary, but firstRectForRange doesn't offer precisely
2774 // the same functionality.
2775 - (CGRect)rectContainingCaretSelection
2776 {
2777     return CGRectZero;
2778 }
2779
2780 // Web events.
2781 - (BOOL)requiresKeyEvents
2782 {
2783     return YES;
2784 }
2785
2786 - (void)_handleKeyUIEvent:(::UIEvent *)event
2787 {
2788     // We only want to handle key event from the hardware keyboard when we are
2789     // first responder and we are not interacting with editable content.
2790     if ([self isFirstResponder] && event._hidEvent && !_page->editorState().isContentEditable)
2791         [self handleKeyEvent:event];
2792
2793     [super _handleKeyUIEvent:event];
2794 }
2795
2796 - (void)handleKeyEvent:(::UIEvent *)event
2797 {
2798     ::WebEvent *webEvent = [[[::WebEvent alloc] initWithKeyEventType:(event._isKeyDown) ? WebEventKeyDown : WebEventKeyUp
2799                                                            timeStamp:event.timestamp
2800                                                           characters:event._modifiedInput
2801                                          charactersIgnoringModifiers:event._unmodifiedInput
2802                                                            modifiers:event._modifierFlags
2803                                                          isRepeating:(event._inputFlags & kUIKeyboardInputRepeat)
2804                                                            withFlags:event._inputFlags
2805                                                              keyCode:0
2806                                                             isTabKey:[event._modifiedInput isEqualToString:@"\t"]
2807                                                         characterSet:WebEventCharacterSetUnicode] autorelease];
2808     
2809     [self handleKeyWebEvent:webEvent];    
2810 }
2811
2812 - (void)handleKeyWebEvent:(WebIOSEvent *)theEvent
2813 {
2814     _page->handleKeyboardEvent(NativeWebKeyboardEvent(theEvent));
2815 }
2816
2817 - (void)_didHandleKeyEvent:(WebIOSEvent *)event
2818 {
2819     if (event.type == WebEventKeyDown) {
2820         // FIXME: This is only for staging purposes.
2821         if ([[UIKeyboardImpl sharedInstance] respondsToSelector:@selector(didHandleWebKeyEvent:)])
2822             [[UIKeyboardImpl sharedInstance] didHandleWebKeyEvent:event];
2823         else
2824             [[UIKeyboardImpl sharedInstance] didHandleWebKeyEvent];
2825     }
2826 }
2827
2828 - (BOOL)_interpretKeyEvent:(WebIOSEvent *)event isCharEvent:(BOOL)isCharEvent
2829 {
2830     static const unsigned kWebEnterKey = 0x0003;
2831     static const unsigned kWebBackspaceKey = 0x0008;
2832     static const unsigned kWebReturnKey = 0x000D;
2833     static const unsigned kWebDeleteKey = 0x007F;
2834     static const unsigned kWebDeleteForwardKey = 0xF728;
2835     static const unsigned kWebSpaceKey = 0x20;
2836
2837     BOOL contentEditable = _page->editorState().isContentEditable;
2838
2839     if (!contentEditable && event.isTabKey)
2840         return NO;
2841
2842     BOOL shift = event.modifierFlags & WebEventFlagMaskShift;
2843     BOOL command = event.modifierFlags & WebEventFlagMaskCommand;
2844     BOOL option = event.modifierFlags & WebEventFlagMaskAlternate;
2845     NSString *charactersIgnoringModifiers = [event charactersIgnoringModifiers];
2846     BOOL shouldScroll = YES;
2847     FloatPoint scrollOffset;
2848
2849     if ([charactersIgnoringModifiers isEqualToString:UIKeyInputLeftArrow])
2850         scrollOffset.setX(-Scrollbar::pixelsPerLineStep());
2851     else if ([charactersIgnoringModifiers isEqualToString:UIKeyInputUpArrow]) {
2852         if (option)
2853             scrollOffset.setY(-_page->unobscuredContentRect().height());
2854         else if (command)
2855             scrollOffset.setY(-[self bounds].size.height);
2856         else
2857             scrollOffset.setY(-Scrollbar::pixelsPerLineStep());
2858     } else if ([charactersIgnoringModifiers isEqualToString:UIKeyInputRightArrow])
2859             scrollOffset.setX(Scrollbar::pixelsPerLineStep());
2860     else if ([charactersIgnoringModifiers isEqualToString:UIKeyInputDownArrow]) {
2861         if (option)
2862             scrollOffset.setY(_page->unobscuredContentRect().height());
2863         else if (command)
2864             scrollOffset.setY([self bounds].size.height);
2865         else
2866             scrollOffset.setY(Scrollbar::pixelsPerLineStep());
2867     } else if ([charactersIgnoringModifiers isEqualToString:UIKeyInputPageDown])
2868         scrollOffset.setY(_page->unobscuredContentRect().height());
2869     else if ([charactersIgnoringModifiers isEqualToString:UIKeyInputPageUp])
2870         scrollOffset.setY(-_page->unobscuredContentRect().height());
2871     else
2872         shouldScroll = NO;
2873
2874     if (shouldScroll) {
2875         [_webView _scrollByOffset:scrollOffset];
2876         return YES;
2877     }
2878
2879     UIKeyboardImpl *keyboard = [UIKeyboardImpl sharedInstance];
2880     NSString *characters = [event characters];
2881     
2882     if (![characters length])
2883         return NO;
2884
2885     switch ([characters characterAtIndex:0]) {
2886     case kWebBackspaceKey:
2887     case kWebDeleteKey:
2888         if (contentEditable) {
2889             // FIXME: remove deleteFromInput once UIKit adopts deleteFromInputWithFlags
2890             if ([keyboard respondsToSelector:@selector(deleteFromInputWithFlags:)])
2891                 [keyboard deleteFromInputWithFlags:event.keyboardFlags];
2892             else
2893                 [keyboard deleteFromInput];
2894             return YES;
2895         }
2896         break;
2897
2898     case kWebSpaceKey:
2899         if (!contentEditable) {
2900             [_webView _scrollByOffset:FloatPoint(0, shift ? -_page->unobscuredContentRect().height() : _page->unobscuredContentRect().height())];
2901             return YES;
2902         }
2903         if (isCharEvent) {
2904             [keyboard addInputString:event.characters withFlags:event.keyboardFlags];
2905             return YES;
2906         }
2907         break;
2908
2909     case kWebEnterKey:
2910     case kWebReturnKey:
2911         if (contentEditable && isCharEvent) {
2912             // Map \r from HW keyboard to \n to match the behavior of the soft keyboard.
2913             [keyboard addInputString:@"\n" withFlags:0];
2914             return YES;
2915         }
2916         break;
2917
2918     case kWebDeleteForwardKey:
2919         _page->executeEditCommand(ASCIILiteral("deleteForward"));
2920         return YES;
2921
2922     default:
2923         if (contentEditable && isCharEvent) {
2924             [keyboard addInputString:event.characters withFlags:event.keyboardFlags];
2925             return YES;
2926         }
2927         break;
2928     }
2929
2930     return NO;
2931 }
2932
2933 - (void)executeEditCommandWithCallback:(NSString *)commandName
2934 {
2935     [self beginSelectionChange];
2936     RetainPtr<WKContentView> view = self;
2937     _page->executeEditCommand(commandName, [view](WebKit::CallbackBase::Error) {
2938         [view endSelectionChange];
2939     });
2940 }
2941
2942 - (UITextInputArrowKeyHistory *)_moveUp:(BOOL)extending withHistory:(UITextInputArrowKeyHistory *)history
2943 {
2944     [self executeEditCommandWithCallback:extending ? @"moveUpAndModifySelection" : @"moveUp"];
2945     return nil;
2946 }
2947
2948 - (UITextInputArrowKeyHistory *)_moveDown:(BOOL)extending withHistory:(UITextInputArrowKeyHistory *)history
2949 {
2950     [self executeEditCommandWithCallback:extending ? @"moveDownAndModifySelection" : @"moveDown"];
2951     return nil;
2952 }
2953
2954 - (UITextInputArrowKeyHistory *)_moveLeft:(BOOL)extending withHistory:(UITextInputArrowKeyHistory *)history
2955 {
2956     [self executeEditCommandWithCallback:extending? @"moveLeftAndModifySelection" : @"moveLeft"];
2957     return nil;
2958 }
2959
2960 - (UITextInputArrowKeyHistory *)_moveRight:(BOOL)extending withHistory:(UITextInputArrowKeyHistory *)history
2961 {
2962     [self executeEditCommandWithCallback:extending ? @"moveRightAndModifySelection" : @"moveRight"];
2963     return nil;
2964 }
2965
2966 - (UITextInputArrowKeyHistory *)_moveToStartOfWord:(BOOL)extending withHistory:(UITextInputArrowKeyHistory *)history
2967 {
2968     [self executeEditCommandWithCallback:extending ? @"moveWordBackwardAndModifySelection" : @"moveWordBackward"];
2969     return nil;
2970 }
2971
2972 - (UITextInputArrowKeyHistory *)_moveToStartOfParagraph:(BOOL)extending withHistory:(UITextInputArrowKeyHistory *)history
2973 {
2974     [self executeEditCommandWithCallback:extending ? @"moveToBeginningOfParagraphAndModifySelection" : @"moveToBeginningOfParagraph"];
2975     return nil;
2976 }
2977
2978 - (UITextInputArrowKeyHistory *)_moveToStartOfLine:(BOOL)extending withHistory:(UITextInputArrowKeyHistory *)history
2979 {
2980     [self executeEditCommandWithCallback:extending ? @"moveToBeginningOfLineAndModifySelection" : @"moveToBeginningOfLine"];
2981     return nil;
2982 }
2983
2984 - (UITextInputArrowKeyHistory *)_moveToStartOfDocument:(BOOL)extending withHistory:(UITextInputArrowKeyHistory *)history
2985 {
2986     [self executeEditCommandWithCallback:extending ? @"moveToBeginningOfDocumentAndModifySelection" : @"moveToBeginningOfDocument"];
2987     return nil;
2988 }
2989
2990 - (UITextInputArrowKeyHistory *)_moveToEndOfWord:(BOOL)extending withHistory:(UITextInputArrowKeyHistory *)history
2991 {
2992     [self executeEditCommandWithCallback:extending ? @"moveWordForwardAndModifySelection" : @"moveWordForward"];
2993     return nil;
2994 }
2995
2996 - (UITextInputArrowKeyHistory *)_moveToEndOfParagraph:(BOOL)extending withHistory:(UITextInputArrowKeyHistory *)history
2997 {
2998     [self executeEditCommandWithCallback:extending ? @"moveToEndOfParagraphAndModifySelection" : @"moveToEndOfParagraph"];
2999     return nil;
3000 }
3001
3002 - (UITextInputArrowKeyHistory *)_moveToEndOfLine:(BOOL)extending withHistory:(UITextInputArrowKeyHistory *)history
3003 {
3004     [self executeEditCommandWithCallback:extending ? @"moveToEndOfLineAndModifySelection" : @"moveToEndOfLine"];
3005     return nil;
3006 }
3007
3008 - (UITextInputArrowKeyHistory *)_moveToEndOfDocument:(BOOL)extending withHistory:(UITextInputArrowKeyHistory *)history
3009 {
3010     [self executeEditCommandWithCallback:extending ? @"moveToEndOfDocumentAndModifySelection" : @"moveToEndOfDocument"];
3011     return nil;
3012 }
3013
3014 // Sets a buffer to make room for autocorrection views
3015 - (void)setBottomBufferHeight:(CGFloat)bottomBuffer
3016 {
3017 }
3018
3019 - (UIView *)automaticallySelectedOverlay
3020 {
3021     return self;
3022 }
3023
3024 - (UITextGranularity)selectionGranularity
3025 {
3026     return UITextGranularityCharacter;
3027 }
3028
3029 // Should return an array of NSDictionary objects that key/value paries for the final text, correction identifier and
3030 // alternative selection counts using the keys defined at the top of this header.
3031 - (NSArray *)metadataDictionariesForDictationResults
3032 {
3033     return nil;
3034 }
3035
3036 // Returns the dictation result boundaries from position so that text that was not dictated can be excluded from logging.
3037 // If these are not implemented, no text will be logged.
3038 - (UITextPosition *)previousUnperturbedDictationResultBoundaryFromPosition:(UITextPosition *)position
3039 {
3040     return nil;
3041 }
3042
3043 - (UITextPosition *)nextUnperturbedDictationResultBoundaryFromPosition:(UITextPosition *)position
3044 {
3045     return nil;
3046 }
3047
3048 // The can all be (and have been) trivially implemented in terms of UITextInput.  Deprecate and remove.
3049 - (void)moveBackward:(unsigned)count
3050 {
3051 }
3052
3053 - (void)moveForward:(unsigned)count
3054 {
3055 }
3056
3057 - (unichar)characterBeforeCaretSelection
3058 {
3059     return 0;
3060 }
3061
3062 - (NSString *)wordContainingCaretSelection
3063 {
3064     return nil;
3065 }
3066
3067 - (DOMRange *)wordRangeContainingCaretSelection
3068 {
3069     return nil;
3070 }
3071
3072 - (void)setMarkedText:(NSString *)text
3073 {
3074 }
3075
3076 - (BOOL)hasContent
3077 {
3078     return _page->editorState().postLayoutData().hasContent;
3079 }
3080
3081 - (void)selectAll
3082 {
3083 }
3084
3085 - (UIColor *)textColorForCaretSelection
3086 {
3087     return [UIColor blackColor];
3088 }
3089
3090 - (UIFont *)fontForCaretSelection
3091 {
3092     CGFloat zoomScale = 1.0;    // FIXME: retrieve the actual document scale factor.
3093     CGFloat scaledSize = _autocorrectionData.fontSize;
3094     if (CGFAbs(zoomScale - 1.0) > FLT_EPSILON)
3095         scaledSize *= zoomScale;
3096     return [UIFont fontWithFamilyName:_autocorrectionData.fontName traits:(UIFontTrait)_autocorrectionData.fontTraits size:scaledSize];
3097 }
3098
3099 - (BOOL)hasSelection
3100 {
3101     return NO;
3102 }
3103
3104 - (BOOL)isPosition:(UITextPosition *)position atBoundary:(UITextGranularity)granularity inDirection:(UITextDirection)direction
3105 {
3106     return NO;
3107 }
3108
3109 - (UITextPosition *)positionFromPosition:(UITextPosition *)position toBoundary:(UITextGranularity)granularity inDirection:(UITextDirection)direction
3110 {
3111     return nil;
3112 }
3113
3114 - (BOOL)isPosition:(UITextPosition *)position withinTextUnit:(UITextGranularity)granularity inDirection:(UITextDirection)direction
3115 {
3116     return NO;
3117 }
3118
3119 - (UITextRange *)rangeEnclosingPosition:(UITextPosition *)position withGranularity:(UITextGranularity)granularity inDirection:(UITextDirection)direction
3120 {
3121     return nil;
3122 }
3123
3124 - (void)takeTraitsFrom:(UITextInputTraits *)traits
3125 {
3126     [[self textInputTraits] takeTraitsFrom:traits];
3127 }
3128
3129 // FIXME: I want to change the name of these functions, but I'm leaving it for now
3130 // to make it easier to look up the corresponding functions in UIKit.
3131
3132 - (void)_startAssistingKeyboard
3133 {
3134     [self useSelectionAssistantWithMode:UIWebSelectionModeTextOnly];
3135     [self reloadInputViews];
3136 }
3137
3138 - (void)_stopAssistingKeyboard
3139 {
3140     [self useSelectionAssistantWithMode:toUIWebSelectionMode([[_webView configuration] selectionGranularity])];
3141 }
3142
3143 - (const AssistedNodeInformation&)assistedNodeInformation
3144 {
3145     return _assistedNodeInformation;
3146 }
3147
3148 - (Vector<OptionItem>&)assistedNodeSelectOptions
3149 {
3150     return _assistedNodeInformation.selectOptions;
3151 }
3152
3153 - (UIWebFormAccessory *)formAccessoryView
3154 {
3155     [self _ensureFormAccessoryView];
3156     return _formAccessoryView.get();
3157 }
3158
3159 static bool isAssistableInputType(InputType type)
3160 {
3161     switch (type) {
3162     case InputType::ContentEditable:
3163     case InputType::Text:
3164     case InputType::Password:
3165     case InputType::TextArea:
3166     case InputType::Search:
3167     case InputType::Email:
3168     case InputType::URL:
3169     case InputType::Phone:
3170     case InputType::Number:
3171     case InputType::NumberPad:
3172     case InputType::Date:
3173     case InputType::DateTime:
3174     case InputType::DateTimeLocal:
3175     case InputType::Month:
3176     case InputType::Week:
3177     case InputType::Time:
3178     case InputType::Select:
3179         return true;
3180
3181     case InputType::None:
3182         return false;
3183     }
3184
3185     ASSERT_NOT_REACHED();
3186     return false;
3187 }
3188
3189 - (void)_startAssistingNode:(const AssistedNodeInformation&)information userIsInteracting:(BOOL)userIsInteracting blurPreviousNode:(BOOL)blurPreviousNode userObject:(NSObject <NSSecureCoding> *)userObject
3190 {
3191     id <_WKInputDelegate> inputDelegate = [_webView _inputDelegate];
3192     RetainPtr<WKFocusedElementInfo> focusedElementInfo = adoptNS([[WKFocusedElementInfo alloc] initWithAssistedNodeInformation:information isUserInitiated:userIsInteracting]);
3193     BOOL shouldShowKeyboard;
3194
3195     if ([inputDelegate respondsToSelector:@selector(_webView:focusShouldStartInputSession:)])
3196         shouldShowKeyboard = [inputDelegate _webView:_webView focusShouldStartInputSession:focusedElementInfo.get()];
3197     else {
3198         // The default behavior is to allow node assistance if the user is interacting or the keyboard is already active.
3199         shouldShowKeyboard = userIsInteracting || _textSelectionAssistant;
3200     }
3201     if (!shouldShowKeyboard)
3202         return;
3203
3204     if (blurPreviousNode)
3205         [self _stopAssistingNode];
3206
3207     if (!isAssistableInputType(information.elementType))
3208         return;
3209
3210     // FIXME: We should remove this check when we manage to send StartAssistingNode from the WebProcess
3211     // only when it is truly time to show the keyboard.
3212     if (_assistedNodeInformation.elementType == information.elementType && _assistedNodeInformation.elementRect == information.elementRect)
3213         return;
3214
3215     _isEditable = YES;
3216     _assistedNodeInformation = information;
3217     _inputPeripheral = nil;
3218     _traits = nil;
3219     if (![self isFirstResponder])
3220         [self becomeFirstResponder];
3221
3222     [self reloadInputViews];
3223     
3224     switch (information.elementType) {
3225     case InputType::Select:
3226     case InputType::DateTimeLocal:
3227     case InputType::Time:
3228     case InputType::Month:
3229     case InputType::Date:
3230         break;
3231     default:
3232         [self _startAssistingKeyboard];
3233         break;
3234     }
3235     
3236     if (information.insideFixedPosition)
3237         [_webView _updateVisibleContentRects];
3238     
3239     [self _displayFormNodeInputView];
3240
3241     // _inputPeripheral has been initialized in inputView called by reloadInputViews.
3242     [_inputPeripheral beginEditing];
3243
3244     if ([inputDelegate respondsToSelector:@selector(_webView:didStartInputSession:)]) {
3245         _formInputSession = adoptNS([[WKFormInputSession alloc] initWithContentView:self focusedElementInfo:focusedElementInfo.get() userObject:userObject]);
3246         [inputDelegate _webView:_webView didStartInputSession:_formInputSession.get()];
3247     }
3248 }
3249
3250 - (void)_stopAssistingNode
3251 {
3252     [_formInputSession invalidate];
3253     _formInputSession = nil;
3254     _isEditable = NO;
3255     _assistedNodeInformation.elementType = InputType::None;
3256     [_inputPeripheral endEditing];
3257     _inputPeripheral = nil;
3258
3259     [self _stopAssistingKeyboard];
3260     [_formAccessoryView hideAutoFillButton];
3261     [self reloadInputViews];
3262     [self _updateAccessory];
3263     // The name is misleading, but this actually clears the selection views and removes any selection.
3264     [_webSelectionAssistant resignedFirstResponder];
3265 }
3266
3267 - (void)_selectionChanged
3268 {
3269     _selectionNeedsUpdate = YES;
3270     // If we are changing the selection with a gesture there is no need
3271     // to wait to paint the selection.
3272     if (_usingGestureForSelection)
3273         [self _updateChangedSelection];
3274 }
3275
3276 - (void)selectWordForReplacement
3277 {
3278     _page->extendSelection(WordGranularity);
3279 }
3280
3281 - (void)_updateChangedSelection
3282 {
3283     [self _updateChangedSelection:NO];
3284 }
3285
3286 - (void)_updateChangedSelection:(BOOL)force
3287 {
3288     if (!_selectionNeedsUpdate)
3289         return;
3290
3291     WKSelectionDrawingInfo selectionDrawingInfo(_page->editorState());
3292     if (!force && selectionDrawingInfo == _lastSelectionDrawingInfo)
3293         return;
3294
3295     _lastSelectionDrawingInfo = selectionDrawingInfo;
3296
3297     // FIXME: We need to figure out what to do if the selection is changed by Javascript.
3298     if (_textSelectionAssistant) {
3299         _markedText = (_page->editorState().hasComposition) ? _page->editorState().markedText : String();
3300         if (!_showingTextStyleOptions)
3301             [_textSelectionAssistant selectionChanged];
3302     } else if (!_page->editorState().isContentEditable)
3303         [_webSelectionAssistant selectionChanged];
3304     _selectionNeedsUpdate = NO;
3305     if (_shouldRestoreSelection) {
3306         [_webSelectionAssistant didEndScrollingOverflow];
3307         [_textSelectionAssistant didEndScrollingOverflow];
3308         _shouldRestoreSelection = NO;
3309     }
3310 }
3311
3312 - (void)_showPlaybackTargetPicker:(BOOL)hasVideo fromRect:(const IntRect&)elementRect
3313 {
3314     if (!_airPlayRoutePicker)
3315         _airPlayRoutePicker = adoptNS([[WKAirPlayRoutePicker alloc] initWithView:self]);
3316     [_airPlayRoutePicker show:hasVideo fromRect:elementRect];
3317 }
3318
3319 - (void)_showRunOpenPanel:(WebOpenPanelParameters*)parameters resultListener:(WebOpenPanelResultListenerProxy*)listener
3320 {
3321     ASSERT(!_fileUploadPanel);
3322     if (_fileUploadPanel)
3323         return;
3324
3325     _fileUploadPanel = adoptNS([[WKFileUploadPanel alloc] initWithView:self]);
3326     [_fileUploadPanel setDelegate:self];
3327     [_fileUploadPanel presentWithParameters:parameters resultListener:listener];
3328 }
3329
3330 - (void)fileUploadPanelDidDismiss:(WKFileUploadPanel *)fileUploadPanel
3331 {
3332     ASSERT(_fileUploadPanel.get() == fileUploadPanel);
3333
3334     [_fileUploadPanel setDelegate:nil];
3335     _fileUploadPanel = nil;
3336 }
3337
3338 #pragma mark - Implementation of UIWebTouchEventsGestureRecognizerDelegate.
3339
3340 - (BOOL)shouldIgnoreWebTouch
3341 {
3342     return NO;
3343 }
3344
3345 - (BOOL)isAnyTouchOverActiveArea:(NSSet *)touches
3346 {
3347     return YES;
3348 }
3349
3350 #pragma mark - Implementation of WKActionSheetAssistantDelegate.
3351
3352 - (const WebKit::InteractionInformationAtPosition&)positionInformationForActionSheetAssistant:(WKActionSheetAssistant *)assistant
3353 {
3354     return _positionInformation;
3355 }
3356
3357 - (void)updatePositionInformationForActionSheetAssistant:(WKActionSheetAssistant *)assistant
3358 {
3359     _hasValidPositionInformation = NO;
3360     _page->requestPositionInformation(_positionInformation.point);
3361 }
3362
3363 - (void)actionSheetAssistant:(WKActionSheetAssistant *)assistant performAction:(WebKit::SheetAction)action
3364 {
3365     _page->performActionOnElement((uint32_t)action);
3366 }
3367
3368 - (void)actionSheetAssistant:(WKActionSheetAssistant *)assistant openElementAtLocation:(CGPoint)location
3369 {
3370     [self _attemptClickAtLocation:location];
3371 }
3372
3373 - (void)actionSheetAssistant:(WKActionSheetAssistant *)assistant shareElementWithURL:(NSURL *)url rect:(CGRect)boundingRect
3374 {
3375     if (_textSelectionAssistant)