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