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