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