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