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