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