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