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