2 * Copyright (C) 2012-2014 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
27 #import "WKContentViewInteraction.h"
31 #import "APIUIClient.h"
32 #import "DataDetectorsUISPI.h"
33 #import "EditingRange.h"
34 #import "ManagedConfigurationSPI.h"
35 #import "NativeWebKeyboardEvent.h"
36 #import "NativeWebTouchEvent.h"
37 #import "SafariServicesSPI.h"
38 #import "SmartMagnificationController.h"
39 #import "TextInputSPI.h"
41 #import "WKActionSheetAssistant.h"
42 #import "WKFormInputControl.h"
43 #import "WKFormSelectControl.h"
44 #import "WKImagePreviewViewController.h"
45 #import "WKInspectorNodeSearchGestureRecognizer.h"
46 #import "WKNSURLExtras.h"
47 #import "WKUIDelegatePrivate.h"
48 #import "WKWebViewConfiguration.h"
49 #import "WKWebViewInternal.h"
50 #import "WKWebViewPrivate.h"
52 #import "WebIOSEventFactory.h"
53 #import "WebPageMessages.h"
54 #import "WebProcessProxy.h"
55 #import "_WKActivatedElementInfoInternal.h"
56 #import "_WKFormDelegate.h"
57 #import "_WKFormInputSession.h"
58 #import <CoreText/CTFont.h>
59 #import <CoreText/CTFontDescriptor.h>
60 #import <MobileCoreServices/UTCoreTypes.h>
61 #import <WebCore/Color.h>
62 #import <WebCore/CoreGraphicsSPI.h>
63 #import <WebCore/FloatQuad.h>
64 #import <WebCore/Pasteboard.h>
65 #import <WebCore/Path.h>
66 #import <WebCore/PathUtilities.h>
67 #import <WebCore/RuntimeApplicationChecksIOS.h>
68 #import <WebCore/Scrollbar.h>
69 #import <WebCore/SoftLinking.h>
70 #import <WebCore/TextIndicator.h>
71 #import <WebCore/WebEvent.h>
72 #import <WebKit/WebSelectionRect.h> // FIXME: WK2 should not include WebKit headers!
73 #import <WebKitSystemInterfaceIOS.h>
74 #import <wtf/RetainPtr.h>
76 @interface UIEvent(UIEventInternal)
77 @property (nonatomic, assign) UIKeyboardInputFlags _inputFlags;
80 using namespace WebCore;
81 using namespace WebKit;
85 WKSelectionDrawingInfo::WKSelectionDrawingInfo()
86 : type(SelectionType::None)
90 WKSelectionDrawingInfo::WKSelectionDrawingInfo(const EditorState& editorState)
92 if (editorState.selectionIsNone) {
93 type = SelectionType::None;
97 if (editorState.isInPlugin) {
98 type = SelectionType::Plugin;
102 type = SelectionType::Range;
103 auto& postLayoutData = editorState.postLayoutData();
104 caretRect = postLayoutData.caretRectAtEnd;
105 selectionRects = postLayoutData.selectionRects;
108 inline bool operator==(const WKSelectionDrawingInfo& a, const WKSelectionDrawingInfo& b)
110 if (a.type != b.type)
113 if (a.type == WKSelectionDrawingInfo::SelectionType::Range) {
114 if (a.caretRect != b.caretRect)
117 if (a.selectionRects.size() != b.selectionRects.size())
120 for (unsigned i = 0; i < a.selectionRects.size(); ++i) {
121 if (a.selectionRects[i].rect() != b.selectionRects[i].rect())
129 inline bool operator!=(const WKSelectionDrawingInfo& a, const WKSelectionDrawingInfo& b)
134 } // namespace WebKit
136 static const float highlightDelay = 0.12;
137 static const float tapAndHoldDelay = 0.75;
138 const CGFloat minimumTapHighlightRadius = 2.0;
140 @interface WKTextRange : UITextRange {
146 NSArray *_selectionRects;
147 NSUInteger _selectedTextLength;
149 @property (nonatomic) CGRect startRect;
150 @property (nonatomic) CGRect endRect;
151 @property (nonatomic) BOOL isNone;
152 @property (nonatomic) BOOL isRange;
153 @property (nonatomic) BOOL isEditable;
154 @property (nonatomic) NSUInteger selectedTextLength;
155 @property (copy, nonatomic) NSArray *selectionRects;
157 + (WKTextRange *)textRangeWithState:(BOOL)isNone isRange:(BOOL)isRange isEditable:(BOOL)isEditable startRect:(CGRect)startRect endRect:(CGRect)endRect selectionRects:(NSArray *)selectionRects selectedTextLength:(NSUInteger)selectedTextLength;
161 @interface WKTextPosition : UITextPosition {
162 CGRect _positionRect;
165 @property (nonatomic) CGRect positionRect;
167 + (WKTextPosition *)textPositionWithRect:(CGRect)positionRect;
171 @interface WKTextSelectionRect : UITextSelectionRect
173 @property (nonatomic, retain) WebSelectionRect *webRect;
175 + (NSArray *)textSelectionRectsWithWebRects:(NSArray *)webRects;
179 @interface WKAutocorrectionRects : UIWKAutocorrectionRects
180 + (WKAutocorrectionRects *)autocorrectionRectsWithRects:(CGRect)firstRect lastRect:(CGRect)lastRect;
183 @interface WKAutocorrectionContext : UIWKAutocorrectionContext
184 + (WKAutocorrectionContext *)autocorrectionContextWithData:(NSString *)beforeText markedText:(NSString *)markedText selectedText:(NSString *)selectedText afterText:(NSString *)afterText selectedRangeInMarkedText:(NSRange)range;
187 @interface UITextInteractionAssistant (UITextInteractionAssistant_Internal)
188 // FIXME: this needs to be moved from the internal header to the private.
189 - (id)initWithView:(UIResponder <UITextInput> *)view;
191 - (void)scheduleReanalysis;
194 @interface UITextInteractionAssistant (StagingToRemove)
195 - (void)scheduleReplacementsForText:(NSString *)text;
196 - (void)showTextServiceFor:(NSString *)selectedTerm fromRect:(CGRect)presentationRect;
197 - (void)scheduleChineseTransliterationForText:(NSString *)text;
198 - (void)showShareSheetFor:(NSString *)selectedTerm fromRect:(CGRect)presentationRect;
199 - (void)lookup:(NSString *)textWithContext fromRect:(CGRect)presentationRect;
202 @interface UIWKSelectionAssistant (StagingToRemove)
203 - (void)showTextServiceFor:(NSString *)selectedTerm fromRect:(CGRect)presentationRect;
204 - (void)showShareSheetFor:(NSString *)selectedTerm fromRect:(CGRect)presentationRect;
205 - (void)lookup:(NSString *)textWithContext fromRect:(CGRect)presentationRect;
208 @interface UIKeyboardImpl (StagingToRemove)
209 - (void)didHandleWebKeyEvent;
210 - (void)didHandleWebKeyEvent:(WebIOSEvent *)event;
211 - (void)deleteFromInputWithFlags:(NSUInteger)flags;
214 @interface UIView (UIViewInternalHack)
215 + (BOOL)_addCompletion:(void(^)(BOOL))completion;
218 #if __IPHONE_OS_VERSION_MIN_REQUIRED >= 90000
219 @protocol UISelectionInteractionAssistant;
220 #if HAVE(LINK_PREVIEW)
221 @interface UIPreviewItemController (StagingToRemove)
222 @property (strong, nonatomic, readonly) UIGestureRecognizer *presentationSecondaryGestureRecognizer;
228 @interface WKFormInputSession : NSObject <_WKFormInputSession>
230 - (instancetype)initWithContentView:(WKContentView *)view userObject:(NSObject <NSSecureCoding> *)userObject;
235 @implementation WKFormInputSession {
236 WKContentView *_contentView;
237 RetainPtr<NSObject <NSSecureCoding>> _userObject;
240 - (instancetype)initWithContentView:(WKContentView *)view userObject:(NSObject <NSSecureCoding> *)userObject
242 if (!(self = [super init]))
246 _userObject = userObject;
251 - (NSObject <NSSecureCoding> *)userObject
253 return _userObject.get();
258 return _contentView != nil;
261 - (NSString *)accessoryViewCustomButtonTitle
263 return [[[_contentView formAccessoryView] _autofill] title];
266 - (void)setAccessoryViewCustomButtonTitle:(NSString *)title
269 [[_contentView formAccessoryView] showAutoFillButtonWithTitle:title];
271 [[_contentView formAccessoryView] hideAutoFillButton];
272 if (UICurrentUserInterfaceIdiomIsPad())
273 [_contentView reloadInputViews];
283 @interface WKContentView (WKInteractionPrivate)
284 - (void)accessibilitySpeakSelectionSetContent:(NSString *)string;
287 @implementation WKContentView (WKInteraction)
289 static UIWebSelectionMode toUIWebSelectionMode(WKSelectionGranularity granularity)
291 switch (granularity) {
292 case WKSelectionGranularityDynamic:
293 return UIWebSelectionModeWeb;
294 case WKSelectionGranularityCharacter:
295 return UIWebSelectionModeTextOnly;
298 ASSERT_NOT_REACHED();
299 return UIWebSelectionModeWeb;
302 - (void)setupInteraction
304 if (!_interactionViewsContainerView) {
305 _interactionViewsContainerView = adoptNS([[UIView alloc] init]);
306 [_interactionViewsContainerView setOpaque:NO];
307 [_interactionViewsContainerView layer].anchorPoint = CGPointZero;
308 [self.superview addSubview:_interactionViewsContainerView.get()];
311 [self.layer addObserver:self forKeyPath:@"transform" options:NSKeyValueObservingOptionInitial context:nil];
313 #if ENABLE(TOUCH_EVENTS)
314 _touchEventGestureRecognizer = adoptNS([[UIWebTouchEventsGestureRecognizer alloc] initWithTarget:self action:@selector(_webTouchEventsRecognized:) touchDelegate:self]);
315 [_touchEventGestureRecognizer setDelegate:self];
316 [self addGestureRecognizer:_touchEventGestureRecognizer.get()];
319 _singleTapGestureRecognizer = adoptNS([[WKSyntheticClickTapGestureRecognizer alloc] initWithTarget:self action:@selector(_singleTapCommited:)]);
320 [_singleTapGestureRecognizer setDelegate:self];
321 [_singleTapGestureRecognizer setGestureRecognizedTarget:self action:@selector(_singleTapRecognized:)];
322 [_singleTapGestureRecognizer setResetTarget:self action:@selector(_singleTapDidReset:)];
323 [self addGestureRecognizer:_singleTapGestureRecognizer.get()];
325 _doubleTapGestureRecognizer = adoptNS([[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(_doubleTapRecognized:)]);
326 [_doubleTapGestureRecognizer setNumberOfTapsRequired:2];
327 [_doubleTapGestureRecognizer setDelegate:self];
328 [self addGestureRecognizer:_doubleTapGestureRecognizer.get()];
329 [_singleTapGestureRecognizer requireOtherGestureToFail:_doubleTapGestureRecognizer.get()];
331 _twoFingerDoubleTapGestureRecognizer = adoptNS([[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(_twoFingerDoubleTapRecognized:)]);
332 [_twoFingerDoubleTapGestureRecognizer setNumberOfTapsRequired:2];
333 [_twoFingerDoubleTapGestureRecognizer setNumberOfTouchesRequired:2];
334 [_twoFingerDoubleTapGestureRecognizer setDelegate:self];
335 [self addGestureRecognizer:_twoFingerDoubleTapGestureRecognizer.get()];
337 _highlightLongPressGestureRecognizer = adoptNS([[_UIWebHighlightLongPressGestureRecognizer alloc] initWithTarget:self action:@selector(_highlightLongPressRecognized:)]);
338 [_highlightLongPressGestureRecognizer setDelay:highlightDelay];
339 [_highlightLongPressGestureRecognizer setDelegate:self];
340 [self addGestureRecognizer:_highlightLongPressGestureRecognizer.get()];
342 _longPressGestureRecognizer = adoptNS([[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(_longPressRecognized:)]);
343 [_longPressGestureRecognizer setDelay:tapAndHoldDelay];
344 [_longPressGestureRecognizer setDelegate:self];
345 [self addGestureRecognizer:_longPressGestureRecognizer.get()];
347 #if HAVE(LINK_PREVIEW)
348 [self _registerPreview];
351 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_resetShowingTextStyle:) name:UIMenuControllerDidHideMenuNotification object:nil];
352 _showingTextStyleOptions = NO;
354 // FIXME: This should be called when we get notified that loading has completed.
355 [self useSelectionAssistantWithMode:toUIWebSelectionMode([[_webView configuration] selectionGranularity])];
357 _actionSheetAssistant = adoptNS([[WKActionSheetAssistant alloc] initWithView:self]);
358 [_actionSheetAssistant setDelegate:self];
359 _smartMagnificationController = std::make_unique<SmartMagnificationController>(self);
362 - (void)cleanupInteraction
364 _webSelectionAssistant = nil;
365 _textSelectionAssistant = nil;
366 _actionSheetAssistant = nil;
367 _smartMagnificationController = nil;
368 _didAccessoryTabInitiateFocus = NO;
369 [_formInputSession invalidate];
370 _formInputSession = nil;
371 [_highlightView removeFromSuperview];
373 if (_interactionViewsContainerView) {
374 [self.layer removeObserver:self forKeyPath:@"transform"];
375 [_interactionViewsContainerView removeFromSuperview];
376 _interactionViewsContainerView = nil;
379 [_touchEventGestureRecognizer setDelegate:nil];
380 [self removeGestureRecognizer:_touchEventGestureRecognizer.get()];
382 [_singleTapGestureRecognizer setDelegate:nil];
383 [_singleTapGestureRecognizer setGestureRecognizedTarget:nil action:nil];
384 [_singleTapGestureRecognizer setResetTarget:nil action:nil];
385 [self removeGestureRecognizer:_singleTapGestureRecognizer.get()];
387 [_highlightLongPressGestureRecognizer setDelegate:nil];
388 [self removeGestureRecognizer:_highlightLongPressGestureRecognizer.get()];
390 [_longPressGestureRecognizer setDelegate:nil];
391 [self removeGestureRecognizer:_longPressGestureRecognizer.get()];
393 [_doubleTapGestureRecognizer setDelegate:nil];
394 [self removeGestureRecognizer:_doubleTapGestureRecognizer.get()];
396 [_twoFingerDoubleTapGestureRecognizer setDelegate:nil];
397 [self removeGestureRecognizer:_twoFingerDoubleTapGestureRecognizer.get()];
399 _inspectorNodeSearchEnabled = NO;
400 if (_inspectorNodeSearchGestureRecognizer) {
401 [_inspectorNodeSearchGestureRecognizer setDelegate:nil];
402 [self removeGestureRecognizer:_inspectorNodeSearchGestureRecognizer.get()];
403 _inspectorNodeSearchGestureRecognizer = nil;
406 #if HAVE(LINK_PREVIEW)
407 [self _unregisterPreview];
410 if (_fileUploadPanel) {
411 [_fileUploadPanel setDelegate:nil];
412 [_fileUploadPanel dismiss];
413 _fileUploadPanel = nil;
417 - (void)_removeDefaultGestureRecognizers
419 [self removeGestureRecognizer:_touchEventGestureRecognizer.get()];
420 [self removeGestureRecognizer:_singleTapGestureRecognizer.get()];
421 [self removeGestureRecognizer:_highlightLongPressGestureRecognizer.get()];
422 [self removeGestureRecognizer:_doubleTapGestureRecognizer.get()];
423 [self removeGestureRecognizer:_twoFingerDoubleTapGestureRecognizer.get()];
426 - (void)_addDefaultGestureRecognizers
428 [self addGestureRecognizer:_touchEventGestureRecognizer.get()];
429 [self addGestureRecognizer:_singleTapGestureRecognizer.get()];
430 [self addGestureRecognizer:_highlightLongPressGestureRecognizer.get()];
431 [self addGestureRecognizer:_doubleTapGestureRecognizer.get()];
432 [self addGestureRecognizer:_twoFingerDoubleTapGestureRecognizer.get()];
435 - (UIView*)unscaledView
437 return _interactionViewsContainerView.get();
440 - (CGFloat)inverseScale
442 return 1 / [[self layer] transform].m11;
445 - (UIScrollView *)_scroller
447 return [_webView scrollView];
450 - (CGRect)unobscuredContentRect
452 return _page->unobscuredContentRect();
455 - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
457 ASSERT([keyPath isEqualToString:@"transform"]);
458 ASSERT(object == self.layer);
460 if ([UIView _isInAnimationBlock] && _page->editorState().selectionIsNone) {
461 // If the utility views are not already visible, we don't want them to become visible during the animation since
462 // they could not start from a reasonable state.
463 // This is not perfect since views could also get updated during the animation, in practice this is rare and the end state
465 [self _cancelInteraction];
466 [_interactionViewsContainerView setHidden:YES];
467 [UIView _addCompletion:^(BOOL){ [_interactionViewsContainerView setHidden:NO]; }];
470 _selectionNeedsUpdate = YES;
471 [self _updateChangedSelection:YES];
472 [self _updateTapHighlight];
475 - (void)_enableInspectorNodeSearch
477 _inspectorNodeSearchEnabled = YES;
479 [self _cancelInteraction];
481 [self _removeDefaultGestureRecognizers];
482 _inspectorNodeSearchGestureRecognizer = adoptNS([[WKInspectorNodeSearchGestureRecognizer alloc] initWithTarget:self action:@selector(_inspectorNodeSearchRecognized:)]);
483 [self addGestureRecognizer:_inspectorNodeSearchGestureRecognizer.get()];
486 - (void)_disableInspectorNodeSearch
488 _inspectorNodeSearchEnabled = NO;
490 [self _addDefaultGestureRecognizers];
491 [self removeGestureRecognizer:_inspectorNodeSearchGestureRecognizer.get()];
492 _inspectorNodeSearchGestureRecognizer = nil;
495 - (UIView *)hitTest:(CGPoint)point withEvent:(::UIEvent *)event
497 for (UIView *subView in [_interactionViewsContainerView.get() subviews]) {
498 UIView *hitView = [subView hitTest:[subView convertPoint:point fromView:self] withEvent:event];
502 return [super hitTest:point withEvent:event];
505 - (const InteractionInformationAtPosition&)positionInformation
507 return _positionInformation;
510 - (void)setInputDelegate:(id <UITextInputDelegate>)inputDelegate
512 _inputDelegate = inputDelegate;
515 - (id <UITextInputDelegate>)inputDelegate
517 return _inputDelegate;
520 - (CGPoint)lastInteractionLocation
522 return _lastInteractionLocation;
530 - (BOOL)canBecomeFirstResponder
532 // We might want to return something else
533 // if we decide to enable/disable interaction programmatically.
537 - (BOOL)becomeFirstResponder
539 BOOL didBecomeFirstResponder = [super becomeFirstResponder];
540 if (didBecomeFirstResponder)
541 [_textSelectionAssistant activateSelection];
543 return didBecomeFirstResponder;
546 - (BOOL)resignFirstResponder
548 // FIXME: Maybe we should call resignFirstResponder on the superclass
549 // and do nothing if the return value is NO.
551 if (!_webView->_activeFocusedStateRetainCount) {
552 // We need to complete the editing operation before we blur the element.
553 [_inputPeripheral endEditing];
554 _page->blurAssistedNode();
557 [self _cancelInteraction];
558 [_webSelectionAssistant resignedFirstResponder];
559 [_textSelectionAssistant deactivateSelection];
561 return [super resignFirstResponder];
564 #if ENABLE(TOUCH_EVENTS)
565 - (void)_webTouchEventsRecognized:(UIWebTouchEventsGestureRecognizer *)gestureRecognizer
567 const _UIWebTouchEvent* lastTouchEvent = gestureRecognizer.lastTouchEvent;
568 NativeWebTouchEvent nativeWebTouchEvent(lastTouchEvent);
570 _lastInteractionLocation = lastTouchEvent->locationInDocumentCoordinates;
571 nativeWebTouchEvent.setCanPreventNativeGestures(!_canSendTouchEventsAsynchronously || [gestureRecognizer isDefaultPrevented]);
573 if (_canSendTouchEventsAsynchronously)
574 _page->handleTouchEventAsynchronously(nativeWebTouchEvent);
576 _page->handleTouchEventSynchronously(nativeWebTouchEvent);
578 if (nativeWebTouchEvent.allTouchPointsAreReleased())
579 _canSendTouchEventsAsynchronously = NO;
583 - (void)_inspectorNodeSearchRecognized:(UIGestureRecognizer *)gestureRecognizer
585 ASSERT(_inspectorNodeSearchEnabled);
587 CGPoint point = [gestureRecognizer locationInView:self];
589 switch (gestureRecognizer.state) {
590 case UIGestureRecognizerStateBegan:
591 case UIGestureRecognizerStateChanged:
592 _page->inspectorNodeSearchMovedToPosition(point);
594 case UIGestureRecognizerStateEnded:
595 case UIGestureRecognizerStateCancelled:
596 default: // To ensure we turn off node search.
597 _page->inspectorNodeSearchEndedAtPosition(point);
602 static FloatQuad inflateQuad(const FloatQuad& quad, float inflateSize)
604 // We sort the output points like this (as expected by the highlight view):
609 // 1) Sort the points horizontally.
610 FloatPoint points[4] = { quad.p1(), quad.p4(), quad.p2(), quad.p3() };
611 if (points[0].x() > points[1].x())
612 std::swap(points[0], points[1]);
613 if (points[2].x() > points[3].x())
614 std::swap(points[2], points[3]);
616 if (points[0].x() > points[2].x())
617 std::swap(points[0], points[2]);
618 if (points[1].x() > points[3].x())
619 std::swap(points[1], points[3]);
621 if (points[1].x() > points[2].x())
622 std::swap(points[1], points[2]);
624 // 2) Swap them vertically to have the output points [p2, p1, p3, p4].
625 if (points[1].y() < points[0].y())
626 std::swap(points[0], points[1]);
627 if (points[3].y() < points[2].y())
628 std::swap(points[2], points[3]);
630 // 3) Adjust the positions.
631 points[0].move(-inflateSize, -inflateSize);
632 points[1].move(-inflateSize, inflateSize);
633 points[2].move(inflateSize, -inflateSize);
634 points[3].move(inflateSize, inflateSize);
636 return FloatQuad(points[1], points[0], points[2], points[3]);
639 #if ENABLE(TOUCH_EVENTS)
640 - (void)_webTouchEvent:(const WebKit::NativeWebTouchEvent&)touchEvent preventsNativeGestures:(BOOL)preventsNativeGesture
642 if (preventsNativeGesture) {
643 _highlightLongPressCanClick = NO;
645 _canSendTouchEventsAsynchronously = YES;
646 [_touchEventGestureRecognizer setDefaultPrevented:YES];
651 static inline bool highlightedQuadsAreSmallerThanRect(const Vector<FloatQuad>& quads, const FloatRect& rect)
653 for (size_t i = 0; i < quads.size(); ++i) {
654 FloatRect boundingBox = quads[i].boundingBox();
655 if (boundingBox.width() > rect.width() || boundingBox.height() > rect.height())
661 static NSValue *nsSizeForTapHighlightBorderRadius(WebCore::IntSize borderRadius)
663 return [NSValue valueWithCGSize:CGSizeMake(borderRadius.width() + minimumTapHighlightRadius, borderRadius.height() + minimumTapHighlightRadius)];
666 - (void)_updateTapHighlight
668 if (![_highlightView superview])
672 RetainPtr<UIColor> highlightUIKitColor = adoptNS([[UIColor alloc] initWithCGColor:cachedCGColor(_tapHighlightInformation.color, WebCore::ColorSpaceDeviceRGB)]);
673 [_highlightView setColor:highlightUIKitColor.get()];
676 CGFloat selfScale = self.layer.transform.m11;
677 bool allHighlightRectsAreRectilinear = true;
678 float deviceScaleFactor = _page->deviceScaleFactor();
679 const Vector<WebCore::FloatQuad>& highlightedQuads = _tapHighlightInformation.quads;
680 const size_t quadCount = highlightedQuads.size();
681 RetainPtr<NSMutableArray> rects = adoptNS([[NSMutableArray alloc] initWithCapacity:static_cast<const NSUInteger>(quadCount)]);
682 for (size_t i = 0; i < quadCount; ++i) {
683 const FloatQuad& quad = highlightedQuads[i];
684 if (quad.isRectilinear()) {
685 FloatRect boundingBox = quad.boundingBox();
686 boundingBox.scale(selfScale);
687 boundingBox.inflate(minimumTapHighlightRadius);
688 CGRect pixelAlignedRect = static_cast<CGRect>(encloseRectToDevicePixels(boundingBox, deviceScaleFactor));
689 [rects addObject:[NSValue valueWithCGRect:pixelAlignedRect]];
691 allHighlightRectsAreRectilinear = false;
697 if (allHighlightRectsAreRectilinear)
698 [_highlightView setFrames:rects.get() boundaryRect:_page->exposedContentRect()];
700 RetainPtr<NSMutableArray> quads = adoptNS([[NSMutableArray alloc] initWithCapacity:static_cast<const NSUInteger>(quadCount)]);
701 for (size_t i = 0; i < quadCount; ++i) {
702 FloatQuad quad = highlightedQuads[i];
703 quad.scale(selfScale, selfScale);
704 FloatQuad extendedQuad = inflateQuad(quad, minimumTapHighlightRadius);
705 [quads addObject:[NSValue valueWithCGPoint:extendedQuad.p1()]];
706 [quads addObject:[NSValue valueWithCGPoint:extendedQuad.p2()]];
707 [quads addObject:[NSValue valueWithCGPoint:extendedQuad.p3()]];
708 [quads addObject:[NSValue valueWithCGPoint:extendedQuad.p4()]];
710 [_highlightView setQuads:quads.get() boundaryRect:_page->exposedContentRect()];
713 RetainPtr<NSMutableArray> borderRadii = adoptNS([[NSMutableArray alloc] initWithCapacity:4]);
714 [borderRadii addObject:nsSizeForTapHighlightBorderRadius(_tapHighlightInformation.topLeftRadius)];
715 [borderRadii addObject:nsSizeForTapHighlightBorderRadius(_tapHighlightInformation.topRightRadius)];
716 [borderRadii addObject:nsSizeForTapHighlightBorderRadius(_tapHighlightInformation.bottomLeftRadius)];
717 [borderRadii addObject:nsSizeForTapHighlightBorderRadius(_tapHighlightInformation.bottomRightRadius)];
718 [_highlightView setCornerRadii:borderRadii.get()];
721 - (void)_showTapHighlight
723 if (!highlightedQuadsAreSmallerThanRect(_tapHighlightInformation.quads, _page->unobscuredContentRect()))
726 if (!_highlightView) {
727 _highlightView = adoptNS([[_UIHighlightView alloc] initWithFrame:CGRectZero]);
728 [_highlightView setOpaque:NO];
729 [_highlightView setCornerRadius:minimumTapHighlightRadius];
731 [_highlightView layer].opacity = 1;
732 [_interactionViewsContainerView addSubview:_highlightView.get()];
733 [self _updateTapHighlight];
736 - (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
738 if (!_isTapHighlightIDValid || _latestTapHighlightID != requestID)
741 _isTapHighlightIDValid = NO;
743 _tapHighlightInformation.color = color;
744 _tapHighlightInformation.quads = highlightedQuads;
745 _tapHighlightInformation.topLeftRadius = topLeftRadius;
746 _tapHighlightInformation.topRightRadius = topRightRadius;
747 _tapHighlightInformation.bottomLeftRadius = bottomLeftRadius;
748 _tapHighlightInformation.bottomRightRadius = bottomRightRadius;
750 if (_potentialTapInProgress) {
751 _hasTapHighlightForPotentialTap = YES;
755 [self _showTapHighlight];
758 - (void)_cancelLongPressGestureRecognizer
760 [_highlightLongPressGestureRecognizer cancel];
765 [self _cancelLongPressGestureRecognizer];
766 [self _cancelInteraction];
769 - (void)_overflowScrollingWillBegin
771 [_webSelectionAssistant willStartScrollingOverflow];
772 [_textSelectionAssistant willStartScrollingOverflow];
775 - (void)_overflowScrollingDidEnd
777 // If scrolling ends before we've received a selection update,
778 // we postpone showing the selection until the update is received.
779 if (!_selectionNeedsUpdate) {
780 _shouldRestoreSelection = YES;
783 [self _updateChangedSelection];
784 [_webSelectionAssistant didEndScrollingOverflow];
785 [_textSelectionAssistant didEndScrollingOverflow];
788 - (BOOL)_requiresKeyboardWhenFirstResponder
790 // FIXME: We should add the logic to handle keyboard visibility during focus redirects.
791 switch (_assistedNodeInformation.elementType) {
792 case InputType::None:
794 case InputType::Select:
795 return !UICurrentUserInterfaceIdiomIsPad();
796 case InputType::Date:
797 case InputType::Month:
798 case InputType::DateTimeLocal:
799 case InputType::Time:
800 return !UICurrentUserInterfaceIdiomIsPad();
802 return !_assistedNodeInformation.isReadOnly;
807 - (BOOL)_requiresKeyboardResetOnReload
812 - (void)_displayFormNodeInputView
814 [self _zoomToFocusRect:_assistedNodeInformation.elementRect
815 selectionRect: _didAccessoryTabInitiateFocus ? IntRect() : _assistedNodeInformation.selectionRect
816 fontSize:_assistedNodeInformation.nodeFontSize
817 minimumScale:_assistedNodeInformation.minimumScaleFactor
818 maximumScale:_assistedNodeInformation.maximumScaleFactor
819 allowScaling:(_assistedNodeInformation.allowsUserScaling && !UICurrentUserInterfaceIdiomIsPad())
820 forceScroll:[self requiresAccessoryView]];
821 _didAccessoryTabInitiateFocus = NO;
822 [self _ensureFormAccessoryView];
823 [self _updateAccessory];
826 - (UIView *)inputView
828 if (_assistedNodeInformation.elementType == InputType::None)
831 if (!_inputPeripheral)
832 _inputPeripheral = adoptNS(_assistedNodeInformation.elementType == InputType::Select ? [WKFormSelectControl createPeripheralWithView:self] : [WKFormInputControl createPeripheralWithView:self]);
834 [self _displayFormNodeInputView];
836 return [_inputPeripheral assistantView];
839 - (CGRect)_selectionClipRect
841 if (_assistedNodeInformation.elementType == InputType::None)
843 return _page->editorState().postLayoutData().selectionClipRect;
846 - (BOOL)gestureRecognizer:(UIGestureRecognizer *)preventingGestureRecognizer canPreventGestureRecognizer:(UIGestureRecognizer *)preventedGestureRecognizer
848 // A long-press gesture can not be recognized while panning, but a pan can be recognized
849 // during a long-press gesture.
850 BOOL shouldNotPreventScrollViewGestures = preventingGestureRecognizer == _highlightLongPressGestureRecognizer || preventingGestureRecognizer == _longPressGestureRecognizer;
851 return !(shouldNotPreventScrollViewGestures
852 && ([preventedGestureRecognizer isKindOfClass:NSClassFromString(@"UIScrollViewPanGestureRecognizer")] || [preventedGestureRecognizer isKindOfClass:NSClassFromString(@"UIScrollViewPinchGestureRecognizer")]));
855 - (BOOL)gestureRecognizer:(UIGestureRecognizer *)preventedGestureRecognizer canBePreventedByGestureRecognizer:(UIGestureRecognizer *)preventingGestureRecognizer {
856 // 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.
857 if ((preventingGestureRecognizer == _textSelectionAssistant.get().loupeGesture || [_webSelectionAssistant isSelectionGestureRecognizer:preventingGestureRecognizer])
858 && (preventedGestureRecognizer == _highlightLongPressGestureRecognizer || preventedGestureRecognizer == _longPressGestureRecognizer)) {
865 static inline bool isSamePair(UIGestureRecognizer *a, UIGestureRecognizer *b, UIGestureRecognizer *x, UIGestureRecognizer *y)
867 return (a == x && b == y) || (b == x && a == y);
870 - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer*)otherGestureRecognizer
872 if (isSamePair(gestureRecognizer, otherGestureRecognizer, _highlightLongPressGestureRecognizer.get(), _longPressGestureRecognizer.get()))
875 if (isSamePair(gestureRecognizer, otherGestureRecognizer, _highlightLongPressGestureRecognizer.get(), _webSelectionAssistant.get().selectionLongPressRecognizer))
878 if (isSamePair(gestureRecognizer, otherGestureRecognizer, _singleTapGestureRecognizer.get(), _textSelectionAssistant.get().singleTapGesture))
881 if (isSamePair(gestureRecognizer, otherGestureRecognizer, _highlightLongPressGestureRecognizer.get(), _previewSecondaryGestureRecognizer.get()))
884 if (isSamePair(gestureRecognizer, otherGestureRecognizer, _highlightLongPressGestureRecognizer.get(), _previewGestureRecognizer.get()))
890 - (void)_showImageSheet
892 [_actionSheetAssistant showImageSheet];
895 - (void)_showLinkSheet
897 [_actionSheetAssistant showLinkSheet];
900 - (void)_showDataDetectorsSheet
902 [_actionSheetAssistant showDataDetectorsSheet];
905 - (SEL)_actionForLongPress
907 if (!_positionInformation.touchCalloutEnabled)
910 if (_positionInformation.clickableElementName == "IMG")
911 return @selector(_showImageSheet);
912 else if (_positionInformation.clickableElementName == "A") {
913 NSURL *targetURL = [NSURL URLWithString:_positionInformation.url];
914 if ([[getDDDetectionControllerClass() tapAndHoldSchemes] containsObject:[targetURL scheme]])
915 return @selector(_showDataDetectorsSheet);
916 return @selector(_showLinkSheet);
921 - (void)ensurePositionInformationIsUpToDate:(CGPoint)point
923 if (!_hasValidPositionInformation || roundedIntPoint(point) != _positionInformation.point) {
924 _page->getPositionInformation(roundedIntPoint(point), _positionInformation);
925 _hasValidPositionInformation = YES;
929 - (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
931 CGPoint point = [gestureRecognizer locationInView:self];
933 if (gestureRecognizer == _highlightLongPressGestureRecognizer
934 || gestureRecognizer == _doubleTapGestureRecognizer
935 || gestureRecognizer == _twoFingerDoubleTapGestureRecognizer
936 || gestureRecognizer == _singleTapGestureRecognizer) {
938 if (_textSelectionAssistant) {
939 // Request information about the position with sync message.
940 // If the assisted node is the same, prevent the gesture.
941 _page->getPositionInformation(roundedIntPoint(point), _positionInformation);
942 _hasValidPositionInformation = YES;
943 if (_positionInformation.nodeAtPositionIsAssistedNode)
948 if (gestureRecognizer == _highlightLongPressGestureRecognizer) {
949 if (_textSelectionAssistant) {
950 // This is a different node than the assisted one.
951 // Prevent the gesture if there is no node.
952 // Allow the gesture if it is a node that wants highlight or if there is an action for it.
953 if (_positionInformation.clickableElementName.isNull())
955 return [self _actionForLongPress] != nil;
957 // We still have no idea about what is at the location.
958 // Send and async message to find out.
959 _hasValidPositionInformation = NO;
960 _page->requestPositionInformation(roundedIntPoint(point));
965 if (gestureRecognizer == _longPressGestureRecognizer) {
966 // Use the information retrieved with one of the previous calls
967 // to gestureRecognizerShouldBegin.
968 // Force a sync call if not ready yet.
969 [self ensurePositionInformationIsUpToDate:point];
971 if (_textSelectionAssistant) {
972 // Prevent the gesture if it is the same node.
973 if (_positionInformation.nodeAtPositionIsAssistedNode)
976 // Prevent the gesture if there is no action for the node.
977 return [self _actionForLongPress] != nil;
984 - (void)_cancelInteraction
986 _isTapHighlightIDValid = NO;
987 [_highlightView removeFromSuperview];
990 - (void)_finishInteraction
992 _isTapHighlightIDValid = NO;
993 [UIView animateWithDuration:0.1
995 [_highlightView layer].opacity = 0;
997 completion:^(BOOL finished){
999 [_highlightView removeFromSuperview];
1003 - (BOOL)hasSelectablePositionAtPoint:(CGPoint)point
1005 if (_inspectorNodeSearchEnabled)
1008 [self ensurePositionInformationIsUpToDate:point];
1009 return _positionInformation.isSelectable;
1012 - (BOOL)pointIsNearMarkedText:(CGPoint)point
1014 [self ensurePositionInformationIsUpToDate:point];
1015 return _positionInformation.isNearMarkedText;
1018 - (BOOL)pointIsInAssistedNode:(CGPoint)point
1020 [self ensurePositionInformationIsUpToDate:point];
1021 return _positionInformation.nodeAtPositionIsAssistedNode;
1024 - (NSArray *)webSelectionRects
1026 const auto& selectionRects = _page->editorState().postLayoutData().selectionRects;
1027 unsigned size = selectionRects.size();
1031 NSMutableArray *webRects = [NSMutableArray arrayWithCapacity:size];
1032 for (unsigned i = 0; i < size; i++) {
1033 const WebCore::SelectionRect& coreRect = selectionRects[i];
1034 WebSelectionRect *webRect = [WebSelectionRect selectionRect];
1035 webRect.rect = coreRect.rect();
1036 webRect.writingDirection = coreRect.direction() == LTR ? WKWritingDirectionLeftToRight : WKWritingDirectionRightToLeft;
1037 webRect.isLineBreak = coreRect.isLineBreak();
1038 webRect.isFirstOnLine = coreRect.isFirstOnLine();
1039 webRect.isLastOnLine = coreRect.isLastOnLine();
1040 webRect.containsStart = coreRect.containsStart();
1041 webRect.containsEnd = coreRect.containsEnd();
1042 webRect.isInFixedPosition = coreRect.isInFixedPosition();
1043 webRect.isHorizontal = coreRect.isHorizontal();
1044 [webRects addObject:webRect];
1050 - (void)_highlightLongPressRecognized:(UILongPressGestureRecognizer *)gestureRecognizer
1052 ASSERT(gestureRecognizer == _highlightLongPressGestureRecognizer);
1054 _lastInteractionLocation = gestureRecognizer.startPoint;
1056 switch ([gestureRecognizer state]) {
1057 case UIGestureRecognizerStateBegan:
1058 _highlightLongPressCanClick = YES;
1059 cancelPotentialTapIfNecessary(self);
1060 _page->tapHighlightAtPosition([gestureRecognizer startPoint], ++_latestTapHighlightID);
1061 _isTapHighlightIDValid = YES;
1063 case UIGestureRecognizerStateEnded:
1064 if (_highlightLongPressCanClick && !_positionInformation.clickableElementName.isEmpty()) {
1065 [self _attemptClickAtLocation:[gestureRecognizer startPoint]];
1066 [self _finishInteraction];
1068 [self _cancelInteraction];
1069 _highlightLongPressCanClick = NO;
1071 case UIGestureRecognizerStateCancelled:
1072 [self _cancelInteraction];
1073 _highlightLongPressCanClick = NO;
1080 - (void)_longPressRecognized:(UILongPressGestureRecognizer *)gestureRecognizer
1082 ASSERT(gestureRecognizer == _longPressGestureRecognizer);
1084 _lastInteractionLocation = gestureRecognizer.startPoint;
1086 if ([gestureRecognizer state] == UIGestureRecognizerStateBegan) {
1087 SEL action = [self _actionForLongPress];
1089 [self performSelector:action];
1090 [self _cancelLongPressGestureRecognizer];
1095 - (void)_singleTapRecognized:(UITapGestureRecognizer *)gestureRecognizer
1097 ASSERT(gestureRecognizer == _singleTapGestureRecognizer);
1098 ASSERT(!_potentialTapInProgress);
1100 _page->potentialTapAtPosition(gestureRecognizer.location, ++_latestTapHighlightID);
1101 _potentialTapInProgress = YES;
1102 _isTapHighlightIDValid = YES;
1105 static void cancelPotentialTapIfNecessary(WKContentView* contentView)
1107 if (contentView->_potentialTapInProgress) {
1108 contentView->_potentialTapInProgress = NO;
1109 [contentView _cancelInteraction];
1110 contentView->_page->cancelPotentialTap();
1114 - (void)_singleTapDidReset:(UITapGestureRecognizer *)gestureRecognizer
1116 ASSERT(gestureRecognizer == _singleTapGestureRecognizer);
1117 cancelPotentialTapIfNecessary(self);
1120 - (void)_commitPotentialTapFailed
1122 [self _cancelInteraction];
1125 - (void)_singleTapCommited:(UITapGestureRecognizer *)gestureRecognizer
1127 ASSERT(gestureRecognizer == _singleTapGestureRecognizer);
1129 if (![self isFirstResponder])
1130 [self becomeFirstResponder];
1132 if (_webSelectionAssistant && ![_webSelectionAssistant shouldHandleSingleTapAtPoint:gestureRecognizer.location]) {
1133 [self _singleTapDidReset:gestureRecognizer];
1137 ASSERT(_potentialTapInProgress);
1139 // We don't want to clear the selection if it is in editable content.
1140 // The selection could have been set by autofocusing on page load and not
1141 // reflected in the UI process since the user was not interacting with the page.
1142 if (!_page->editorState().isContentEditable)
1143 [_webSelectionAssistant clearSelection];
1145 _lastInteractionLocation = gestureRecognizer.location;
1147 _potentialTapInProgress = NO;
1149 if (_hasTapHighlightForPotentialTap) {
1150 [self _showTapHighlight];
1151 _hasTapHighlightForPotentialTap = NO;
1154 _page->commitPotentialTap();
1156 [self _finishInteraction];
1159 - (void)_doubleTapRecognized:(UITapGestureRecognizer *)gestureRecognizer
1161 _lastInteractionLocation = gestureRecognizer.location;
1163 _smartMagnificationController->handleSmartMagnificationGesture(gestureRecognizer.location);
1166 - (void)_twoFingerDoubleTapRecognized:(UITapGestureRecognizer *)gestureRecognizer
1168 _lastInteractionLocation = gestureRecognizer.location;
1170 _smartMagnificationController->handleResetMagnificationGesture(gestureRecognizer.location);
1173 - (void)_attemptClickAtLocation:(CGPoint)location
1175 if (![self isFirstResponder])
1176 [self becomeFirstResponder];
1178 _page->handleTap(location);
1181 - (void)useSelectionAssistantWithMode:(UIWebSelectionMode)selectionMode
1183 if (selectionMode == UIWebSelectionModeWeb) {
1184 if (_textSelectionAssistant) {
1185 [_textSelectionAssistant deactivateSelection];
1186 _textSelectionAssistant = nil;
1188 if (!_webSelectionAssistant)
1189 _webSelectionAssistant = adoptNS([[UIWKSelectionAssistant alloc] initWithView:self]);
1190 } else if (selectionMode == UIWebSelectionModeTextOnly) {
1191 if (_webSelectionAssistant)
1192 _webSelectionAssistant = nil;
1194 if (!_textSelectionAssistant)
1195 _textSelectionAssistant = adoptNS([[UIWKTextInteractionAssistant alloc] initWithView:self]);
1197 // Reset the gesture recognizers in case editibility has changed.
1198 [_textSelectionAssistant setGestureRecognizers];
1201 if (self.isFirstResponder)
1202 [_textSelectionAssistant activateSelection];
1206 - (void)clearSelection
1208 _page->clearSelection();
1211 - (void)_positionInformationDidChange:(const InteractionInformationAtPosition&)info
1213 _positionInformation = info;
1214 _hasValidPositionInformation = YES;
1215 if (_actionSheetAssistant)
1216 [_actionSheetAssistant updateSheetPosition];
1219 - (void)_willStartScrollingOrZooming
1221 [_webSelectionAssistant willStartScrollingOrZoomingPage];
1222 [_textSelectionAssistant willStartScrollingOverflow];
1225 - (void)scrollViewWillStartPanOrPinchGesture
1227 _canSendTouchEventsAsynchronously = YES;
1230 - (void)_didEndScrollingOrZooming
1232 [_webSelectionAssistant didEndScrollingOrZoomingPage];
1233 [_textSelectionAssistant didEndScrollingOverflow];
1236 - (BOOL)requiresAccessoryView
1238 switch (_assistedNodeInformation.elementType) {
1239 case InputType::None:
1241 case InputType::Text:
1242 case InputType::Password:
1243 case InputType::Search:
1244 case InputType::Email:
1245 case InputType::URL:
1246 case InputType::Phone:
1247 case InputType::Number:
1248 case InputType::NumberPad:
1249 return !UICurrentUserInterfaceIdiomIsPad();
1250 case InputType::ContentEditable:
1251 case InputType::TextArea:
1252 return !UICurrentUserInterfaceIdiomIsPad();
1253 case InputType::Select:
1254 case InputType::Date:
1255 case InputType::DateTime:
1256 case InputType::DateTimeLocal:
1257 case InputType::Month:
1258 case InputType::Week:
1259 case InputType::Time:
1260 return !UICurrentUserInterfaceIdiomIsPad();
1264 - (void)_ensureFormAccessoryView
1266 if (_formAccessoryView)
1269 #if __IPHONE_OS_VERSION_MIN_REQUIRED >= 90000
1270 _formAccessoryView = adoptNS([[UIWebFormAccessory alloc] initWithInputAssistantItem:self.inputAssistantItem]);
1272 _formAccessoryView = adoptNS([[UIWebFormAccessory alloc] init]);
1274 [_formAccessoryView setDelegate:self];
1277 - (UIView *)inputAccessoryView
1279 if (![self requiresAccessoryView])
1282 return self.formAccessoryView;
1285 - (NSArray *)supportedPasteboardTypesForCurrentSelection
1287 if (_page->editorState().selectionIsNone)
1290 static NSMutableArray *richTypes = nil;
1291 static NSMutableArray *plainTextTypes = nil;
1292 if (!plainTextTypes) {
1293 plainTextTypes = [[NSMutableArray alloc] init];
1294 [plainTextTypes addObject:(id)kUTTypeURL];
1295 [plainTextTypes addObjectsFromArray:UIPasteboardTypeListString];
1297 richTypes = [[NSMutableArray alloc] init];
1298 [richTypes addObject:WebArchivePboardType];
1299 [richTypes addObjectsFromArray:UIPasteboardTypeListImage];
1300 [richTypes addObjectsFromArray:plainTextTypes];
1303 return (_page->editorState().isContentRichlyEditable) ? richTypes : plainTextTypes;
1306 - (void)_lookup:(CGPoint)point
1308 RetainPtr<WKContentView> view = self;
1309 _page->getLookupContextAtPoint(WebCore::IntPoint(point), [view](const String& string, CallbackBase::Error error) {
1310 if (error != CallbackBase::Error::None)
1315 CGRect presentationRect = view->_page->editorState().selectionIsRange ? view->_page->editorState().postLayoutData().selectionRects[0].rect() : view->_page->editorState().postLayoutData().caretRectAtStart;
1317 if (view->_textSelectionAssistant && [view->_textSelectionAssistant respondsToSelector:@selector(lookup:fromRect:)])
1318 [view->_textSelectionAssistant lookup:string fromRect:presentationRect];
1319 else if (view->_webSelectionAssistant && [view->_webSelectionAssistant respondsToSelector:@selector(lookup:fromRect:)])
1320 [view->_webSelectionAssistant lookup:string fromRect:presentationRect];
1324 - (void)_share:(id)sender
1326 RetainPtr<WKContentView> view = self;
1327 _page->getSelectionOrContentsAsString([view](const String& string, CallbackBase::Error error) {
1328 if (error != CallbackBase::Error::None)
1333 CGRect presentationRect = view->_page->editorState().postLayoutData().selectionRects[0].rect();
1335 if (view->_textSelectionAssistant && [view->_textSelectionAssistant respondsToSelector:@selector(showShareSheetFor:fromRect:)])
1336 [view->_textSelectionAssistant showShareSheetFor:string fromRect:presentationRect];
1337 else if (view->_webSelectionAssistant && [view->_webSelectionAssistant respondsToSelector:@selector(showShareSheetFor:fromRect:)])
1338 [view->_webSelectionAssistant showShareSheetFor:string fromRect:presentationRect];
1342 - (void)_addShortcut:(id)sender
1344 if (_textSelectionAssistant && [_textSelectionAssistant respondsToSelector:@selector(showTextServiceFor:fromRect:)])
1345 [_textSelectionAssistant showTextServiceFor:[self selectedText] fromRect:_page->editorState().postLayoutData().selectionRects[0].rect()];
1346 else if (_webSelectionAssistant && [_webSelectionAssistant respondsToSelector:@selector(showTextServiceFor:fromRect:)])
1347 [_webSelectionAssistant showTextServiceFor:[self selectedText] fromRect:_page->editorState().postLayoutData().selectionRects[0].rect()];
1350 - (NSString *)selectedText
1352 return (NSString *)_page->editorState().postLayoutData().wordAtSelection;
1355 - (BOOL)isReplaceAllowed
1357 return _page->editorState().postLayoutData().isReplaceAllowed;
1360 - (void)replaceText:(NSString *)text withText:(NSString *)word
1362 _page->replaceSelectedText(text, word);
1365 - (void)selectWordBackward
1367 _page->selectWordBackward();
1370 - (void)_promptForReplace:(id)sender
1372 const auto& wordAtSelection = _page->editorState().postLayoutData().wordAtSelection;
1373 if (wordAtSelection.isEmpty())
1376 if ([_textSelectionAssistant respondsToSelector:@selector(scheduleReplacementsForText:)])
1377 [_textSelectionAssistant scheduleReplacementsForText:wordAtSelection];
1380 - (void)_transliterateChinese:(id)sender
1382 if ([_textSelectionAssistant respondsToSelector:@selector(scheduleChineseTransliterationForText:)])
1383 [_textSelectionAssistant scheduleChineseTransliterationForText:_page->editorState().postLayoutData().wordAtSelection];
1386 - (void)_reanalyze:(id)sender
1388 [_textSelectionAssistant scheduleReanalysis];
1391 - (void)replace:(id)sender
1393 [[UIKeyboardImpl sharedInstance] replaceText:sender];
1396 - (NSDictionary *)textStylingAtPosition:(UITextPosition *)position inDirection:(UITextStorageDirection)direction
1398 if (!position || !_page->editorState().isContentRichlyEditable)
1401 NSMutableDictionary* result = [NSMutableDictionary dictionary];
1403 auto typingAttributes = _page->editorState().postLayoutData().typingAttributes;
1404 CTFontSymbolicTraits symbolicTraits = 0;
1405 if (typingAttributes & AttributeBold)
1406 symbolicTraits |= kCTFontBoldTrait;
1407 if (typingAttributes & AttributeItalics)
1408 symbolicTraits |= kCTFontTraitItalic;
1410 // We chose a random font family and size.
1411 // What matters are the traits but the caller expects a font object
1412 // in the dictionary for NSFontAttributeName.
1413 RetainPtr<CTFontDescriptorRef> fontDescriptor = adoptCF(CTFontDescriptorCreateWithNameAndSize(CFSTR("Helvetica"), 10));
1415 fontDescriptor = adoptCF(CTFontDescriptorCreateCopyWithSymbolicTraits(fontDescriptor.get(), symbolicTraits, symbolicTraits));
1417 RetainPtr<CTFontRef> font = adoptCF(CTFontCreateWithFontDescriptor(fontDescriptor.get(), 10, nullptr));
1419 [result setObject:(id)font.get() forKey:NSFontAttributeName];
1421 if (typingAttributes & AttributeUnderline)
1422 [result setObject:[NSNumber numberWithInt:NSUnderlineStyleSingle] forKey:NSUnderlineStyleAttributeName];
1427 - (BOOL)canPerformAction:(SEL)action withSender:(id)sender
1429 BOOL hasWebSelection = _webSelectionAssistant && !CGRectIsEmpty(_webSelectionAssistant.get().selectionFrame);
1431 if (action == @selector(_arrowKey:))
1432 return [self isFirstResponder];
1434 if (action == @selector(_showTextStyleOptions:))
1435 return _page->editorState().isContentRichlyEditable && _page->editorState().selectionIsRange && !_showingTextStyleOptions;
1436 if (_showingTextStyleOptions)
1437 return (action == @selector(toggleBoldface:) || action == @selector(toggleItalics:) || action == @selector(toggleUnderline:));
1438 if (action == @selector(toggleBoldface:) || action == @selector(toggleItalics:) || action == @selector(toggleUnderline:))
1439 return _page->editorState().isContentRichlyEditable;
1440 if (action == @selector(cut:))
1441 return !_page->editorState().isInPasswordField && _page->editorState().isContentEditable && _page->editorState().selectionIsRange;
1443 if (action == @selector(paste:)) {
1444 if (_page->editorState().selectionIsNone || !_page->editorState().isContentEditable)
1446 UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
1447 NSArray *types = [self supportedPasteboardTypesForCurrentSelection];
1448 NSIndexSet *indices = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, [pasteboard numberOfItems])];
1449 return [pasteboard containsPasteboardTypes:types inItemSet:indices];
1452 if (action == @selector(copy:)) {
1453 if (_page->editorState().isInPasswordField)
1455 return hasWebSelection || _page->editorState().selectionIsRange;
1458 if (action == @selector(_define:)) {
1459 if (_page->editorState().isInPasswordField || !(hasWebSelection || _page->editorState().selectionIsRange))
1462 NSUInteger textLength = _page->editorState().postLayoutData().selectedTextLength;
1463 // FIXME: We should be calling UIReferenceLibraryViewController to check if the length is
1464 // acceptable, but the interface takes a string.
1465 // <rdar://problem/15254406>
1466 if (!textLength || textLength > 200)
1469 if ([[getMCProfileConnectionClass() sharedConnection] effectiveBoolValueForSetting:MCFeatureDefinitionLookupAllowed] == MCRestrictedBoolExplicitNo)
1475 if (action == @selector(_lookup:)) {
1476 if (_page->editorState().isInPasswordField)
1479 if ([[getMCProfileConnectionClass() sharedConnection] effectiveBoolValueForSetting:MCFeatureDefinitionLookupAllowed] == MCRestrictedBoolExplicitNo)
1485 if (action == @selector(_share:)) {
1486 if (_page->editorState().isInPasswordField || !(hasWebSelection || _page->editorState().selectionIsRange))
1489 NSUInteger textLength = _page->editorState().postLayoutData().selectedTextLength;
1490 // See FIXME above for _define.
1491 if (!textLength || textLength > 200)
1497 if (action == @selector(_addShortcut:)) {
1498 if (_page->editorState().isInPasswordField || !(hasWebSelection || _page->editorState().selectionIsRange))
1501 NSString *selectedText = [self selectedText];
1502 if (![selectedText length])
1505 if (!UIKeyboardEnabledInputModesAllowOneToManyShortcuts())
1507 if (![selectedText _containsCJScripts])
1512 if (action == @selector(_promptForReplace:)) {
1513 if (!_page->editorState().selectionIsRange || !_page->editorState().postLayoutData().isReplaceAllowed || ![[UIKeyboardImpl activeInstance] autocorrectSpellingEnabled])
1515 if ([[self selectedText] _containsCJScriptsOnly])
1520 if (action == @selector(_transliterateChinese:)) {
1521 if (!_page->editorState().selectionIsRange || !_page->editorState().postLayoutData().isReplaceAllowed || ![[UIKeyboardImpl activeInstance] autocorrectSpellingEnabled])
1523 return UIKeyboardEnabledInputModesAllowChineseTransliterationForText([self selectedText]);
1526 if (action == @selector(_reanalyze:)) {
1527 if (!_page->editorState().selectionIsRange || !_page->editorState().postLayoutData().isReplaceAllowed || ![[UIKeyboardImpl activeInstance] autocorrectSpellingEnabled])
1529 return UIKeyboardCurrentInputModeAllowsChineseOrJapaneseReanalysisForText([self selectedText]);
1532 if (action == @selector(select:)) {
1533 // Disable select in password fields so that you can't see word boundaries.
1534 return !_page->editorState().isInPasswordField && [self hasContent] && !_page->editorState().selectionIsNone && !_page->editorState().selectionIsRange;
1537 if (action == @selector(selectAll:)) {
1538 if (_page->editorState().selectionIsNone || ![self hasContent])
1540 if (!_page->editorState().selectionIsRange)
1542 // Enable selectAll for non-editable text, where the user can't access
1543 // this command via long-press to get a caret.
1544 if (_page->editorState().isContentEditable)
1546 // Don't attempt selectAll with general web content.
1547 if (hasWebSelection)
1549 // FIXME: Only enable if the selection doesn't already span the entire document.
1553 if (action == @selector(replace:))
1554 return _page->editorState().isContentEditable && !_page->editorState().isInPasswordField;
1556 return [super canPerformAction:action withSender:sender];
1559 - (void)_resetShowingTextStyle:(NSNotification *)notification
1561 _showingTextStyleOptions = NO;
1562 [_textSelectionAssistant hideTextStyleOptions];
1565 - (void)copy:(id)sender
1567 _page->executeEditCommand(ASCIILiteral("copy"));
1570 - (void)cut:(id)sender
1572 _page->executeEditCommand(ASCIILiteral("cut"));
1575 - (void)paste:(id)sender
1577 _page->executeEditCommand(ASCIILiteral("paste"));
1580 - (void)select:(id)sender
1582 [_textSelectionAssistant selectWord];
1583 // We cannot use selectWord command, because we want to be able to select the word even when it is the last in the paragraph.
1584 _page->extendSelection(WordGranularity);
1587 - (void)selectAll:(id)sender
1589 [_textSelectionAssistant selectAll:sender];
1590 _page->executeEditCommand(ASCIILiteral("selectAll"));
1593 - (void)toggleBoldface:(id)sender
1595 if (!_page->editorState().isContentRichlyEditable)
1598 [self executeEditCommandWithCallback:@"toggleBold"];
1601 - (void)toggleItalics:(id)sender
1603 if (!_page->editorState().isContentRichlyEditable)
1606 [self executeEditCommandWithCallback:@"toggleItalic"];
1609 - (void)toggleUnderline:(id)sender
1611 if (!_page->editorState().isContentRichlyEditable)
1614 [self executeEditCommandWithCallback:@"toggleUnderline"];
1617 - (void)_showTextStyleOptions:(id)sender
1619 _showingTextStyleOptions = YES;
1620 [_textSelectionAssistant showTextStyleOptions];
1623 - (void)_showDictionary:(NSString *)text
1625 CGRect presentationRect = _page->editorState().postLayoutData().selectionRects[0].rect();
1626 if (_textSelectionAssistant)
1627 [_textSelectionAssistant showDictionaryFor:text fromRect:presentationRect];
1629 [_webSelectionAssistant showDictionaryFor:text fromRect:presentationRect];
1632 - (void)_define:(id)sender
1634 if ([[getMCProfileConnectionClass() sharedConnection] effectiveBoolValueForSetting:MCFeatureDefinitionLookupAllowed] == MCRestrictedBoolExplicitNo)
1637 RetainPtr<WKContentView> view = self;
1638 _page->getSelectionOrContentsAsString([view](const String& string, WebKit::CallbackBase::Error error) {
1639 if (error != WebKit::CallbackBase::Error::None)
1644 [view _showDictionary:string];
1648 - (void)accessibilityRetrieveSpeakSelectionContent
1650 RetainPtr<WKContentView> view = self;
1651 _page->getSelectionOrContentsAsString([view](const String& string, WebKit::CallbackBase::Error error) {
1652 if (error != WebKit::CallbackBase::Error::None)
1654 if ([view respondsToSelector:@selector(accessibilitySpeakSelectionSetContent:)])
1655 [view accessibilitySpeakSelectionSetContent:string];
1659 // UIWKInteractionViewProtocol
1661 static inline GestureType toGestureType(UIWKGestureType gestureType)
1663 switch (gestureType) {
1664 case UIWKGestureLoupe:
1665 return GestureType::Loupe;
1666 case UIWKGestureOneFingerTap:
1667 return GestureType::OneFingerTap;
1668 case UIWKGestureTapAndAHalf:
1669 return GestureType::TapAndAHalf;
1670 case UIWKGestureDoubleTap:
1671 return GestureType::DoubleTap;
1672 case UIWKGestureTapAndHalf:
1673 return GestureType::TapAndHalf;
1674 case UIWKGestureDoubleTapInUneditable:
1675 return GestureType::DoubleTapInUneditable;
1676 case UIWKGestureOneFingerTapInUneditable:
1677 return GestureType::OneFingerTapInUneditable;
1678 case UIWKGestureOneFingerTapSelectsAll:
1679 return GestureType::OneFingerTapSelectsAll;
1680 case UIWKGestureOneFingerDoubleTap:
1681 return GestureType::OneFingerDoubleTap;
1682 case UIWKGestureOneFingerTripleTap:
1683 return GestureType::OneFingerTripleTap;
1684 case UIWKGestureTwoFingerSingleTap:
1685 return GestureType::TwoFingerSingleTap;
1686 case UIWKGestureTwoFingerRangedSelectGesture:
1687 return GestureType::TwoFingerRangedSelectGesture;
1688 case UIWKGestureTapOnLinkWithGesture:
1689 return GestureType::TapOnLinkWithGesture;
1690 case UIWKGestureMakeWebSelection:
1691 return GestureType::MakeWebSelection;
1692 case UIWKGesturePhraseBoundary:
1693 return GestureType::PhraseBoundary;
1695 ASSERT_NOT_REACHED();
1696 return GestureType::Loupe;
1699 static inline UIWKGestureType toUIWKGestureType(GestureType gestureType)
1701 switch (gestureType) {
1702 case GestureType::Loupe:
1703 return UIWKGestureLoupe;
1704 case GestureType::OneFingerTap:
1705 return UIWKGestureOneFingerTap;
1706 case GestureType::TapAndAHalf:
1707 return UIWKGestureTapAndAHalf;
1708 case GestureType::DoubleTap:
1709 return UIWKGestureDoubleTap;
1710 case GestureType::TapAndHalf:
1711 return UIWKGestureTapAndHalf;
1712 case GestureType::DoubleTapInUneditable:
1713 return UIWKGestureDoubleTapInUneditable;
1714 case GestureType::OneFingerTapInUneditable:
1715 return UIWKGestureOneFingerTapInUneditable;
1716 case GestureType::OneFingerTapSelectsAll:
1717 return UIWKGestureOneFingerTapSelectsAll;
1718 case GestureType::OneFingerDoubleTap:
1719 return UIWKGestureOneFingerDoubleTap;
1720 case GestureType::OneFingerTripleTap:
1721 return UIWKGestureOneFingerTripleTap;
1722 case GestureType::TwoFingerSingleTap:
1723 return UIWKGestureTwoFingerSingleTap;
1724 case GestureType::TwoFingerRangedSelectGesture:
1725 return UIWKGestureTwoFingerRangedSelectGesture;
1726 case GestureType::TapOnLinkWithGesture:
1727 return UIWKGestureTapOnLinkWithGesture;
1728 case GestureType::MakeWebSelection:
1729 return UIWKGestureMakeWebSelection;
1730 case GestureType::PhraseBoundary:
1731 return UIWKGesturePhraseBoundary;
1735 static inline SelectionTouch toSelectionTouch(UIWKSelectionTouch touch)
1738 case UIWKSelectionTouchStarted:
1739 return SelectionTouch::Started;
1740 case UIWKSelectionTouchMoved:
1741 return SelectionTouch::Moved;
1742 case UIWKSelectionTouchEnded:
1743 return SelectionTouch::Ended;
1744 case UIWKSelectionTouchEndedMovingForward:
1745 return SelectionTouch::EndedMovingForward;
1746 case UIWKSelectionTouchEndedMovingBackward:
1747 return SelectionTouch::EndedMovingBackward;
1748 case UIWKSelectionTouchEndedNotMoving:
1749 return SelectionTouch::EndedNotMoving;
1751 ASSERT_NOT_REACHED();
1752 return SelectionTouch::Ended;
1755 static inline UIWKSelectionTouch toUIWKSelectionTouch(SelectionTouch touch)
1758 case SelectionTouch::Started:
1759 return UIWKSelectionTouchStarted;
1760 case SelectionTouch::Moved:
1761 return UIWKSelectionTouchMoved;
1762 case SelectionTouch::Ended:
1763 return UIWKSelectionTouchEnded;
1764 case SelectionTouch::EndedMovingForward:
1765 return UIWKSelectionTouchEndedMovingForward;
1766 case SelectionTouch::EndedMovingBackward:
1767 return UIWKSelectionTouchEndedMovingBackward;
1768 case SelectionTouch::EndedNotMoving:
1769 return UIWKSelectionTouchEndedNotMoving;
1773 static inline GestureRecognizerState toGestureRecognizerState(UIGestureRecognizerState state)
1776 case UIGestureRecognizerStatePossible:
1777 return GestureRecognizerState::Possible;
1778 case UIGestureRecognizerStateBegan:
1779 return GestureRecognizerState::Began;
1780 case UIGestureRecognizerStateChanged:
1781 return GestureRecognizerState::Changed;
1782 case UIGestureRecognizerStateCancelled:
1783 return GestureRecognizerState::Cancelled;
1784 case UIGestureRecognizerStateEnded:
1785 return GestureRecognizerState::Ended;
1786 case UIGestureRecognizerStateFailed:
1787 return GestureRecognizerState::Failed;
1791 static inline UIGestureRecognizerState toUIGestureRecognizerState(GestureRecognizerState state)
1794 case GestureRecognizerState::Possible:
1795 return UIGestureRecognizerStatePossible;
1796 case GestureRecognizerState::Began:
1797 return UIGestureRecognizerStateBegan;
1798 case GestureRecognizerState::Changed:
1799 return UIGestureRecognizerStateChanged;
1800 case GestureRecognizerState::Cancelled:
1801 return UIGestureRecognizerStateCancelled;
1802 case GestureRecognizerState::Ended:
1803 return UIGestureRecognizerStateEnded;
1804 case GestureRecognizerState::Failed:
1805 return UIGestureRecognizerStateFailed;
1809 static inline UIWKSelectionFlags toUIWKSelectionFlags(SelectionFlags flags)
1811 NSInteger uiFlags = UIWKNone;
1812 if (flags & WordIsNearTap)
1813 uiFlags |= UIWKWordIsNearTap;
1814 if (flags & IsBlockSelection)
1815 uiFlags |= UIWKIsBlockSelection;
1816 if (flags & PhraseBoundaryChanged)
1817 uiFlags |= UIWKPhraseBoundaryChanged;
1819 return static_cast<UIWKSelectionFlags>(uiFlags);
1822 static inline SelectionHandlePosition toSelectionHandlePosition(UIWKHandlePosition position)
1826 return SelectionHandlePosition::Top;
1827 case UIWKHandleRight:
1828 return SelectionHandlePosition::Right;
1829 case UIWKHandleBottom:
1830 return SelectionHandlePosition::Bottom;
1831 case UIWKHandleLeft:
1832 return SelectionHandlePosition::Left;
1836 static inline WebCore::TextGranularity toWKTextGranularity(UITextGranularity granularity)
1838 switch (granularity) {
1839 case UITextGranularityCharacter:
1840 return CharacterGranularity;
1841 case UITextGranularityWord:
1842 return WordGranularity;
1843 case UITextGranularitySentence:
1844 return SentenceGranularity;
1845 case UITextGranularityParagraph:
1846 return ParagraphGranularity;
1847 case UITextGranularityLine:
1848 return LineGranularity;
1849 case UITextGranularityDocument:
1850 return DocumentGranularity;
1854 static inline WebCore::SelectionDirection toWKSelectionDirection(UITextDirection direction)
1856 switch (direction) {
1857 case UITextLayoutDirectionDown:
1858 case UITextLayoutDirectionRight:
1859 return DirectionRight;
1860 case UITextLayoutDirectionUp:
1861 case UITextLayoutDirectionLeft:
1862 return DirectionLeft;
1864 // UITextDirection is not an enum, but we only want to accept values from UITextLayoutDirection.
1865 ASSERT_NOT_REACHED();
1866 return DirectionRight;
1870 static void selectionChangedWithGesture(WKContentView *view, const WebCore::IntPoint& point, uint32_t gestureType, uint32_t gestureState, uint32_t flags, WebKit::CallbackBase::Error error)
1872 if (error != WebKit::CallbackBase::Error::None) {
1873 ASSERT_NOT_REACHED();
1876 if ([view webSelectionAssistant])
1877 [(UIWKSelectionAssistant *)[view webSelectionAssistant] selectionChangedWithGestureAt:(CGPoint)point withGesture:toUIWKGestureType((GestureType)gestureType) withState:toUIGestureRecognizerState(static_cast<GestureRecognizerState>(gestureState)) withFlags:(toUIWKSelectionFlags((SelectionFlags)flags))];
1879 [(UIWKTextInteractionAssistant *)[view interactionAssistant] selectionChangedWithGestureAt:(CGPoint)point withGesture:toUIWKGestureType((GestureType)gestureType) withState:toUIGestureRecognizerState(static_cast<GestureRecognizerState>(gestureState)) withFlags:(toUIWKSelectionFlags((SelectionFlags)flags))];
1882 static void selectionChangedWithTouch(WKContentView *view, const WebCore::IntPoint& point, uint32_t touch, WebKit::CallbackBase::Error error)
1884 if (error != WebKit::CallbackBase::Error::None) {
1885 ASSERT_NOT_REACHED();
1888 if ([view webSelectionAssistant])
1889 [(UIWKSelectionAssistant *)[view webSelectionAssistant] selectionChangedWithTouchAt:(CGPoint)point withSelectionTouch:toUIWKSelectionTouch((SelectionTouch)touch)];
1891 [(UIWKTextInteractionAssistant *)[view interactionAssistant] selectionChangedWithTouchAt:(CGPoint)point withSelectionTouch:toUIWKSelectionTouch((SelectionTouch)touch)];
1894 - (void)_didUpdateBlockSelectionWithTouch:(SelectionTouch)touch withFlags:(SelectionFlags)flags growThreshold:(CGFloat)growThreshold shrinkThreshold:(CGFloat)shrinkThreshold
1896 [_webSelectionAssistant blockSelectionChangedWithTouch:toUIWKSelectionTouch(touch) withFlags:toUIWKSelectionFlags(flags) growThreshold:growThreshold shrinkThreshold:shrinkThreshold];
1897 if (touch != SelectionTouch::Started && touch != SelectionTouch::Moved)
1898 _usingGestureForSelection = NO;
1901 - (void)changeSelectionWithGestureAt:(CGPoint)point withGesture:(UIWKGestureType)gestureType withState:(UIGestureRecognizerState)state
1903 _usingGestureForSelection = YES;
1904 _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, WebKit::CallbackBase::Error error) {
1905 selectionChangedWithGesture(self, point, gestureType, gestureState, flags, error);
1906 if (state == UIGestureRecognizerStateEnded || state == UIGestureRecognizerStateCancelled)
1907 _usingGestureForSelection = NO;
1911 - (void)changeSelectionWithTouchAt:(CGPoint)point withSelectionTouch:(UIWKSelectionTouch)touch baseIsStart:(BOOL)baseIsStart
1913 _usingGestureForSelection = YES;
1914 _page->updateSelectionWithTouches(WebCore::IntPoint(point), static_cast<uint32_t>(toSelectionTouch(touch)), baseIsStart, [self, touch](const WebCore::IntPoint& point, uint32_t touch, WebKit::CallbackBase::Error error) {
1915 selectionChangedWithTouch(self, point, touch, error);
1916 if (touch != UIWKSelectionTouchStarted && touch != UIWKSelectionTouchMoved)
1917 _usingGestureForSelection = NO;
1921 - (void)changeSelectionWithTouchesFrom:(CGPoint)from to:(CGPoint)to withGesture:(UIWKGestureType)gestureType withState:(UIGestureRecognizerState)gestureState
1923 _usingGestureForSelection = YES;
1924 _page->selectWithTwoTouches(WebCore::IntPoint(from), WebCore::IntPoint(to), static_cast<uint32_t>(toGestureType(gestureType)), static_cast<uint32_t>(toGestureRecognizerState(gestureState)), [self, gestureState](const WebCore::IntPoint& point, uint32_t gestureType, uint32_t gestureState, uint32_t flags, WebKit::CallbackBase::Error error) {
1925 selectionChangedWithGesture(self, point, gestureType, gestureState, flags, error);
1926 if (gestureState == UIGestureRecognizerStateEnded || gestureState == UIGestureRecognizerStateCancelled)
1927 _usingGestureForSelection = NO;
1931 - (void)changeBlockSelectionWithTouchAt:(CGPoint)point withSelectionTouch:(UIWKSelectionTouch)touch forHandle:(UIWKHandlePosition)handle
1933 _usingGestureForSelection = YES;
1934 _page->updateBlockSelectionWithTouch(WebCore::IntPoint(point), static_cast<uint32_t>(toSelectionTouch(touch)), static_cast<uint32_t>(toSelectionHandlePosition(handle)));
1937 - (void)moveByOffset:(NSInteger)offset
1942 [self beginSelectionChange];
1943 RetainPtr<WKContentView> view = self;
1944 _page->moveSelectionByOffset(offset, [view](WebKit::CallbackBase::Error) {
1945 [view endSelectionChange];
1949 - (const WKAutoCorrectionData&)autocorrectionData
1951 return _autocorrectionData;
1954 // The completion handler can pass nil if input does not match the actual text preceding the insertion point.
1955 - (void)requestAutocorrectionRectsForString:(NSString *)input withCompletionHandler:(void (^)(UIWKAutocorrectionRects *rectsForInput))completionHandler
1957 if (!input || ![input length]) {
1958 completionHandler(nil);
1962 RetainPtr<WKContentView> view = self;
1963 _autocorrectionData.autocorrectionHandler = [completionHandler copy];
1964 _page->requestAutocorrectionData(input, [view](const Vector<FloatRect>& rects, const String& fontName, double fontSize, uint64_t traits, WebKit::CallbackBase::Error) {
1965 CGRect firstRect = CGRectZero;
1966 CGRect lastRect = CGRectZero;
1968 firstRect = rects[0];
1969 lastRect = rects[rects.size() - 1];
1972 view->_autocorrectionData.fontName = fontName;
1973 view->_autocorrectionData.fontSize = fontSize;
1974 view->_autocorrectionData.fontTraits = traits;
1975 view->_autocorrectionData.textFirstRect = firstRect;
1976 view->_autocorrectionData.textLastRect = lastRect;
1978 view->_autocorrectionData.autocorrectionHandler(rects.size() ? [WKAutocorrectionRects autocorrectionRectsWithRects:firstRect lastRect:lastRect] : nil);
1979 [view->_autocorrectionData.autocorrectionHandler release];
1980 view->_autocorrectionData.autocorrectionHandler = nil;
1984 - (void)selectPositionAtPoint:(CGPoint)point completionHandler:(void (^)(void))completionHandler
1986 _usingGestureForSelection = YES;
1987 UIWKSelectionCompletionHandler selectionHandler = [completionHandler copy];
1988 RetainPtr<WKContentView> view = self;
1990 _page->selectPositionAtPoint(WebCore::IntPoint(point), [view, selectionHandler](WebKit::CallbackBase::Error error) {
1992 view->_usingGestureForSelection = NO;
1993 [selectionHandler release];
1997 - (void)selectPositionAtBoundary:(UITextGranularity)granularity inDirection:(UITextDirection)direction fromPoint:(CGPoint)point completionHandler:(void (^)(void))completionHandler
1999 _usingGestureForSelection = YES;
2000 UIWKSelectionCompletionHandler selectionHandler = [completionHandler copy];
2001 RetainPtr<WKContentView> view = self;
2003 _page->selectPositionAtBoundaryWithDirection(WebCore::IntPoint(point), toWKTextGranularity(granularity), toWKSelectionDirection(direction), [view, selectionHandler](WebKit::CallbackBase::Error error) {
2005 view->_usingGestureForSelection = NO;
2006 [selectionHandler release];
2010 - (void)moveSelectionAtBoundary:(UITextGranularity)granularity inDirection:(UITextDirection)direction completionHandler:(void (^)(void))completionHandler
2012 _usingGestureForSelection = YES;
2013 UIWKSelectionCompletionHandler selectionHandler = [completionHandler copy];
2014 RetainPtr<WKContentView> view = self;
2016 _page->moveSelectionAtBoundaryWithDirection(toWKTextGranularity(granularity), toWKSelectionDirection(direction), [view, selectionHandler](WebKit::CallbackBase::Error error) {
2018 view->_usingGestureForSelection = NO;
2019 [selectionHandler release];
2023 - (void)selectTextWithGranularity:(UITextGranularity)granularity atPoint:(CGPoint)point completionHandler:(void (^)(void))completionHandler
2025 _usingGestureForSelection = YES;
2026 UIWKSelectionCompletionHandler selectionHandler = [completionHandler copy];
2027 RetainPtr<WKContentView> view = self;
2029 _page->selectTextWithGranularityAtPoint(WebCore::IntPoint(point), toWKTextGranularity(granularity), [view, selectionHandler](WebKit::CallbackBase::Error error) {
2031 view->_usingGestureForSelection = NO;
2032 [selectionHandler release];
2036 - (void)beginSelectionInDirection:(UITextDirection)direction completionHandler:(void (^)(BOOL endIsMoving))completionHandler
2038 UIWKSelectionWithDirectionCompletionHandler selectionHandler = [completionHandler copy];
2040 _page->beginSelectionInDirection(toWKSelectionDirection(direction), [selectionHandler](bool endIsMoving, WebKit::CallbackBase::Error error) {
2041 selectionHandler(endIsMoving);
2042 [selectionHandler release];
2046 - (void)updateSelectionWithExtentPoint:(CGPoint)point completionHandler:(void (^)(BOOL endIsMoving))completionHandler
2048 UIWKSelectionWithDirectionCompletionHandler selectionHandler = [completionHandler copy];
2050 _page->updateSelectionWithExtentPoint(WebCore::IntPoint(point), [selectionHandler](bool endIsMoving, WebKit::CallbackBase::Error error) {
2051 selectionHandler(endIsMoving);
2052 [selectionHandler release];
2056 - (void)updateSelectionWithExtentPoint:(CGPoint)point withBoundary:(UITextGranularity)granularity completionHandler:(void (^)(BOOL selectionEndIsMoving))completionHandler
2058 UIWKSelectionWithDirectionCompletionHandler selectionHandler = [completionHandler copy];
2060 _page->updateSelectionWithExtentPointAndBoundary(WebCore::IntPoint(point), toWKTextGranularity(granularity), [selectionHandler](bool endIsMoving, WebKit::CallbackBase::Error error) {
2061 selectionHandler(endIsMoving);
2062 [selectionHandler release];
2066 - (UTF32Char)_characterBeforeCaretSelection
2068 return _page->editorState().postLayoutData().characterBeforeSelection;
2071 - (UTF32Char)_characterInRelationToCaretSelection:(int)amount
2075 return _page->editorState().postLayoutData().characterAfterSelection;
2077 return _page->editorState().postLayoutData().characterBeforeSelection;
2079 return _page->editorState().postLayoutData().twoCharacterBeforeSelection;
2085 - (BOOL)_selectionAtDocumentStart
2087 return !_page->editorState().postLayoutData().characterBeforeSelection;
2090 - (CGRect)textFirstRect
2092 return (_page->editorState().hasComposition) ? _page->editorState().firstMarkedRect : _autocorrectionData.textFirstRect;
2095 - (CGRect)textLastRect
2097 return (_page->editorState().hasComposition) ? _page->editorState().lastMarkedRect : _autocorrectionData.textLastRect;
2100 - (void)replaceDictatedText:(NSString*)oldText withText:(NSString *)newText
2102 _page->replaceDictatedText(oldText, newText);
2105 - (void)requestDictationContext:(void (^)(NSString *selectedText, NSString *beforeText, NSString *afterText))completionHandler
2107 UIWKDictationContextHandler dictationHandler = [completionHandler copy];
2109 _page->requestDictationContext([dictationHandler](const String& selectedText, const String& beforeText, const String& afterText, WebKit::CallbackBase::Error) {
2110 dictationHandler(selectedText, beforeText, afterText);
2111 [dictationHandler release];
2115 // 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.
2116 - (void)applyAutocorrection:(NSString *)correction toString:(NSString *)input withCompletionHandler:(void (^)(UIWKAutocorrectionRects *rectsForCorrection))completionHandler
2118 // FIXME: Remove the synchronous call when <rdar://problem/16207002> is fixed.
2119 const bool useSyncRequest = true;
2121 if (useSyncRequest) {
2122 completionHandler(_page->applyAutocorrection(correction, input) ? [WKAutocorrectionRects autocorrectionRectsWithRects:_autocorrectionData.textFirstRect lastRect:_autocorrectionData.textLastRect] : nil);
2125 _autocorrectionData.autocorrectionHandler = [completionHandler copy];
2126 RetainPtr<WKContentView> view = self;
2127 _page->applyAutocorrection(correction, input, [view](const String& string, WebKit::CallbackBase::Error error) {
2128 view->_autocorrectionData.autocorrectionHandler(!string.isNull() ? [WKAutocorrectionRects autocorrectionRectsWithRects:view->_autocorrectionData.textFirstRect lastRect:view->_autocorrectionData.textLastRect] : nil);
2129 [view->_autocorrectionData.autocorrectionHandler release];
2130 view->_autocorrectionData.autocorrectionHandler = nil;
2134 - (void)requestAutocorrectionContextWithCompletionHandler:(void (^)(UIWKAutocorrectionContext *autocorrectionContext))completionHandler
2136 // FIXME: Remove the synchronous call when <rdar://problem/16207002> is fixed.
2137 const bool useSyncRequest = true;
2139 if (useSyncRequest) {
2142 String selectedText;
2146 _page->getAutocorrectionContext(beforeText, markedText, selectedText, afterText, location, length);
2147 completionHandler([WKAutocorrectionContext autocorrectionContextWithData:beforeText markedText:markedText selectedText:selectedText afterText:afterText selectedRangeInMarkedText:NSMakeRange(location, length)]);
2149 _autocorrectionData.autocorrectionContextHandler = [completionHandler copy];
2150 RetainPtr<WKContentView> view = self;
2151 _page->requestAutocorrectionContext([view](const String& beforeText, const String& markedText, const String& selectedText, const String& afterText, uint64_t location, uint64_t length, WebKit::CallbackBase::Error) {
2152 view->_autocorrectionData.autocorrectionContextHandler([WKAutocorrectionContext autocorrectionContextWithData:beforeText markedText:markedText selectedText:selectedText afterText:afterText selectedRangeInMarkedText:NSMakeRange(location, length)]);
2157 // UIWebFormAccessoryDelegate
2158 - (void)accessoryDone
2160 [self resignFirstResponder];
2163 - (NSArray *)keyCommands
2165 static NSArray* nonEditableKeyCommands = [@[
2166 [UIKeyCommand keyCommandWithInput:UIKeyInputUpArrow modifierFlags:0 action:@selector(_arrowKey:)],
2167 [UIKeyCommand keyCommandWithInput:UIKeyInputDownArrow modifierFlags:0 action:@selector(_arrowKey:)],
2168 [UIKeyCommand keyCommandWithInput:UIKeyInputLeftArrow modifierFlags:0 action:@selector(_arrowKey:)],
2169 [UIKeyCommand keyCommandWithInput:UIKeyInputRightArrow modifierFlags:0 action:@selector(_arrowKey:)],
2171 [UIKeyCommand keyCommandWithInput:UIKeyInputUpArrow modifierFlags:UIKeyModifierCommand action:@selector(_arrowKey:)],
2172 [UIKeyCommand keyCommandWithInput:UIKeyInputDownArrow modifierFlags:UIKeyModifierCommand action:@selector(_arrowKey:)],
2174 [UIKeyCommand keyCommandWithInput:UIKeyInputUpArrow modifierFlags:UIKeyModifierShift action:@selector(_arrowKey:)],
2175 [UIKeyCommand keyCommandWithInput:UIKeyInputDownArrow modifierFlags:UIKeyModifierShift action:@selector(_arrowKey:)],
2176 [UIKeyCommand keyCommandWithInput:UIKeyInputLeftArrow modifierFlags:UIKeyModifierShift action:@selector(_arrowKey:)],
2177 [UIKeyCommand keyCommandWithInput:UIKeyInputRightArrow modifierFlags:UIKeyModifierShift action:@selector(_arrowKey:)],
2179 [UIKeyCommand keyCommandWithInput:UIKeyInputUpArrow modifierFlags:UIKeyModifierAlternate action:@selector(_arrowKey:)],
2180 [UIKeyCommand keyCommandWithInput:UIKeyInputDownArrow modifierFlags:UIKeyModifierAlternate action:@selector(_arrowKey:)],
2181 [UIKeyCommand keyCommandWithInput:UIKeyInputLeftArrow modifierFlags:UIKeyModifierAlternate action:@selector(_arrowKey:)],
2182 [UIKeyCommand keyCommandWithInput:UIKeyInputRightArrow modifierFlags:UIKeyModifierAlternate action:@selector(_arrowKey:)],
2184 [UIKeyCommand keyCommandWithInput:@" " modifierFlags:0 action:@selector(_arrowKey:)],
2185 [UIKeyCommand keyCommandWithInput:@" " modifierFlags:UIKeyModifierShift action:@selector(_arrowKey:)],
2187 [UIKeyCommand keyCommandWithInput:UIKeyInputPageDown modifierFlags:0 action:@selector(_arrowKey:)],
2188 [UIKeyCommand keyCommandWithInput:UIKeyInputPageDown modifierFlags:0 action:@selector(_arrowKey:)],
2191 static NSArray* editableKeyCommands = [@[
2192 [UIKeyCommand keyCommandWithInput:@"\t" modifierFlags:0 action:@selector(_nextAccessoryTab:)],
2193 [UIKeyCommand keyCommandWithInput:@"\t" modifierFlags:UIKeyModifierShift action:@selector(_prevAccessoryTab:)]
2196 return (_page->editorState().isContentEditable) ? editableKeyCommands : nonEditableKeyCommands;
2199 - (void)_arrowKey:(id)sender
2201 UIKeyCommand* command = sender;
2202 [self handleKeyEvent:command._triggeringEvent];
2205 - (void)_nextAccessoryTab:(id)sender
2207 [self accessoryTab:YES];
2210 - (void)_prevAccessoryTab:(id)sender
2212 [self accessoryTab:NO];
2215 - (void)accessoryTab:(BOOL)isNext
2217 [_inputPeripheral endEditing];
2218 _inputPeripheral = nil;
2220 _didAccessoryTabInitiateFocus = YES; // Will be cleared in either -_displayFormNodeInputView or -cleanupInteraction.
2221 [self beginSelectionChange];
2222 RetainPtr<WKContentView> view = self;
2223 _page->focusNextAssistedNode(isNext, [view](WebKit::CallbackBase::Error) {
2224 [view endSelectionChange];
2225 [view reloadInputViews];
2230 - (void)_becomeFirstResponderWithSelectionMovingForward:(BOOL)selectingForward completionHandler:(void (^)(BOOL didBecomeFirstResponder))completionHandler
2232 auto completionHandlerCopy = Block_copy(completionHandler);
2233 RetainPtr<WKContentView> view = self;
2234 _page->setInitialFocus(selectingForward, false, WebKit::WebKeyboardEvent(), [view, completionHandlerCopy](WebKit::CallbackBase::Error) {
2235 BOOL didBecomeFirstResponder = view->_assistedNodeInformation.elementType != InputType::None && [view becomeFirstResponder];
2236 completionHandlerCopy(didBecomeFirstResponder);
2237 Block_release(completionHandlerCopy);
2241 - (void)accessoryAutoFill
2243 id <_WKFormDelegate> formDelegate = [_webView _formDelegate];
2244 if ([formDelegate respondsToSelector:@selector(_webView:accessoryViewCustomButtonTappedInFormInputSession:)])
2245 [formDelegate _webView:_webView accessoryViewCustomButtonTappedInFormInputSession:_formInputSession.get()];
2248 - (void)accessoryClear
2250 _page->setAssistedNodeValue(String());
2253 - (void)_updateAccessory
2255 [_formAccessoryView setNextEnabled:_assistedNodeInformation.hasNextNode];
2256 [_formAccessoryView setPreviousEnabled:_assistedNodeInformation.hasPreviousNode];
2258 if (UICurrentUserInterfaceIdiomIsPad())
2259 [_formAccessoryView setClearVisible:NO];
2261 switch (_assistedNodeInformation.elementType) {
2262 case InputType::Date:
2263 case InputType::Month:
2264 case InputType::DateTimeLocal:
2265 case InputType::Time:
2266 [_formAccessoryView setClearVisible:YES];
2269 [_formAccessoryView setClearVisible:NO];
2274 // FIXME: hide or show the AutoFill button as needed.
2277 // Keyboard interaction
2278 // UITextInput protocol implementation
2280 - (void)beginSelectionChange
2282 [self.inputDelegate selectionWillChange:self];
2285 - (void)endSelectionChange
2287 [self.inputDelegate selectionDidChange:self];
2290 - (NSString *)textInRange:(UITextRange *)range
2295 - (void)replaceRange:(UITextRange *)range withText:(NSString *)text
2299 - (UITextRange *)selectedTextRange
2301 auto& postLayoutEditorStateData = _page->editorState().postLayoutData();
2302 FloatRect startRect = postLayoutEditorStateData.caretRectAtStart;
2303 FloatRect endRect = postLayoutEditorStateData.caretRectAtEnd;
2304 double inverseScale = [self inverseScale];
2305 // We want to keep the original caret width, while the height scales with
2306 // the content taking orientation into account.
2307 // We achieve this by scaling the width with the inverse
2308 // scale factor. This way, when it is converted from the content view
2309 // the width remains unchanged.
2310 if (startRect.width() < startRect.height())
2311 startRect.setWidth(startRect.width() * inverseScale);
2313 startRect.setHeight(startRect.height() * inverseScale);
2314 if (endRect.width() < endRect.height()) {
2315 double delta = endRect.width();
2316 endRect.setWidth(endRect.width() * inverseScale);
2317 delta = endRect.width() - delta;
2318 endRect.move(delta, 0);
2320 double delta = endRect.height();
2321 endRect.setHeight(endRect.height() * inverseScale);
2322 delta = endRect.height() - delta;
2323 endRect.move(0, delta);
2325 return [WKTextRange textRangeWithState:_page->editorState().selectionIsNone
2326 isRange:_page->editorState().selectionIsRange
2327 isEditable:_page->editorState().isContentEditable
2330 selectionRects:[self webSelectionRects]
2331 selectedTextLength:postLayoutEditorStateData.selectedTextLength];
2334 - (CGRect)caretRectForPosition:(UITextPosition *)position
2336 return ((WKTextPosition *)position).positionRect;
2339 - (NSArray *)selectionRectsForRange:(UITextRange *)range
2341 return [WKTextSelectionRect textSelectionRectsWithWebRects:((WKTextRange *)range).selectionRects];
2344 - (void)setSelectedTextRange:(UITextRange *)range
2348 - (BOOL)hasMarkedText
2350 return [_markedText length];
2353 - (NSString *)markedText
2355 return _markedText.get();
2358 - (UITextRange *)markedTextRange
2363 - (NSDictionary *)markedTextStyle
2368 - (void)setMarkedTextStyle:(NSDictionary *)styleDictionary
2372 - (void)setMarkedText:(NSString *)markedText selectedRange:(NSRange)selectedRange
2374 _markedText = markedText;
2375 _page->setCompositionAsync(markedText, Vector<WebCore::CompositionUnderline>(), selectedRange, EditingRange());
2381 _page->confirmCompositionAsync();
2384 - (UITextPosition *)beginningOfDocument
2389 - (UITextPosition *)endOfDocument
2394 - (UITextRange *)textRangeFromPosition:(UITextPosition *)fromPosition toPosition:(UITextPosition *)toPosition
2399 - (UITextPosition *)positionFromPosition:(UITextPosition *)position offset:(NSInteger)offset
2404 - (UITextPosition *)positionFromPosition:(UITextPosition *)position inDirection:(UITextLayoutDirection)direction offset:(NSInteger)offset
2409 - (NSComparisonResult)comparePosition:(UITextPosition *)position toPosition:(UITextPosition *)other
2411 return NSOrderedSame;
2414 - (NSInteger)offsetFromPosition:(UITextPosition *)from toPosition:(UITextPosition *)toPosition
2419 - (id <UITextInputTokenizer>)tokenizer
2424 - (UITextPosition *)positionWithinRange:(UITextRange *)range farthestInDirection:(UITextLayoutDirection)direction
2429 - (UITextRange *)characterRangeByExtendingPosition:(UITextPosition *)position inDirection:(UITextLayoutDirection)direction
2434 - (UITextWritingDirection)baseWritingDirectionForPosition:(UITextPosition *)position inDirection:(UITextStorageDirection)direction
2436 return UITextWritingDirectionLeftToRight;
2439 - (void)setBaseWritingDirection:(UITextWritingDirection)writingDirection forRange:(UITextRange *)range
2443 - (CGRect)firstRectForRange:(UITextRange *)range
2449 - (UITextPosition *)closestPositionToPoint:(CGPoint)point
2454 - (UITextPosition *)closestPositionToPoint:(CGPoint)point withinRange:(UITextRange *)range
2459 - (UITextRange *)characterRangeAtPoint:(CGPoint)point
2464 - (void)deleteBackward
2466 _page->executeEditCommand(ASCIILiteral("deleteBackward"));
2469 // Inserts the given string, replacing any selected or marked text.
2470 - (void)insertText:(NSString *)aStringValue
2472 _page->insertTextAsync(aStringValue, EditingRange());
2480 // end of UITextInput protocol implementation
2482 static UITextAutocapitalizationType toUITextAutocapitalize(WebAutocapitalizeType webkitType)
2484 switch (webkitType) {
2485 case WebAutocapitalizeTypeDefault:
2486 return UITextAutocapitalizationTypeSentences;
2487 case WebAutocapitalizeTypeNone:
2488 return UITextAutocapitalizationTypeNone;
2489 case WebAutocapitalizeTypeWords:
2490 return UITextAutocapitalizationTypeWords;
2491 case WebAutocapitalizeTypeSentences:
2492 return UITextAutocapitalizationTypeSentences;
2493 case WebAutocapitalizeTypeAllCharacters:
2494 return UITextAutocapitalizationTypeAllCharacters;
2497 return UITextAutocapitalizationTypeSentences;
2500 // UITextInputPrivate protocol
2501 // Direct access to the (private) UITextInputTraits object.
2502 - (UITextInputTraits *)textInputTraits
2505 _traits = adoptNS([[UITextInputTraits alloc] init]);
2507 [_traits setSecureTextEntry:_assistedNodeInformation.elementType == InputType::Password];
2508 [_traits setShortcutConversionType:_assistedNodeInformation.elementType == InputType::Password ? UITextShortcutConversionTypeNo : UITextShortcutConversionTypeDefault];
2510 if (!_assistedNodeInformation.formAction.isEmpty())
2511 [_traits setReturnKeyType:(_assistedNodeInformation.elementType == InputType::Search) ? UIReturnKeySearch : UIReturnKeyGo];
2513 if (_assistedNodeInformation.elementType == InputType::Password || _assistedNodeInformation.elementType == InputType::Email || _assistedNodeInformation.elementType == InputType::URL || _assistedNodeInformation.formAction.contains("login")) {
2514 [_traits setAutocapitalizationType:UITextAutocapitalizationTypeNone];
2515 [_traits setAutocorrectionType:UITextAutocorrectionTypeNo];
2517 [_traits setAutocapitalizationType:toUITextAutocapitalize(_assistedNodeInformation.autocapitalizeType)];
2518 [_traits setAutocorrectionType:_assistedNodeInformation.isAutocorrect ? UITextAutocorrectionTypeYes : UITextAutocorrectionTypeNo];
2521 switch (_assistedNodeInformation.elementType) {
2522 case InputType::Phone:
2523 [_traits setKeyboardType:UIKeyboardTypePhonePad];
2525 case InputType::URL:
2526 [_traits setKeyboardType:UIKeyboardTypeURL];
2528 case InputType::Email:
2529 [_traits setKeyboardType:UIKeyboardTypeEmailAddress];
2531 case InputType::Number:
2532 [_traits setKeyboardType:UIKeyboardTypeNumbersAndPunctuation];
2534 case InputType::NumberPad:
2535 [_traits setKeyboardType:UIKeyboardTypeNumberPad];
2538 [_traits setKeyboardType:UIKeyboardTypeDefault];
2541 return _traits.get();
2544 - (UITextInteractionAssistant *)interactionAssistant
2546 return _textSelectionAssistant.get();
2549 - (UIWebSelectionAssistant *)webSelectionAssistant
2551 return _webSelectionAssistant.get();
2554 #if __IPHONE_OS_VERSION_MIN_REQUIRED >= 90000
2555 - (id<UISelectionInteractionAssistant>)selectionInteractionAssistant
2557 if ([_webSelectionAssistant conformsToProtocol:@protocol(UISelectionInteractionAssistant)])
2558 return (id<UISelectionInteractionAssistant>)_webSelectionAssistant.get();
2563 // NSRange support. Would like to deprecate to the extent possible, although some support
2564 // (i.e. selectionRange) has shipped as API.
2565 - (NSRange)selectionRange
2567 return NSMakeRange(NSNotFound, 0);
2570 - (CGRect)rectForNSRange:(NSRange)range
2575 - (NSRange)_markedTextNSRange
2577 return NSMakeRange(NSNotFound, 0);
2580 // DOM range support.
2581 - (DOMRange *)selectedDOMRange
2586 - (void)setSelectedDOMRange:(DOMRange *)range affinityDownstream:(BOOL)affinityDownstream
2590 // Modify text without starting a new undo grouping.
2591 - (void)replaceRangeWithTextWithoutClosingTyping:(UITextRange *)range replacementText:(NSString *)text
2595 // Caret rect support. Shouldn't be necessary, but firstRectForRange doesn't offer precisely
2596 // the same functionality.
2597 - (CGRect)rectContainingCaretSelection
2603 - (BOOL)requiresKeyEvents
2608 - (void)_handleKeyUIEvent:(::UIEvent *)event
2610 // We only want to handle key event from the hardware keyboard when we are
2611 // first responder and we are not interacting with editable content.
2612 if ([self isFirstResponder] && event._hidEvent && !_page->editorState().isContentEditable)
2613 [self handleKeyEvent:event];
2615 [super _handleKeyUIEvent:event];
2618 - (void)handleKeyEvent:(::UIEvent *)event
2620 ::WebEvent *webEvent = [[[::WebEvent alloc] initWithKeyEventType:(event._isKeyDown) ? WebEventKeyDown : WebEventKeyUp
2621 timeStamp:event.timestamp
2622 characters:event._modifiedInput
2623 charactersIgnoringModifiers:event._unmodifiedInput
2624 modifiers:event._modifierFlags
2625 isRepeating:(event._inputFlags & kUIKeyboardInputRepeat)
2626 withFlags:event._inputFlags
2628 isTabKey:[event._modifiedInput isEqualToString:@"\t"]
2629 characterSet:WebEventCharacterSetUnicode] autorelease];
2631 [self handleKeyWebEvent:webEvent];
2634 - (void)handleKeyWebEvent:(WebIOSEvent *)theEvent
2636 _page->handleKeyboardEvent(NativeWebKeyboardEvent(theEvent));
2639 - (void)_didHandleKeyEvent:(WebIOSEvent *)event
2641 if (event.type == WebEventKeyDown) {
2642 // FIXME: This is only for staging purposes.
2643 if ([[UIKeyboardImpl sharedInstance] respondsToSelector:@selector(didHandleWebKeyEvent:)])
2644 [[UIKeyboardImpl sharedInstance] didHandleWebKeyEvent:event];
2646 [[UIKeyboardImpl sharedInstance] didHandleWebKeyEvent];
2650 - (BOOL)_interpretKeyEvent:(WebIOSEvent *)event isCharEvent:(BOOL)isCharEvent
2652 static const unsigned kWebEnterKey = 0x0003;
2653 static const unsigned kWebBackspaceKey = 0x0008;
2654 static const unsigned kWebReturnKey = 0x000D;
2655 static const unsigned kWebDeleteKey = 0x007F;
2656 static const unsigned kWebDeleteForwardKey = 0xF728;
2657 static const unsigned kWebSpaceKey = 0x20;
2659 BOOL contentEditable = _page->editorState().isContentEditable;
2661 if (!contentEditable && event.isTabKey)
2664 BOOL shift = event.modifierFlags & WebEventFlagMaskShift;
2665 BOOL command = event.modifierFlags & WebEventFlagMaskCommand;
2666 BOOL option = event.modifierFlags & WebEventFlagMaskAlternate;
2667 NSString *charactersIgnoringModifiers = [event charactersIgnoringModifiers];
2668 BOOL shouldScroll = YES;
2669 FloatPoint scrollOffset;
2671 if ([charactersIgnoringModifiers isEqualToString:UIKeyInputLeftArrow])
2672 scrollOffset.setX(-Scrollbar::pixelsPerLineStep());
2673 else if ([charactersIgnoringModifiers isEqualToString:UIKeyInputUpArrow]) {
2675 scrollOffset.setY(-_page->unobscuredContentRect().height());
2677 scrollOffset.setY(-[self bounds].size.height);
2679 scrollOffset.setY(-Scrollbar::pixelsPerLineStep());
2680 } else if ([charactersIgnoringModifiers isEqualToString:UIKeyInputRightArrow])
2681 scrollOffset.setX(Scrollbar::pixelsPerLineStep());
2682 else if ([charactersIgnoringModifiers isEqualToString:UIKeyInputDownArrow]) {
2684 scrollOffset.setY(_page->unobscuredContentRect().height());
2686 scrollOffset.setY([self bounds].size.height);
2688 scrollOffset.setY(Scrollbar::pixelsPerLineStep());
2689 } else if ([charactersIgnoringModifiers isEqualToString:UIKeyInputPageDown])
2690 scrollOffset.setY(_page->unobscuredContentRect().height());
2691 else if ([charactersIgnoringModifiers isEqualToString:UIKeyInputPageUp])
2692 scrollOffset.setY(-_page->unobscuredContentRect().height());
2697 [_webView _scrollByOffset:scrollOffset];
2701 UIKeyboardImpl *keyboard = [UIKeyboardImpl sharedInstance];
2702 NSString *characters = [event characters];
2704 if (![characters length])
2707 switch ([characters characterAtIndex:0]) {
2708 case kWebBackspaceKey:
2710 if (contentEditable) {
2711 // FIXME: remove deleteFromInput once UIKit adopts deleteFromInputWithFlags
2712 if ([keyboard respondsToSelector:@selector(deleteFromInputWithFlags:)])
2713 [keyboard deleteFromInputWithFlags:event.keyboardFlags];
2715 [keyboard deleteFromInput];
2721 if (!contentEditable) {
2722 [_webView _scrollByOffset:FloatPoint(0, shift ? -_page->unobscuredContentRect().height() : _page->unobscuredContentRect().height())];
2726 [keyboard addInputString:event.characters withFlags:event.keyboardFlags];
2733 if (contentEditable && isCharEvent) {
2734 // Map \r from HW keyboard to \n to match the behavior of the soft keyboard.
2735 [keyboard addInputString:@"\n" withFlags:0];
2740 case kWebDeleteForwardKey:
2741 _page->executeEditCommand(ASCIILiteral("deleteForward"));
2745 if (contentEditable && isCharEvent) {
2746 [keyboard addInputString:event.characters withFlags:event.keyboardFlags];
2755 - (void)executeEditCommandWithCallback:(NSString *)commandName
2757 [self beginSelectionChange];
2758 RetainPtr<WKContentView> view = self;
2759 _page->executeEditCommand(commandName, [view](WebKit::CallbackBase::Error) {
2760 [view endSelectionChange];
2764 - (UITextInputArrowKeyHistory *)_moveUp:(BOOL)extending withHistory:(UITextInputArrowKeyHistory *)history
2766 [self executeEditCommandWithCallback:extending ? @"moveUpAndModifySelection" : @"moveUp"];
2770 - (UITextInputArrowKeyHistory *)_moveDown:(BOOL)extending withHistory:(UITextInputArrowKeyHistory *)history
2772 [self executeEditCommandWithCallback:extending ? @"moveDownAndModifySelection" : @"moveDown"];
2776 - (UITextInputArrowKeyHistory *)_moveLeft:(BOOL)extending withHistory:(UITextInputArrowKeyHistory *)history
2778 [self executeEditCommandWithCallback:extending? @"moveLeftAndModifySelection" : @"moveLeft"];
2782 - (UITextInputArrowKeyHistory *)_moveRight:(BOOL)extending withHistory:(UITextInputArrowKeyHistory *)history
2784 [self executeEditCommandWithCallback:extending ? @"moveRightAndModifySelection" : @"moveRight"];
2788 - (UITextInputArrowKeyHistory *)_moveToStartOfWord:(BOOL)extending withHistory:(UITextInputArrowKeyHistory *)history
2790 [self executeEditCommandWithCallback:extending ? @"moveWordBackwardAndModifySelection" : @"moveWordBackward"];
2794 - (UITextInputArrowKeyHistory *)_moveToStartOfParagraph:(BOOL)extending withHistory:(UITextInputArrowKeyHistory *)history
2796 [self executeEditCommandWithCallback:extending ? @"moveToBeginningOfParagraphAndModifySelection" : @"moveToBeginningOfParagraph"];
2800 - (UITextInputArrowKeyHistory *)_moveToStartOfLine:(BOOL)extending withHistory:(UITextInputArrowKeyHistory *)history
2802 [self executeEditCommandWithCallback:extending ? @"moveToBeginningOfLineAndModifySelection" : @"moveToBeginningOfLine"];
2806 - (UITextInputArrowKeyHistory *)_moveToStartOfDocument:(BOOL)extending withHistory:(UITextInputArrowKeyHistory *)history
2808 [self executeEditCommandWithCallback:extending ? @"moveToBeginningOfDocumentAndModifySelection" : @"moveToBeginningOfDocument"];
2812 - (UITextInputArrowKeyHistory *)_moveToEndOfWord:(BOOL)extending withHistory:(UITextInputArrowKeyHistory *)history
2814 [self executeEditCommandWithCallback:extending ? @"moveWordForwardAndModifySelection" : @"moveWordForward"];
2818 - (UITextInputArrowKeyHistory *)_moveToEndOfParagraph:(BOOL)extending withHistory:(UITextInputArrowKeyHistory *)history
2820 [self executeEditCommandWithCallback:extending ? @"moveToEndOfParagraphAndModifySelection" : @"moveToEndOfParagraph"];
2824 - (UITextInputArrowKeyHistory *)_moveToEndOfLine:(BOOL)extending withHistory:(UITextInputArrowKeyHistory *)history
2826 [self executeEditCommandWithCallback:extending ? @"moveToEndOfLineAndModifySelection" : @"moveToEndOfLine"];
2830 - (UITextInputArrowKeyHistory *)_moveToEndOfDocument:(BOOL)extending withHistory:(UITextInputArrowKeyHistory *)history
2832 [self executeEditCommandWithCallback:extending ? @"moveToEndOfDocumentAndModifySelection" : @"moveToEndOfDocument"];
2836 // Sets a buffer to make room for autocorrection views
2837 - (void)setBottomBufferHeight:(CGFloat)bottomBuffer
2841 - (UIView *)automaticallySelectedOverlay
2846 - (UITextGranularity)selectionGranularity
2848 return UITextGranularityCharacter;
2851 // Should return an array of NSDictionary objects that key/value paries for the final text, correction identifier and
2852 // alternative selection counts using the keys defined at the top of this header.
2853 - (NSArray *)metadataDictionariesForDictationResults
2858 // Returns the dictation result boundaries from position so that text that was not dictated can be excluded from logging.
2859 // If these are not implemented, no text will be logged.
2860 - (UITextPosition *)previousUnperturbedDictationResultBoundaryFromPosition:(UITextPosition *)position
2865 - (UITextPosition *)nextUnperturbedDictationResultBoundaryFromPosition:(UITextPosition *)position
2870 // The can all be (and have been) trivially implemented in terms of UITextInput. Deprecate and remove.
2871 - (void)moveBackward:(unsigned)count
2875 - (void)moveForward:(unsigned)count
2879 - (unichar)characterBeforeCaretSelection
2884 - (NSString *)wordContainingCaretSelection
2889 - (DOMRange *)wordRangeContainingCaretSelection
2894 - (void)setMarkedText:(NSString *)text
2900 return _page->editorState().postLayoutData().hasContent;
2907 - (UIColor *)textColorForCaretSelection
2909 return [UIColor blackColor];
2912 - (UIFont *)fontForCaretSelection
2914 CGFloat zoomScale = 1.0; // FIXME: retrieve the actual document scale factor.
2915 CGFloat scaledSize = _autocorrectionData.fontSize;
2916 if (CGFAbs(zoomScale - 1.0) > FLT_EPSILON)
2917 scaledSize *= zoomScale;
2918 return [UIFont fontWithFamilyName:_autocorrectionData.fontName traits:(UIFontTrait)_autocorrectionData.fontTraits size:scaledSize];
2921 - (BOOL)hasSelection
2926 - (BOOL)isPosition:(UITextPosition *)position atBoundary:(UITextGranularity)granularity inDirection:(UITextDirection)direction
2931 - (UITextPosition *)positionFromPosition:(UITextPosition *)position toBoundary:(UITextGranularity)granularity inDirection:(UITextDirection)direction
2936 - (BOOL)isPosition:(UITextPosition *)position withinTextUnit:(UITextGranularity)granularity inDirection:(UITextDirection)direction
2941 - (UITextRange *)rangeEnclosingPosition:(UITextPosition *)position withGranularity:(UITextGranularity)granularity inDirection:(UITextDirection)direction
2946 - (void)takeTraitsFrom:(UITextInputTraits *)traits
2948 [[self textInputTraits] takeTraitsFrom:traits];
2951 // FIXME: I want to change the name of these functions, but I'm leaving it for now
2952 // to make it easier to look up the corresponding functions in UIKit.
2954 - (void)_startAssistingKeyboard
2956 [self useSelectionAssistantWithMode:UIWebSelectionModeTextOnly];
2957 [self reloadInputViews];
2960 - (void)_stopAssistingKeyboard
2962 [self useSelectionAssistantWithMode:toUIWebSelectionMode([[_webView configuration] selectionGranularity])];
2965 - (const AssistedNodeInformation&)assistedNodeInformation
2967 return _assistedNodeInformation;
2970 - (Vector<OptionItem>&)assistedNodeSelectOptions
2972 return _assistedNodeInformation.selectOptions;
2975 - (UIWebFormAccessory *)formAccessoryView
2977 [self _ensureFormAccessoryView];
2978 return _formAccessoryView.get();
2981 static bool isAssistableInputType(InputType type)
2984 case InputType::ContentEditable:
2985 case InputType::Text:
2986 case InputType::Password:
2987 case InputType::TextArea:
2988 case InputType::Search:
2989 case InputType::Email:
2990 case InputType::URL:
2991 case InputType::Phone:
2992 case InputType::Number:
2993 case InputType::NumberPad:
2994 case InputType::Date:
2995 case InputType::DateTime:
2996 case InputType::DateTimeLocal:
2997 case InputType::Month:
2998 case InputType::Week:
2999 case InputType::Time:
3000 case InputType::Select:
3003 case InputType::None:
3007 ASSERT_NOT_REACHED();
3011 - (void)_startAssistingNode:(const AssistedNodeInformation&)information userIsInteracting:(BOOL)userIsInteracting blurPreviousNode:(BOOL)blurPreviousNode userObject:(NSObject <NSSecureCoding> *)userObject
3013 // FIXME: This is a temporary workaround for <rdar://problem/22126518>. The real fix will involve refactoring
3014 // the way we assist programmatically focused nodes.
3015 if (!userIsInteracting && !_textSelectionAssistant)
3018 if (blurPreviousNode)
3019 [self _stopAssistingNode];
3021 if (!isAssistableInputType(information.elementType))
3024 // FIXME: We should remove this check when we manage to send StartAssistingNode from the WebProcess
3025 // only when it is truly time to show the keyboard.
3026 if (_assistedNodeInformation.elementType == information.elementType && _assistedNodeInformation.elementRect == information.elementRect)
3030 _assistedNodeInformation = information;
3031 _inputPeripheral = nil;
3033 if (![self isFirstResponder])
3034 [self becomeFirstResponder];
3036 [self reloadInputViews];
3038 switch (information.elementType) {
3039 case InputType::Select:
3040 case InputType::DateTimeLocal:
3041 case InputType::Time:
3042 case InputType::Month:
3043 case InputType::Date:
3046 [self _startAssistingKeyboard];
3050 if (information.insideFixedPosition)
3051 [_webView _updateVisibleContentRects];
3053 [self _displayFormNodeInputView];
3055 // _inputPeripheral has been initialized in inputView called by reloadInputViews.
3056 [_inputPeripheral beginEditing];
3058 id <_WKFormDelegate> formDelegate = [_webView _formDelegate];
3059 if ([formDelegate respondsToSelector:@selector(_webView:didStartInputSession:)]) {
3060 _formInputSession = adoptNS([[WKFormInputSession alloc] initWithContentView:self userObject:userObject]);
3061 [formDelegate _webView:_webView didStartInputSession:_formInputSession.get()];
3065 - (void)_stopAssistingNode
3067 [_formInputSession invalidate];
3068 _formInputSession = nil;
3070 _assistedNodeInformation.elementType = InputType::None;
3071 [_inputPeripheral endEditing];
3072 _inputPeripheral = nil;
3074 [self _stopAssistingKeyboard];
3075 [_formAccessoryView hideAutoFillButton];
3076 [self reloadInputViews];
3077 [self _updateAccessory];
3078 // The name is misleading, but this actually clears the selection views and removes any selection.
3079 [_webSelectionAssistant resignedFirstResponder];
3082 - (void)_selectionChanged
3084 _selectionNeedsUpdate = YES;
3085 // If we are changing the selection with a gesture there is no need
3086 // to wait to paint the selection.
3087 if (_usingGestureForSelection)
3088 [self _updateChangedSelection];
3091 - (void)selectWordForReplacement
3093 _page->extendSelection(WordGranularity);
3096 - (void)_updateChangedSelection
3098 [self _updateChangedSelection:NO];
3101 - (void)_updateChangedSelection:(BOOL)force
3103 if (!_selectionNeedsUpdate)
3106 WKSelectionDrawingInfo selectionDrawingInfo(_page->editorState());
3107 if (!force && selectionDrawingInfo == _lastSelectionDrawingInfo)
3110 _lastSelectionDrawingInfo = selectionDrawingInfo;
3112 // FIXME: We need to figure out what to do if the selection is changed by Javascript.
3113 if (_textSelectionAssistant) {
3114 _markedText = (_page->editorState().hasComposition) ? _page->editorState().markedText : String();
3115 if (!_showingTextStyleOptions)
3116 [_textSelectionAssistant selectionChanged];
3117 } else if (!_page->editorState().isContentEditable)
3118 [_webSelectionAssistant selectionChanged];
3119 _selectionNeedsUpdate = NO;
3120 if (_shouldRestoreSelection) {
3121 [_webSelectionAssistant didEndScrollingOverflow];
3122 [_textSelectionAssistant didEndScrollingOverflow];
3123 _shouldRestoreSelection = NO;
3127 - (void)_showPlaybackTargetPicker:(BOOL)hasVideo fromRect:(const IntRect&)elementRect
3129 if (!_airPlayRoutePicker)
3130 _airPlayRoutePicker = adoptNS([[WKAirPlayRoutePicker alloc] initWithView:self]);
3131 [_airPlayRoutePicker show:hasVideo fromRect:elementRect];
3134 - (void)_showRunOpenPanel:(WebOpenPanelParameters*)parameters resultListener:(WebOpenPanelResultListenerProxy*)listener
3136 ASSERT(!_fileUploadPanel);
3137 if (_fileUploadPanel)
3140 _fileUploadPanel = adoptNS([[WKFileUploadPanel alloc] initWithView:self]);
3141 [_fileUploadPanel setDelegate:self];
3142 [_fileUploadPanel presentWithParameters:parameters resultListener:listener];
3145 - (void)fileUploadPanelDidDismiss:(WKFileUploadPanel *)fileUploadPanel
3147 ASSERT(_fileUploadPanel.get() == fileUploadPanel);
3149 [_fileUploadPanel setDelegate:nil];
3150 _fileUploadPanel = nil;
3153 #pragma mark - Implementation of UIWebTouchEventsGestureRecognizerDelegate.
3155 - (BOOL)shouldIgnoreWebTouch
3160 - (BOOL)isAnyTouchOverActiveArea:(NSSet *)touches
3165 #pragma mark - Implementation of WKActionSheetAssistantDelegate.
3167 - (const WebKit::InteractionInformationAtPosition&)positionInformationForActionSheetAssistant:(WKActionSheetAssistant *)assistant
3169 return _positionInformation;
3172 - (void)updatePositionInformationForActionSheetAssistant:(WKActionSheetAssistant *)assistant
3174 _hasValidPositionInformation = NO;
3175 _page->requestPositionInformation(_positionInformation.point);
3178 - (void)actionSheetAssistant:(WKActionSheetAssistant *)assistant performAction:(WebKit::SheetAction)action
3180 _page->performActionOnElement((uint32_t)action);
3183 - (void)actionSheetAssistant:(WKActionSheetAssistant *)assistant openElementAtLocation:(CGPoint)location
3185 [self _attemptClickAtLocation:location];
3189 - (BOOL)actionSheetAssistant:(WKActionSheetAssistant *)assistant shouldIncludeAppLinkActionsForElement:(_WKActivatedElementInfo *)element
3191 return _page->uiClient().shouldIncludeAppLinkActionsForElement(element);
3195 - (RetainPtr<NSArray>)actionSheetAssistant:(WKActionSheetAssistant *)assistant decideActionsForElement:(_WKActivatedElementInfo *)element defaultActions:(RetainPtr<NSArray>)defaultActions
3197 return _page->uiClient().actionsForElement(element, WTF::move(defaultActions));
3200 - (void)actionSheetAssistant:(WKActionSheetAssistant *)assistant willStartInteractionWithElement:(_WKActivatedElementInfo *)element
3202 _page->startInteractionWithElementAtPosition(_positionInformation.point);
3205 - (void)actionSheetAssistantDidStopInteraction:(WKActionSheetAssistant *)assistant
3207 _page->stopInteraction();
3212 #if HAVE(LINK_PREVIEW)
3214 @implementation WKContentView (WKInteractionPreview)
3216 - (void)_registerPreview
3218 if (!_webView.allowsLinkPreview)
3221 _previewItemController = adoptNS([[UIPreviewItemController alloc] initWithView:self]);
3222 [_previewItemController setDelegate:self];
3223 _previewGestureRecognizer = _previewItemController.get().presentationGestureRecognizer;
3224 if ([_previewItemController respondsToSelector:@selector(presentationSecondaryGestureRecognizer)])
3225 _previewSecondaryGestureRecognizer = _previewItemController.get().presentationSecondaryGestureRecognizer;
3228 - (void)_unregisterPreview
3230 [_previewItemController setDelegate:nil];
3231 _previewGestureRecognizer = nil;
3232 _previewSecondaryGestureRecognizer = nil;
3233 _previewItemController = nil;
3236 - (BOOL)_interactionShouldBeginFromPreviewItemController:(UIPreviewItemController *)controller forPosition:(CGPoint)position
3238 if (!_highlightLongPressCanClick)
3241 [self ensurePositionInformationIsUpToDate:position];
3242 if (_positionInformation.clickableElementName != "A" && _positionInformation.clickableElementName != "IMG")
3245 String absoluteLinkURL = _positionInformation.url;
3246 if (_positionInformation.clickableElementName == "A") {
3247 if (absoluteLinkURL.isEmpty())
3249 if (WebCore::protocolIsInHTTPFamily(absoluteLinkURL))
3251 NSURL *targetURL = [NSURL _web_URLWithWTFString:_positionInformation.url];
3252 if ([[getDDDetectionControllerClass() tapAndHoldSchemes] containsObject:[targetURL scheme]])
3259 - (NSDictionary *)_dataForPreviewItemController:(UIPreviewItemController *)controller atPosition:(CGPoint)position type:(UIPreviewItemType *)type
3261 *type = UIPreviewItemTypeNone;
3263 id <WKUIDelegatePrivate> uiDelegate = static_cast<id <WKUIDelegatePrivate>>([_webView UIDelegate]);
3264 BOOL supportsImagePreview = [uiDelegate respondsToSelector:@selector(_webView:commitPreviewedImageWithURL:)];
3265 BOOL canShowImagePreview = _positionInformation.clickableElementName == "IMG" && supportsImagePreview;
3266 BOOL canShowLinkPreview = _positionInformation.clickableElementName == "A" || canShowImagePreview;
3267 BOOL useImageURLForLink = NO;
3269 if (canShowImagePreview && _positionInformation.isAnimatedImage) {
3270 canShowImagePreview = NO;
3271 canShowLinkPreview = YES;
3272 useImageURLForLink = YES;
3275 if (!canShowLinkPreview && !canShowImagePreview)
3278 String absoluteLinkURL = _positionInformation.url;
3279 if (!useImageURLForLink && (absoluteLinkURL.isEmpty() || !WebCore::protocolIsInHTTPFamily(absoluteLinkURL))) {
3280 if (canShowLinkPreview && !canShowImagePreview)
3282 canShowLinkPreview = NO;
3285 NSMutableDictionary *dataForPreview = [[[NSMutableDictionary alloc] init] autorelease];
3286 if (canShowLinkPreview) {
3287 *type = UIPreviewItemTypeLink;
3288 if (useImageURLForLink)
3289 dataForPreview[UIPreviewDataLink] = [NSURL _web_URLWithWTFString:_positionInformation.imageURL];
3291 dataForPreview[UIPreviewDataLink] = [NSURL _web_URLWithWTFString:_positionInformation.url];
3292 } else if (canShowImagePreview) {
3293 *type = UIPreviewItemTypeImage;
3294 dataForPreview[UIPreviewDataLink] = [NSURL _web_URLWithWTFString:_positionInformation.imageURL];
3297 return dataForPreview;
3300 - (CGRect)_presentationRectForPreviewItemController:(UIPreviewItemController *)controller
3302 return _positionInformation.bounds;
3305 - (UIViewController *)_presentedViewControllerForPreviewItemController:(UIPreviewItemController *)controller
3307 id <WKUIDelegatePrivate> uiDelegate = static_cast<id <WKUIDelegatePrivate>>([_webView UIDelegate]);
3309 NSURL *targetURL = controller.previewData[UIPreviewDataLink];
3310 URL coreTargetURL = targetURL;
3311 bool isValidURLForImagePreview = !coreTargetURL.isEmpty() && (WebCore::protocolIsInHTTPFamily(coreTargetURL) || WebCore::protocolIs(coreTargetURL, "data"));
3313 if ([_previewItemController type] == UIPreviewItemTypeLink) {
3314 _highlightLongPressCanClick = NO;
3315 _page->startInteractionWithElementAtPosition(_positionInformation.point);
3317 // Treat animated images like a link preview
3318 if (isValidURLForImagePreview && _positionInformation.isAnimatedImage) {
3319 RetainPtr<_WKActivatedElementInfo> animatedImageElementInfo = adoptNS([[_WKActivatedElementInfo alloc] _initWithType:_WKActivatedElementTypeImage URL:targetURL location:_positionInformation.point title:_positionInformation.title rect:_positionInformation.bounds image:_positionInformation.image.get()]);
3321 if ([uiDelegate respondsToSelector:@selector(_webView:previewViewControllerForAnimatedImageAtURL:defaultActions:elementInfo:imageSize:)]) {
3322 RetainPtr<NSArray> actions = [_actionSheetAssistant defaultActionsForImageSheet:animatedImageElementInfo.get()];
3323 return [uiDelegate _webView:_webView previewViewControllerForAnimatedImageAtURL:targetURL defaultActions:actions.get() elementInfo:animatedImageElementInfo.get() imageSize:_positionInformation.image->size()];
3327 RetainPtr<_WKActivatedElementInfo> elementInfo = adoptNS([[_WKActivatedElementInfo alloc] _initWithType:_WKActivatedElementTypeLink URL:targetURL location:_positionInformation.point title:_positionInformation.title rect:_positionInformation.bounds image:_positionInformation.image.get()]);
3329 RetainPtr<NSArray> actions = [_actionSheetAssistant defaultActionsForLinkSheet:elementInfo.get()];
3330 if ([uiDelegate respondsToSelector:@selector(_webView:previewViewControllerForURL:defaultActions:elementInfo:)])
3331 return [uiDelegate _webView:_webView previewViewControllerForURL:targetURL defaultActions:actions.get() elementInfo:elementInfo.get()];
3333 if ([uiDelegate respondsToSelector:@selector(_webView:previewViewControllerForURL:)])
3334 return [uiDelegate _webView:_webView previewViewControllerForURL:targetURL];
3338 if ([_previewItemController type] == UIPreviewItemTypeImage) {
3339 if (!isValidURLForImagePreview)
3342 RetainPtr<_WKActivatedElementInfo> elementInfo = adoptNS([[_WKActivatedElementInfo alloc] _initWithType:_WKActivatedElementTypeImage URL:targetURL location:_positionInformation.point title:_positionInformation.title rect:_positionInformation.bounds image:_positionInformation.image.get()]);
3343 _page->startInteractionWithElementAtPosition(_positionInformation.point);
3345 if ([uiDelegate respondsToSelector:@selector(_webView:willPreviewImageWithURL:)])
3346 [uiDelegate _webView:_webView willPreviewImageWithURL:targetURL];
3347 return [[[WKImagePreviewViewController alloc] initWithCGImage:_positionInformation.image->makeCGImageCopy() defaultActions:[_actionSheetAssistant defaultActionsForImageSheet:elementInfo.get()] elementInfo:elementInfo] autorelease];
3353 - (void)_previewItemController:(UIPreviewItemController *)controller commitPreview:(UIViewController *)viewController
3355 id <WKUIDelegatePrivate> uiDelegate = static_cast<id <WKUIDelegatePrivate>>([_webView UIDelegate]);
3356 if ([_previewItemController type] == UIPreviewItemTypeImage) {
3357 if ([uiDelegate respondsToSelector:@selector(_webView:commitPreviewedImageWithURL:)]) {
3358 String absoluteImageURL = _positionInformation.imageURL;
3359 if (absoluteImageURL.isEmpty() || !(WebCore::protocolIsInHTTPFamily(absoluteImageURL) || WebCore::protocolIs(absoluteImageURL, "data")))
3361 [uiDelegate _webView:_webView commitPreviewedImageWithURL:[NSURL _web_URLWithWTFString:absoluteImageURL]];