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