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