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