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