1/* 2 * Copyright (C) 2010, 2011 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 "WKView.h" 28 29#if PLATFORM(MAC) 30 31#if USE(DICTATION_ALTERNATIVES) 32#import <AppKit/NSTextAlternatives.h> 33#import <AppKit/NSAttributedString.h> 34#endif 35 36#import "APIHistoryClient.h" 37#import "AttributedString.h" 38#import "ColorSpaceData.h" 39#import "DataReference.h" 40#import "EditingRange.h" 41#import "EditorState.h" 42#import "FindIndicator.h" 43#import "FindIndicatorWindow.h" 44#import "LayerTreeContext.h" 45#import "Logging.h" 46#import "NativeWebKeyboardEvent.h" 47#import "NativeWebMouseEvent.h" 48#import "NativeWebWheelEvent.h" 49#import "PageClientImpl.h" 50#import "PasteboardTypes.h" 51#import "RemoteLayerTreeDrawingAreaProxy.h" 52#import "StringUtilities.h" 53#import "TextChecker.h" 54#import "TextCheckerState.h" 55#import "TiledCoreAnimationDrawingAreaProxy.h" 56#import "ViewGestureController.h" 57#import "ViewSnapshotStore.h" 58#import "WKAPICast.h" 59#import "WKFullScreenWindowController.h" 60#import "WKPrintingView.h" 61#import "WKProcessPoolInternal.h" 62#import "WKStringCF.h" 63#import "WKTextInputWindowController.h" 64#import "WKViewInternal.h" 65#import "WKViewPrivate.h" 66#import "WebBackForwardList.h" 67#import "WebContext.h" 68#import "WebEventFactory.h" 69#import "WebKit2Initialize.h" 70#import "WebPage.h" 71#import "WebPageGroup.h" 72#import "WebPageProxy.h" 73#import "WebPreferences.h" 74#import "WebProcessProxy.h" 75#import "WebSystemInterface.h" 76#import "_WKThumbnailViewInternal.h" 77#import <QuartzCore/QuartzCore.h> 78#import <WebCore/AXObjectCache.h> 79#import <WebCore/ColorMac.h> 80#import <WebCore/DragController.h> 81#import <WebCore/DragData.h> 82#import <WebCore/FloatRect.h> 83#import <WebCore/Image.h> 84#import <WebCore/IntRect.h> 85#import <WebCore/FileSystem.h> 86#import <WebCore/KeyboardEvent.h> 87#import <WebCore/LocalizedStrings.h> 88#import <WebCore/PlatformEventFactoryMac.h> 89#import <WebCore/PlatformScreen.h> 90#import <WebCore/Region.h> 91#import <WebCore/SharedBuffer.h> 92#import <WebCore/TextAlternativeWithRange.h> 93#import <WebCore/TextUndoInsertionMarkupMac.h> 94#import <WebCore/WebActionDisablingCALayerDelegate.h> 95#import <WebCore/WebCoreCALayerExtras.h> 96#import <WebCore/WebCoreFullScreenPlaceholderView.h> 97#import <WebCore/WebCoreFullScreenWindow.h> 98#import <WebCore/WebCoreNSStringExtras.h> 99#import <WebKitSystemInterface.h> 100#import <sys/stat.h> 101#import <wtf/RefPtr.h> 102#import <wtf/RetainPtr.h> 103#import <wtf/RunLoop.h> 104 105/* API internals. */ 106#import "WKBrowsingContextControllerInternal.h" 107#import "WKBrowsingContextGroupPrivate.h" 108#import "WKProcessGroupPrivate.h" 109 110@interface NSApplication (WKNSApplicationDetails) 111- (void)speakString:(NSString *)string; 112- (void)_setCurrentEvent:(NSEvent *)event; 113@end 114 115@interface NSWindow (WKNSWindowDetails) 116- (NSRect)_intersectBottomCornersWithRect:(NSRect)viewRect; 117- (void)_maskRoundedBottomCorners:(NSRect)clipRect; 118@end 119 120#if USE(ASYNC_NSTEXTINPUTCLIENT) 121@interface NSTextInputContext (WKNSTextInputContextDetails) 122- (void)handleEvent:(NSEvent *)theEvent completionHandler:(void(^)(BOOL handled))completionHandler; 123- (void)handleEventByInputMethod:(NSEvent *)theEvent completionHandler:(void(^)(BOOL handled))completionHandler; 124- (BOOL)handleEventByKeyboardLayout:(NSEvent *)theEvent; 125@end 126#endif 127 128#if defined(__has_include) && __has_include(<CoreGraphics/CoreGraphicsPrivate.h>) 129#import <CoreGraphics/CoreGraphicsPrivate.h> 130#endif 131 132extern "C" { 133typedef uint32_t CGSConnectionID; 134typedef uint32_t CGSWindowID; 135CGSConnectionID CGSMainConnectionID(void); 136CGError CGSGetScreenRectForWindow(CGSConnectionID cid, CGSWindowID wid, CGRect *rect); 137}; 138 139using namespace WebKit; 140using namespace WebCore; 141 142namespace WebKit { 143 144typedef id <NSValidatedUserInterfaceItem> ValidationItem; 145typedef Vector<RetainPtr<ValidationItem>> ValidationVector; 146typedef HashMap<String, ValidationVector> ValidationMap; 147 148} 149 150#if !USE(ASYNC_NSTEXTINPUTCLIENT) 151struct WKViewInterpretKeyEventsParameters { 152 bool eventInterpretationHadSideEffects; 153 bool consumedByIM; 154 bool executingSavedKeypressCommands; 155 Vector<KeypressCommand>* commands; 156}; 157#endif 158 159@interface WKViewData : NSObject { 160@public 161 std::unique_ptr<PageClientImpl> _pageClient; 162 RefPtr<WebPageProxy> _page; 163 164#if WK_API_ENABLED 165 RetainPtr<WKBrowsingContextController> _browsingContextController; 166#endif 167 168 // For ToolTips. 169 NSToolTipTag _lastToolTipTag; 170 id _trackingRectOwner; 171 void* _trackingRectUserData; 172 173 RetainPtr<NSView> _layerHostingView; 174 175 RetainPtr<id> _remoteAccessibilityChild; 176 177 // For asynchronous validation. 178 ValidationMap _validationMap; 179 180 std::unique_ptr<FindIndicatorWindow> _findIndicatorWindow; 181 182 // We keep here the event when resending it to 183 // the application to distinguish the case of a new event from one 184 // that has been already sent to WebCore. 185 RetainPtr<NSEvent> _keyDownEventBeingResent; 186#if USE(ASYNC_NSTEXTINPUTCLIENT) 187 Vector<KeypressCommand>* _collectedKeypressCommands; 188#else 189 WKViewInterpretKeyEventsParameters* _interpretKeyEventsParameters; 190#endif 191 192 NSSize _resizeScrollOffset; 193 194 // The identifier of the plug-in we want to send complex text input to, or 0 if there is none. 195 uint64_t _pluginComplexTextInputIdentifier; 196 197 // The state of complex text input for the plug-in. 198 PluginComplexTextInputState _pluginComplexTextInputState; 199 200 bool _inBecomeFirstResponder; 201 bool _inResignFirstResponder; 202 NSEvent *_mouseDownEvent; 203 BOOL _ignoringMouseDraggedEvents; 204 205 id _flagsChangedEventMonitor; 206 207#if ENABLE(FULLSCREEN_API) 208 RetainPtr<WKFullScreenWindowController> _fullScreenWindowController; 209#endif 210 211 BOOL _hasSpellCheckerDocumentTag; 212 NSInteger _spellCheckerDocumentTag; 213 214 BOOL _inSecureInputState; 215 216 NSRect _windowBottomCornerIntersectionRect; 217 218 unsigned _frameSizeUpdatesDisabledCount; 219 BOOL _shouldDeferViewInWindowChanges; 220 221 BOOL _viewInWindowChangeWasDeferred; 222 223 BOOL _needsViewFrameInWindowCoordinates; 224 BOOL _didScheduleWindowAndViewFrameUpdate; 225 226 RetainPtr<NSColorSpace> _colorSpace; 227 228 RefPtr<WebCore::Image> _promisedImage; 229 String _promisedFilename; 230 String _promisedURL; 231 232 NSSize _intrinsicContentSize; 233 BOOL _clipsToVisibleRect; 234 NSRect _contentPreparationRect; 235 BOOL _useContentPreparationRectForVisibleRect; 236 BOOL _windowOcclusionDetectionEnabled; 237 238 std::unique_ptr<ViewGestureController> _gestureController; 239 BOOL _allowsMagnification; 240 BOOL _allowsBackForwardNavigationGestures; 241 242 RetainPtr<CALayer> _rootLayer; 243 244 BOOL _didScheduleSetTopContentInset; 245 CGFloat _topContentInset; 246 247#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101000 248 BOOL _automaticallyAdjustsContentInsets; 249#endif 250 251#if WK_API_ENABLED 252 _WKThumbnailView *_thumbnailView; 253#endif 254} 255 256@end 257 258@implementation WKViewData 259@end 260 261@interface WKResponderChainSink : NSResponder { 262 NSResponder *_lastResponderInChain; 263 bool _didReceiveUnhandledCommand; 264} 265- (id)initWithResponderChain:(NSResponder *)chain; 266- (void)detach; 267- (bool)didReceiveUnhandledCommand; 268@end 269 270@interface WKFlippedView : NSView 271@end 272 273@implementation WKFlippedView 274 275- (BOOL)isFlipped 276{ 277 return YES; 278} 279 280@end 281 282@implementation WKView 283 284#if WK_API_ENABLED 285 286- (id)initWithFrame:(NSRect)frame processGroup:(WKProcessGroup *)processGroup browsingContextGroup:(WKBrowsingContextGroup *)browsingContextGroup 287{ 288 return [self initWithFrame:frame contextRef:processGroup._contextRef pageGroupRef:browsingContextGroup._pageGroupRef relatedToPage:nil]; 289} 290 291- (id)initWithFrame:(NSRect)frame processGroup:(WKProcessGroup *)processGroup browsingContextGroup:(WKBrowsingContextGroup *)browsingContextGroup relatedToView:(WKView *)relatedView 292{ 293 return [self initWithFrame:frame contextRef:processGroup._contextRef pageGroupRef:browsingContextGroup._pageGroupRef relatedToPage:relatedView ? toAPI(relatedView->_data->_page.get()) : nil]; 294} 295 296#endif // WK_API_ENABLED 297 298- (void)dealloc 299{ 300 _data->_page->close(); 301 302#if WK_API_ENABLED 303 ASSERT(!_data->_thumbnailView); 304#endif 305 ASSERT(!_data->_inSecureInputState); 306 307 [_data release]; 308 _data = nil; 309 310 NSNotificationCenter* workspaceNotificationCenter = [[NSWorkspace sharedWorkspace] notificationCenter]; 311 [workspaceNotificationCenter removeObserver:self name:NSWorkspaceActiveSpaceDidChangeNotification object:nil]; 312 313 [[NSNotificationCenter defaultCenter] removeObserver:self name:NSApplicationWillTerminateNotification object:NSApp]; 314 315 WebContext::statistics().wkViewCount--; 316 317 [super dealloc]; 318} 319 320#if WK_API_ENABLED 321 322- (WKBrowsingContextController *)browsingContextController 323{ 324 if (!_data->_browsingContextController) 325 _data->_browsingContextController = adoptNS([[WKBrowsingContextController alloc] _initWithPageRef:toAPI(_data->_page.get())]); 326 327 return _data->_browsingContextController.get(); 328} 329 330#endif // WK_API_ENABLED 331 332- (void)setDrawsBackground:(BOOL)drawsBackground 333{ 334 _data->_page->setDrawsBackground(drawsBackground); 335} 336 337- (BOOL)drawsBackground 338{ 339 return _data->_page->drawsBackground(); 340} 341 342- (void)setDrawsTransparentBackground:(BOOL)drawsTransparentBackground 343{ 344 _data->_page->setDrawsTransparentBackground(drawsTransparentBackground); 345} 346 347- (BOOL)drawsTransparentBackground 348{ 349 return _data->_page->drawsTransparentBackground(); 350} 351 352- (BOOL)acceptsFirstResponder 353{ 354 return YES; 355} 356 357- (BOOL)becomeFirstResponder 358{ 359 NSSelectionDirection direction = [[self window] keyViewSelectionDirection]; 360 361 _data->_inBecomeFirstResponder = true; 362 363 [self _updateSecureInputState]; 364 _data->_page->viewStateDidChange(ViewState::IsFocused); 365 366 _data->_inBecomeFirstResponder = false; 367 368 if (direction != NSDirectSelection) { 369 NSEvent *event = [NSApp currentEvent]; 370 NSEvent *keyboardEvent = nil; 371 if ([event type] == NSKeyDown || [event type] == NSKeyUp) 372 keyboardEvent = event; 373 _data->_page->setInitialFocus(direction == NSSelectingNext, keyboardEvent != nil, NativeWebKeyboardEvent(keyboardEvent, false, Vector<KeypressCommand>())); 374 } 375 return YES; 376} 377 378- (BOOL)resignFirstResponder 379{ 380 _data->_inResignFirstResponder = true; 381 382#if USE(ASYNC_NSTEXTINPUTCLIENT) 383 _data->_page->confirmCompositionAsync(); 384#else 385 if (_data->_page->editorState().hasComposition && !_data->_page->editorState().shouldIgnoreCompositionSelectionChange) 386 _data->_page->cancelComposition(); 387#endif 388 389 [self _notifyInputContextAboutDiscardedComposition]; 390 391 [self _resetSecureInputState]; 392 393 if (!_data->_page->maintainsInactiveSelection()) 394 _data->_page->clearSelection(); 395 396 _data->_page->viewStateDidChange(ViewState::IsFocused); 397 398 _data->_inResignFirstResponder = false; 399 400 return YES; 401} 402 403- (void)viewWillStartLiveResize 404{ 405 _data->_page->viewWillStartLiveResize(); 406} 407 408- (void)viewDidEndLiveResize 409{ 410 _data->_page->viewWillEndLiveResize(); 411} 412 413- (BOOL)isFlipped 414{ 415 return YES; 416} 417 418- (NSSize)intrinsicContentSize 419{ 420 return _data->_intrinsicContentSize; 421} 422 423- (void)prepareContentInRect:(NSRect)rect 424{ 425 _data->_contentPreparationRect = rect; 426 _data->_useContentPreparationRectForVisibleRect = YES; 427 428 [self _updateViewExposedRect]; 429} 430 431- (void)_updateViewExposedRect 432{ 433 NSRect exposedRect = [self visibleRect]; 434 435 if (_data->_useContentPreparationRectForVisibleRect) 436 exposedRect = NSUnionRect(_data->_contentPreparationRect, exposedRect); 437 438 if (auto drawingArea = _data->_page->drawingArea()) 439 drawingArea->setExposedRect(_data->_clipsToVisibleRect ? FloatRect(exposedRect) : FloatRect::infiniteRect()); 440} 441 442- (void)setFrameSize:(NSSize)size 443{ 444 [super setFrameSize:size]; 445 446 if (![self frameSizeUpdatesDisabled]) { 447 if (_data->_clipsToVisibleRect) 448 [self _updateViewExposedRect]; 449 [self _setDrawingAreaSize:size]; 450 } 451} 452 453- (void)_updateWindowAndViewFrames 454{ 455 if (_data->_clipsToVisibleRect) 456 [self _updateViewExposedRect]; 457 458 if (_data->_didScheduleWindowAndViewFrameUpdate) 459 return; 460 461 _data->_didScheduleWindowAndViewFrameUpdate = YES; 462 463 dispatch_async(dispatch_get_main_queue(), ^{ 464 _data->_didScheduleWindowAndViewFrameUpdate = NO; 465 466 NSRect viewFrameInWindowCoordinates = NSZeroRect; 467 NSPoint accessibilityPosition = NSZeroPoint; 468 469 if (_data->_needsViewFrameInWindowCoordinates) 470 viewFrameInWindowCoordinates = [self convertRect:self.frame toView:nil]; 471 472#pragma clang diagnostic push 473#pragma clang diagnostic ignored "-Wdeprecated-declarations" 474 if (WebCore::AXObjectCache::accessibilityEnabled()) 475 accessibilityPosition = [[self accessibilityAttributeValue:NSAccessibilityPositionAttribute] pointValue]; 476#pragma clang diagnostic pop 477 478 _data->_page->windowAndViewFramesChanged(viewFrameInWindowCoordinates, accessibilityPosition); 479 }); 480} 481 482- (void)renewGState 483{ 484 // Hide the find indicator. 485 _data->_findIndicatorWindow = nullptr; 486 487 // Update the view frame. 488 if ([self window]) 489 [self _updateWindowAndViewFrames]; 490 491#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101000 492 [self _updateContentInsetsIfAutomatic]; 493#endif 494 495 [super renewGState]; 496} 497 498- (void)_setPluginComplexTextInputState:(PluginComplexTextInputState)pluginComplexTextInputState 499{ 500 _data->_pluginComplexTextInputState = pluginComplexTextInputState; 501 502 if (_data->_pluginComplexTextInputState != PluginComplexTextInputDisabled) 503 return; 504 505 // Send back an empty string to the plug-in. This will disable text input. 506 _data->_page->sendComplexTextInputToPlugin(_data->_pluginComplexTextInputIdentifier, String()); 507} 508 509typedef HashMap<SEL, String> SelectorNameMap; 510 511// Map selectors into Editor command names. 512// This is not needed for any selectors that have the same name as the Editor command. 513static const SelectorNameMap* createSelectorExceptionMap() 514{ 515 SelectorNameMap* map = new HashMap<SEL, String>; 516 517 map->add(@selector(insertNewlineIgnoringFieldEditor:), "InsertNewline"); 518 map->add(@selector(insertParagraphSeparator:), "InsertNewline"); 519 map->add(@selector(insertTabIgnoringFieldEditor:), "InsertTab"); 520 map->add(@selector(pageDown:), "MovePageDown"); 521 map->add(@selector(pageDownAndModifySelection:), "MovePageDownAndModifySelection"); 522 map->add(@selector(pageUp:), "MovePageUp"); 523 map->add(@selector(pageUpAndModifySelection:), "MovePageUpAndModifySelection"); 524 map->add(@selector(scrollPageDown:), "ScrollPageForward"); 525 map->add(@selector(scrollPageUp:), "ScrollPageBackward"); 526 527 return map; 528} 529 530static String commandNameForSelector(SEL selector) 531{ 532 // Check the exception map first. 533 static const SelectorNameMap* exceptionMap = createSelectorExceptionMap(); 534 SelectorNameMap::const_iterator it = exceptionMap->find(selector); 535 if (it != exceptionMap->end()) 536 return it->value; 537 538 // Remove the trailing colon. 539 // No need to capitalize the command name since Editor command names are 540 // not case sensitive. 541 const char* selectorName = sel_getName(selector); 542 size_t selectorNameLength = strlen(selectorName); 543 if (selectorNameLength < 2 || selectorName[selectorNameLength - 1] != ':') 544 return String(); 545 return String(selectorName, selectorNameLength - 1); 546} 547 548// Editing commands 549 550#define WEBCORE_COMMAND(command) - (void)command:(id)sender { _data->_page->executeEditCommand(commandNameForSelector(_cmd)); } 551 552WEBCORE_COMMAND(alignCenter) 553WEBCORE_COMMAND(alignJustified) 554WEBCORE_COMMAND(alignLeft) 555WEBCORE_COMMAND(alignRight) 556WEBCORE_COMMAND(copy) 557WEBCORE_COMMAND(cut) 558WEBCORE_COMMAND(delete) 559WEBCORE_COMMAND(deleteBackward) 560WEBCORE_COMMAND(deleteBackwardByDecomposingPreviousCharacter) 561WEBCORE_COMMAND(deleteForward) 562WEBCORE_COMMAND(deleteToBeginningOfLine) 563WEBCORE_COMMAND(deleteToBeginningOfParagraph) 564WEBCORE_COMMAND(deleteToEndOfLine) 565WEBCORE_COMMAND(deleteToEndOfParagraph) 566WEBCORE_COMMAND(deleteToMark) 567WEBCORE_COMMAND(deleteWordBackward) 568WEBCORE_COMMAND(deleteWordForward) 569WEBCORE_COMMAND(ignoreSpelling) 570WEBCORE_COMMAND(indent) 571WEBCORE_COMMAND(insertBacktab) 572WEBCORE_COMMAND(insertLineBreak) 573WEBCORE_COMMAND(insertNewline) 574WEBCORE_COMMAND(insertNewlineIgnoringFieldEditor) 575WEBCORE_COMMAND(insertParagraphSeparator) 576WEBCORE_COMMAND(insertTab) 577WEBCORE_COMMAND(insertTabIgnoringFieldEditor) 578WEBCORE_COMMAND(makeTextWritingDirectionLeftToRight) 579WEBCORE_COMMAND(makeTextWritingDirectionNatural) 580WEBCORE_COMMAND(makeTextWritingDirectionRightToLeft) 581WEBCORE_COMMAND(moveBackward) 582WEBCORE_COMMAND(moveBackwardAndModifySelection) 583WEBCORE_COMMAND(moveDown) 584WEBCORE_COMMAND(moveDownAndModifySelection) 585WEBCORE_COMMAND(moveForward) 586WEBCORE_COMMAND(moveForwardAndModifySelection) 587WEBCORE_COMMAND(moveLeft) 588WEBCORE_COMMAND(moveLeftAndModifySelection) 589WEBCORE_COMMAND(moveParagraphBackwardAndModifySelection) 590WEBCORE_COMMAND(moveParagraphForwardAndModifySelection) 591WEBCORE_COMMAND(moveRight) 592WEBCORE_COMMAND(moveRightAndModifySelection) 593WEBCORE_COMMAND(moveToBeginningOfDocument) 594WEBCORE_COMMAND(moveToBeginningOfDocumentAndModifySelection) 595WEBCORE_COMMAND(moveToBeginningOfLine) 596WEBCORE_COMMAND(moveToBeginningOfLineAndModifySelection) 597WEBCORE_COMMAND(moveToBeginningOfParagraph) 598WEBCORE_COMMAND(moveToBeginningOfParagraphAndModifySelection) 599WEBCORE_COMMAND(moveToBeginningOfSentence) 600WEBCORE_COMMAND(moveToBeginningOfSentenceAndModifySelection) 601WEBCORE_COMMAND(moveToEndOfDocument) 602WEBCORE_COMMAND(moveToEndOfDocumentAndModifySelection) 603WEBCORE_COMMAND(moveToEndOfLine) 604WEBCORE_COMMAND(moveToEndOfLineAndModifySelection) 605WEBCORE_COMMAND(moveToEndOfParagraph) 606WEBCORE_COMMAND(moveToEndOfParagraphAndModifySelection) 607WEBCORE_COMMAND(moveToEndOfSentence) 608WEBCORE_COMMAND(moveToEndOfSentenceAndModifySelection) 609WEBCORE_COMMAND(moveToLeftEndOfLine) 610WEBCORE_COMMAND(moveToLeftEndOfLineAndModifySelection) 611WEBCORE_COMMAND(moveToRightEndOfLine) 612WEBCORE_COMMAND(moveToRightEndOfLineAndModifySelection) 613WEBCORE_COMMAND(moveUp) 614WEBCORE_COMMAND(moveUpAndModifySelection) 615WEBCORE_COMMAND(moveWordBackward) 616WEBCORE_COMMAND(moveWordBackwardAndModifySelection) 617WEBCORE_COMMAND(moveWordForward) 618WEBCORE_COMMAND(moveWordForwardAndModifySelection) 619WEBCORE_COMMAND(moveWordLeft) 620WEBCORE_COMMAND(moveWordLeftAndModifySelection) 621WEBCORE_COMMAND(moveWordRight) 622WEBCORE_COMMAND(moveWordRightAndModifySelection) 623WEBCORE_COMMAND(outdent) 624WEBCORE_COMMAND(pageDown) 625WEBCORE_COMMAND(pageDownAndModifySelection) 626WEBCORE_COMMAND(pageUp) 627WEBCORE_COMMAND(pageUpAndModifySelection) 628WEBCORE_COMMAND(paste) 629WEBCORE_COMMAND(pasteAsPlainText) 630WEBCORE_COMMAND(scrollPageDown) 631WEBCORE_COMMAND(scrollPageUp) 632WEBCORE_COMMAND(scrollLineDown) 633WEBCORE_COMMAND(scrollLineUp) 634WEBCORE_COMMAND(scrollToBeginningOfDocument) 635WEBCORE_COMMAND(scrollToEndOfDocument) 636WEBCORE_COMMAND(selectAll) 637WEBCORE_COMMAND(selectLine) 638WEBCORE_COMMAND(selectParagraph) 639WEBCORE_COMMAND(selectSentence) 640WEBCORE_COMMAND(selectToMark) 641WEBCORE_COMMAND(selectWord) 642WEBCORE_COMMAND(setMark) 643WEBCORE_COMMAND(subscript) 644WEBCORE_COMMAND(superscript) 645WEBCORE_COMMAND(swapWithMark) 646WEBCORE_COMMAND(takeFindStringFromSelection) 647WEBCORE_COMMAND(transpose) 648WEBCORE_COMMAND(underline) 649WEBCORE_COMMAND(unscript) 650WEBCORE_COMMAND(yank) 651WEBCORE_COMMAND(yankAndSelect) 652 653#undef WEBCORE_COMMAND 654 655// This method is needed to support Mac OS X services. 656 657- (BOOL)writeSelectionToPasteboard:(NSPasteboard *)pasteboard types:(NSArray *)types 658{ 659 size_t numTypes = [types count]; 660 [pasteboard declareTypes:types owner:nil]; 661 for (size_t i = 0; i < numTypes; ++i) { 662 if ([[types objectAtIndex:i] isEqualTo:NSStringPboardType]) 663 [pasteboard setString:_data->_page->stringSelectionForPasteboard() forType:NSStringPboardType]; 664 else { 665 RefPtr<SharedBuffer> buffer = _data->_page->dataSelectionForPasteboard([types objectAtIndex:i]); 666 [pasteboard setData:buffer ? buffer->createNSData().get() : nil forType:[types objectAtIndex:i]]; 667 } 668 } 669 return YES; 670} 671 672- (void)centerSelectionInVisibleArea:(id)sender 673{ 674 _data->_page->centerSelectionInVisibleArea(); 675} 676 677// This method is needed to support Mac OS X services. 678 679- (id)validRequestorForSendType:(NSString *)sendType returnType:(NSString *)returnType 680{ 681 EditorState editorState = _data->_page->editorState(); 682 BOOL isValidSendType = NO; 683 684 if (sendType && !editorState.selectionIsNone) { 685 if (editorState.isInPlugin) 686 isValidSendType = [sendType isEqualToString:NSStringPboardType]; 687 else 688 isValidSendType = [PasteboardTypes::forSelection() containsObject:sendType]; 689 } 690 691 BOOL isValidReturnType = NO; 692 if (!returnType) 693 isValidReturnType = YES; 694 else if ([PasteboardTypes::forEditing() containsObject:returnType] && editorState.isContentEditable) { 695 // We can insert strings in any editable context. We can insert other types, like images, only in rich edit contexts. 696 isValidReturnType = editorState.isContentRichlyEditable || [returnType isEqualToString:NSStringPboardType]; 697 } 698 if (isValidSendType && isValidReturnType) 699 return self; 700 return [[self nextResponder] validRequestorForSendType:sendType returnType:returnType]; 701} 702 703// This method is needed to support Mac OS X services. 704 705- (BOOL)readSelectionFromPasteboard:(NSPasteboard *)pasteboard 706{ 707 return _data->_page->readSelectionFromPasteboard([pasteboard name]); 708} 709 710/* 711 712When possible, editing-related methods should be implemented in WebCore with the 713EditorCommand mechanism and invoked via WEBCORE_COMMAND, rather than implementing 714individual methods here with Mac-specific code. 715 716Editing-related methods still unimplemented that are implemented in WebKit1: 717 718- (void)capitalizeWord:(id)sender; 719- (void)changeFont:(id)sender; 720- (void)complete:(id)sender; 721- (void)copyFont:(id)sender; 722- (void)lowercaseWord:(id)sender; 723- (void)makeBaseWritingDirectionLeftToRight:(id)sender; 724- (void)makeBaseWritingDirectionNatural:(id)sender; 725- (void)makeBaseWritingDirectionRightToLeft:(id)sender; 726- (void)pasteFont:(id)sender; 727- (void)scrollLineDown:(id)sender; 728- (void)scrollLineUp:(id)sender; 729- (void)showGuessPanel:(id)sender; 730- (void)uppercaseWord:(id)sender; 731 732Some other editing-related methods still unimplemented: 733 734- (void)changeCaseOfLetter:(id)sender; 735- (void)copyRuler:(id)sender; 736- (void)insertContainerBreak:(id)sender; 737- (void)insertDoubleQuoteIgnoringSubstitution:(id)sender; 738- (void)insertSingleQuoteIgnoringSubstitution:(id)sender; 739- (void)pasteRuler:(id)sender; 740- (void)toggleRuler:(id)sender; 741- (void)transposeWords:(id)sender; 742 743*/ 744 745// Menu items validation 746 747static NSMenuItem *menuItem(id <NSValidatedUserInterfaceItem> item) 748{ 749 if (![(NSObject *)item isKindOfClass:[NSMenuItem class]]) 750 return nil; 751 return (NSMenuItem *)item; 752} 753 754static NSToolbarItem *toolbarItem(id <NSValidatedUserInterfaceItem> item) 755{ 756 if (![(NSObject *)item isKindOfClass:[NSToolbarItem class]]) 757 return nil; 758 return (NSToolbarItem *)item; 759} 760 761- (BOOL)validateUserInterfaceItem:(id <NSValidatedUserInterfaceItem>)item 762{ 763 SEL action = [item action]; 764 765 if (action == @selector(showGuessPanel:)) { 766 if (NSMenuItem *menuItem = ::menuItem(item)) 767 [menuItem setTitle:contextMenuItemTagShowSpellingPanel(![[[NSSpellChecker sharedSpellChecker] spellingPanel] isVisible])]; 768 return _data->_page->editorState().isContentEditable; 769 } 770 771 if (action == @selector(checkSpelling:) || action == @selector(changeSpelling:)) 772 return _data->_page->editorState().isContentEditable; 773 774 if (action == @selector(toggleContinuousSpellChecking:)) { 775 bool enabled = TextChecker::isContinuousSpellCheckingAllowed(); 776 bool checked = enabled && TextChecker::state().isContinuousSpellCheckingEnabled; 777 [menuItem(item) setState:checked ? NSOnState : NSOffState]; 778 return enabled; 779 } 780 781 if (action == @selector(toggleGrammarChecking:)) { 782 bool checked = TextChecker::state().isGrammarCheckingEnabled; 783 [menuItem(item) setState:checked ? NSOnState : NSOffState]; 784 return YES; 785 } 786 787 if (action == @selector(toggleAutomaticSpellingCorrection:)) { 788 bool checked = TextChecker::state().isAutomaticSpellingCorrectionEnabled; 789 [menuItem(item) setState:checked ? NSOnState : NSOffState]; 790 return _data->_page->editorState().isContentEditable; 791 } 792 793 if (action == @selector(orderFrontSubstitutionsPanel:)) { 794 if (NSMenuItem *menuItem = ::menuItem(item)) 795 [menuItem setTitle:contextMenuItemTagShowSubstitutions(![[[NSSpellChecker sharedSpellChecker] substitutionsPanel] isVisible])]; 796 return _data->_page->editorState().isContentEditable; 797 } 798 799 if (action == @selector(toggleSmartInsertDelete:)) { 800 bool checked = _data->_page->isSmartInsertDeleteEnabled(); 801 [menuItem(item) setState:checked ? NSOnState : NSOffState]; 802 return _data->_page->editorState().isContentEditable; 803 } 804 805 if (action == @selector(toggleAutomaticQuoteSubstitution:)) { 806 bool checked = TextChecker::state().isAutomaticQuoteSubstitutionEnabled; 807 [menuItem(item) setState:checked ? NSOnState : NSOffState]; 808 return _data->_page->editorState().isContentEditable; 809 } 810 811 if (action == @selector(toggleAutomaticDashSubstitution:)) { 812 bool checked = TextChecker::state().isAutomaticDashSubstitutionEnabled; 813 [menuItem(item) setState:checked ? NSOnState : NSOffState]; 814 return _data->_page->editorState().isContentEditable; 815 } 816 817 if (action == @selector(toggleAutomaticLinkDetection:)) { 818 bool checked = TextChecker::state().isAutomaticLinkDetectionEnabled; 819 [menuItem(item) setState:checked ? NSOnState : NSOffState]; 820 return _data->_page->editorState().isContentEditable; 821 } 822 823 if (action == @selector(toggleAutomaticTextReplacement:)) { 824 bool checked = TextChecker::state().isAutomaticTextReplacementEnabled; 825 [menuItem(item) setState:checked ? NSOnState : NSOffState]; 826 return _data->_page->editorState().isContentEditable; 827 } 828 829 if (action == @selector(uppercaseWord:) || action == @selector(lowercaseWord:) || action == @selector(capitalizeWord:)) 830 return _data->_page->editorState().selectionIsRange && _data->_page->editorState().isContentEditable; 831 832 if (action == @selector(stopSpeaking:)) 833 return [NSApp isSpeaking]; 834 835 // The centerSelectionInVisibleArea: selector is enabled if there's a selection range or if there's an insertion point in an editable area. 836 if (action == @selector(centerSelectionInVisibleArea:)) 837 return _data->_page->editorState().selectionIsRange || (_data->_page->editorState().isContentEditable && !_data->_page->editorState().selectionIsNone); 838 839 // Next, handle editor commands. Start by returning YES for anything that is not an editor command. 840 // Returning YES is the default thing to do in an AppKit validate method for any selector that is not recognized. 841 String commandName = commandNameForSelector([item action]); 842 if (!Editor::commandIsSupportedFromMenuOrKeyBinding(commandName)) 843 return YES; 844 845 // Add this item to the vector of items for a given command that are awaiting validation. 846 ValidationMap::AddResult addResult = _data->_validationMap.add(commandName, ValidationVector()); 847 addResult.iterator->value.append(item); 848 if (addResult.isNewEntry) { 849 // If we are not already awaiting validation for this command, start the asynchronous validation process. 850 // FIXME: Theoretically, there is a race here; when we get the answer it might be old, from a previous time 851 // we asked for the same command; there is no guarantee the answer is still valid. 852 _data->_page->validateCommand(commandName, [self](const String& commandName, bool isEnabled, int32_t state, CallbackBase::Error error) { 853 // If the process exits before the command can be validated, we'll be called back with an error. 854 if (error != CallbackBase::Error::None) 855 return; 856 857 [self _setUserInterfaceItemState:commandName enabled:isEnabled state:state]; 858 }); 859 } 860 861 // Treat as enabled until we get the result back from the web process and _setUserInterfaceItemState is called. 862 // FIXME <rdar://problem/8803459>: This means disabled items will flash enabled at first for a moment. 863 // But returning NO here would be worse; that would make keyboard commands such as command-C fail. 864 return YES; 865} 866 867- (IBAction)startSpeaking:(id)sender 868{ 869 _data->_page->getSelectionOrContentsAsString([self](const String& string, CallbackBase::Error error) { 870 if (error != CallbackBase::Error::None) 871 return; 872 if (!string) 873 return; 874 875 [NSApp speakString:string]; 876 }); 877} 878 879- (IBAction)stopSpeaking:(id)sender 880{ 881 [NSApp stopSpeaking:sender]; 882} 883 884- (IBAction)showGuessPanel:(id)sender 885{ 886 NSSpellChecker *checker = [NSSpellChecker sharedSpellChecker]; 887 if (!checker) { 888 LOG_ERROR("No NSSpellChecker"); 889 return; 890 } 891 892 NSPanel *spellingPanel = [checker spellingPanel]; 893 if ([spellingPanel isVisible]) { 894 [spellingPanel orderOut:sender]; 895 return; 896 } 897 898 _data->_page->advanceToNextMisspelling(true); 899 [spellingPanel orderFront:sender]; 900} 901 902- (IBAction)checkSpelling:(id)sender 903{ 904 _data->_page->advanceToNextMisspelling(false); 905} 906 907- (void)changeSpelling:(id)sender 908{ 909 NSString *word = [[sender selectedCell] stringValue]; 910 911 _data->_page->changeSpellingToWord(word); 912} 913 914- (IBAction)toggleContinuousSpellChecking:(id)sender 915{ 916 bool spellCheckingEnabled = !TextChecker::state().isContinuousSpellCheckingEnabled; 917 TextChecker::setContinuousSpellCheckingEnabled(spellCheckingEnabled); 918 919 _data->_page->process().updateTextCheckerState(); 920} 921 922- (BOOL)isGrammarCheckingEnabled 923{ 924 return TextChecker::state().isGrammarCheckingEnabled; 925} 926 927- (void)setGrammarCheckingEnabled:(BOOL)flag 928{ 929 if (static_cast<bool>(flag) == TextChecker::state().isGrammarCheckingEnabled) 930 return; 931 932 TextChecker::setGrammarCheckingEnabled(flag); 933 _data->_page->process().updateTextCheckerState(); 934} 935 936- (IBAction)toggleGrammarChecking:(id)sender 937{ 938 bool grammarCheckingEnabled = !TextChecker::state().isGrammarCheckingEnabled; 939 TextChecker::setGrammarCheckingEnabled(grammarCheckingEnabled); 940 941 _data->_page->process().updateTextCheckerState(); 942} 943 944- (IBAction)toggleAutomaticSpellingCorrection:(id)sender 945{ 946 TextChecker::setAutomaticSpellingCorrectionEnabled(!TextChecker::state().isAutomaticSpellingCorrectionEnabled); 947 948 _data->_page->process().updateTextCheckerState(); 949} 950 951- (void)orderFrontSubstitutionsPanel:(id)sender 952{ 953 NSSpellChecker *checker = [NSSpellChecker sharedSpellChecker]; 954 if (!checker) { 955 LOG_ERROR("No NSSpellChecker"); 956 return; 957 } 958 959 NSPanel *substitutionsPanel = [checker substitutionsPanel]; 960 if ([substitutionsPanel isVisible]) { 961 [substitutionsPanel orderOut:sender]; 962 return; 963 } 964 [substitutionsPanel orderFront:sender]; 965} 966 967- (IBAction)toggleSmartInsertDelete:(id)sender 968{ 969 _data->_page->setSmartInsertDeleteEnabled(!_data->_page->isSmartInsertDeleteEnabled()); 970} 971 972- (BOOL)isAutomaticQuoteSubstitutionEnabled 973{ 974 return TextChecker::state().isAutomaticQuoteSubstitutionEnabled; 975} 976 977- (void)setAutomaticQuoteSubstitutionEnabled:(BOOL)flag 978{ 979 if (static_cast<bool>(flag) == TextChecker::state().isAutomaticQuoteSubstitutionEnabled) 980 return; 981 982 TextChecker::setAutomaticQuoteSubstitutionEnabled(flag); 983 _data->_page->process().updateTextCheckerState(); 984} 985 986- (void)toggleAutomaticQuoteSubstitution:(id)sender 987{ 988 TextChecker::setAutomaticQuoteSubstitutionEnabled(!TextChecker::state().isAutomaticQuoteSubstitutionEnabled); 989 _data->_page->process().updateTextCheckerState(); 990} 991 992- (BOOL)isAutomaticDashSubstitutionEnabled 993{ 994 return TextChecker::state().isAutomaticDashSubstitutionEnabled; 995} 996 997- (void)setAutomaticDashSubstitutionEnabled:(BOOL)flag 998{ 999 if (static_cast<bool>(flag) == TextChecker::state().isAutomaticDashSubstitutionEnabled) 1000 return; 1001 1002 TextChecker::setAutomaticDashSubstitutionEnabled(flag); 1003 _data->_page->process().updateTextCheckerState(); 1004} 1005 1006- (void)toggleAutomaticDashSubstitution:(id)sender 1007{ 1008 TextChecker::setAutomaticDashSubstitutionEnabled(!TextChecker::state().isAutomaticDashSubstitutionEnabled); 1009 _data->_page->process().updateTextCheckerState(); 1010} 1011 1012- (BOOL)isAutomaticLinkDetectionEnabled 1013{ 1014 return TextChecker::state().isAutomaticLinkDetectionEnabled; 1015} 1016 1017- (void)setAutomaticLinkDetectionEnabled:(BOOL)flag 1018{ 1019 if (static_cast<bool>(flag) == TextChecker::state().isAutomaticLinkDetectionEnabled) 1020 return; 1021 1022 TextChecker::setAutomaticLinkDetectionEnabled(flag); 1023 _data->_page->process().updateTextCheckerState(); 1024} 1025 1026- (void)toggleAutomaticLinkDetection:(id)sender 1027{ 1028 TextChecker::setAutomaticLinkDetectionEnabled(!TextChecker::state().isAutomaticLinkDetectionEnabled); 1029 _data->_page->process().updateTextCheckerState(); 1030} 1031 1032- (BOOL)isAutomaticTextReplacementEnabled 1033{ 1034 return TextChecker::state().isAutomaticTextReplacementEnabled; 1035} 1036 1037- (void)setAutomaticTextReplacementEnabled:(BOOL)flag 1038{ 1039 if (static_cast<bool>(flag) == TextChecker::state().isAutomaticTextReplacementEnabled) 1040 return; 1041 1042 TextChecker::setAutomaticTextReplacementEnabled(flag); 1043 _data->_page->process().updateTextCheckerState(); 1044} 1045 1046- (void)toggleAutomaticTextReplacement:(id)sender 1047{ 1048 TextChecker::setAutomaticTextReplacementEnabled(!TextChecker::state().isAutomaticTextReplacementEnabled); 1049 _data->_page->process().updateTextCheckerState(); 1050} 1051 1052- (void)uppercaseWord:(id)sender 1053{ 1054 _data->_page->uppercaseWord(); 1055} 1056 1057- (void)lowercaseWord:(id)sender 1058{ 1059 _data->_page->lowercaseWord(); 1060} 1061 1062- (void)capitalizeWord:(id)sender 1063{ 1064 _data->_page->capitalizeWord(); 1065} 1066 1067- (void)displayIfNeeded 1068{ 1069 // FIXME: We should remove this code when <rdar://problem/9362085> is resolved. In the meantime, 1070 // it is necessary to disable scren updates so we get a chance to redraw the corners before this 1071 // display is visible. 1072 NSWindow *window = [self window]; 1073 BOOL shouldMaskWindow = window && !NSIsEmptyRect(_data->_windowBottomCornerIntersectionRect); 1074 if (shouldMaskWindow) 1075 NSDisableScreenUpdates(); 1076 1077 [super displayIfNeeded]; 1078 1079 if (shouldMaskWindow) { 1080 [window _maskRoundedBottomCorners:_data->_windowBottomCornerIntersectionRect]; 1081 NSEnableScreenUpdates(); 1082 _data->_windowBottomCornerIntersectionRect = NSZeroRect; 1083 } 1084} 1085 1086// Events 1087 1088-(BOOL)shouldIgnoreMouseEvents 1089{ 1090 // FIXME: This check is surprisingly specific. Are there any other cases where we need to block mouse events? 1091 // Do we actually need to in thumbnail view? And if we do, what about non-mouse events? 1092#if WK_API_ENABLED 1093 if (_data->_thumbnailView) 1094 return YES; 1095#endif 1096 return NO; 1097} 1098 1099// Override this so that AppKit will send us arrow keys as key down events so we can 1100// support them via the key bindings mechanism. 1101- (BOOL)_wantsKeyDownForEvent:(NSEvent *)event 1102{ 1103 return YES; 1104} 1105 1106- (void)_setMouseDownEvent:(NSEvent *)event 1107{ 1108 ASSERT(!event || [event type] == NSLeftMouseDown || [event type] == NSRightMouseDown || [event type] == NSOtherMouseDown); 1109 1110 if (event == _data->_mouseDownEvent) 1111 return; 1112 1113 [_data->_mouseDownEvent release]; 1114 _data->_mouseDownEvent = [event retain]; 1115} 1116 1117#if USE(ASYNC_NSTEXTINPUTCLIENT) 1118#define NATIVE_MOUSE_EVENT_HANDLER(Selector) \ 1119 - (void)Selector:(NSEvent *)theEvent \ 1120 { \ 1121 if ([self shouldIgnoreMouseEvents]) \ 1122 return; \ 1123 if (NSTextInputContext *context = [self inputContext]) { \ 1124 [context handleEvent:theEvent completionHandler:^(BOOL handled) { \ 1125 if (handled) \ 1126 LOG(TextInput, "%s was handled by text input context", String(#Selector).substring(0, String(#Selector).find("Internal")).ascii().data()); \ 1127 else { \ 1128 NativeWebMouseEvent webEvent(theEvent, self); \ 1129 _data->_page->handleMouseEvent(webEvent); \ 1130 } \ 1131 }]; \ 1132 return; \ 1133 } \ 1134 NativeWebMouseEvent webEvent(theEvent, self); \ 1135 _data->_page->handleMouseEvent(webEvent); \ 1136 } 1137#else 1138#define NATIVE_MOUSE_EVENT_HANDLER(Selector) \ 1139 - (void)Selector:(NSEvent *)theEvent \ 1140 { \ 1141 if ([self shouldIgnoreMouseEvents]) \ 1142 return; \ 1143 if ([[self inputContext] handleEvent:theEvent]) { \ 1144 LOG(TextInput, "%s was handled by text input context", String(#Selector).substring(0, String(#Selector).find("Internal")).ascii().data()); \ 1145 return; \ 1146 } \ 1147 NativeWebMouseEvent webEvent(theEvent, self); \ 1148 _data->_page->handleMouseEvent(webEvent); \ 1149 } 1150#endif 1151 1152NATIVE_MOUSE_EVENT_HANDLER(mouseEntered) 1153NATIVE_MOUSE_EVENT_HANDLER(mouseExited) 1154NATIVE_MOUSE_EVENT_HANDLER(mouseMovedInternal) 1155NATIVE_MOUSE_EVENT_HANDLER(mouseDownInternal) 1156NATIVE_MOUSE_EVENT_HANDLER(mouseUpInternal) 1157NATIVE_MOUSE_EVENT_HANDLER(mouseDraggedInternal) 1158NATIVE_MOUSE_EVENT_HANDLER(otherMouseDown) 1159NATIVE_MOUSE_EVENT_HANDLER(otherMouseDragged) 1160NATIVE_MOUSE_EVENT_HANDLER(otherMouseMoved) 1161NATIVE_MOUSE_EVENT_HANDLER(otherMouseUp) 1162NATIVE_MOUSE_EVENT_HANDLER(rightMouseDown) 1163NATIVE_MOUSE_EVENT_HANDLER(rightMouseDragged) 1164NATIVE_MOUSE_EVENT_HANDLER(rightMouseUp) 1165 1166#undef NATIVE_MOUSE_EVENT_HANDLER 1167 1168- (void)_ensureGestureController 1169{ 1170 if (_data->_gestureController) 1171 return; 1172 1173 _data->_gestureController = std::make_unique<ViewGestureController>(*_data->_page); 1174} 1175 1176- (void)scrollWheel:(NSEvent *)event 1177{ 1178 if ([self shouldIgnoreMouseEvents]) 1179 return; 1180 1181 if (_data->_allowsBackForwardNavigationGestures) { 1182 [self _ensureGestureController]; 1183 if (_data->_gestureController->handleScrollWheelEvent(event)) 1184 return; 1185 } 1186 1187 NativeWebWheelEvent webEvent = NativeWebWheelEvent(event, self); 1188 _data->_page->handleWheelEvent(webEvent); 1189} 1190 1191- (void)swipeWithEvent:(NSEvent *)event 1192{ 1193 if ([self shouldIgnoreMouseEvents]) 1194 return; 1195 1196 if (!_data->_allowsBackForwardNavigationGestures) { 1197 [super swipeWithEvent:event]; 1198 return; 1199 } 1200 1201 if (event.deltaX > 0.0) 1202 _data->_page->goBack(); 1203 else if (event.deltaX < 0.0) 1204 _data->_page->goForward(); 1205 else 1206 [super swipeWithEvent:event]; 1207} 1208 1209- (void)mouseMoved:(NSEvent *)event 1210{ 1211 if ([self shouldIgnoreMouseEvents]) 1212 return; 1213 1214 // When a view is first responder, it gets mouse moved events even when the mouse is outside its visible rect. 1215 if (self == [[self window] firstResponder] && !NSPointInRect([self convertPoint:[event locationInWindow] fromView:nil], [self visibleRect])) 1216 return; 1217 1218 [self mouseMovedInternal:event]; 1219} 1220 1221- (void)mouseDown:(NSEvent *)event 1222{ 1223 if ([self shouldIgnoreMouseEvents]) 1224 return; 1225 1226 [self _setMouseDownEvent:event]; 1227 _data->_ignoringMouseDraggedEvents = NO; 1228 [self mouseDownInternal:event]; 1229} 1230 1231- (void)mouseUp:(NSEvent *)event 1232{ 1233 if ([self shouldIgnoreMouseEvents]) 1234 return; 1235 1236 [self _setMouseDownEvent:nil]; 1237 [self mouseUpInternal:event]; 1238} 1239 1240- (void)mouseDragged:(NSEvent *)event 1241{ 1242 if ([self shouldIgnoreMouseEvents]) 1243 return; 1244 1245 if (_data->_ignoringMouseDraggedEvents) 1246 return; 1247 [self mouseDraggedInternal:event]; 1248} 1249 1250- (BOOL)acceptsFirstMouse:(NSEvent *)event 1251{ 1252 // There's a chance that responding to this event will run a nested event loop, and 1253 // fetching a new event might release the old one. Retaining and then autoreleasing 1254 // the current event prevents that from causing a problem inside WebKit or AppKit code. 1255 [[event retain] autorelease]; 1256 1257 if (![self hitTest:[event locationInWindow]]) 1258 return NO; 1259 1260 [self _setMouseDownEvent:event]; 1261 bool result = _data->_page->acceptsFirstMouse([event eventNumber], WebEventFactory::createWebMouseEvent(event, self)); 1262 [self _setMouseDownEvent:nil]; 1263 return result; 1264} 1265 1266- (BOOL)shouldDelayWindowOrderingForEvent:(NSEvent *)event 1267{ 1268 // If this is the active window or we don't have a range selection, there is no need to perform additional checks 1269 // and we can avoid making a synchronous call to the WebProcess. 1270 if ([[self window] isKeyWindow] || _data->_page->editorState().selectionIsNone || !_data->_page->editorState().selectionIsRange) 1271 return NO; 1272 1273 // There's a chance that responding to this event will run a nested event loop, and 1274 // fetching a new event might release the old one. Retaining and then autoreleasing 1275 // the current event prevents that from causing a problem inside WebKit or AppKit code. 1276 [[event retain] autorelease]; 1277 1278 if (![self hitTest:[event locationInWindow]]) 1279 return NO; 1280 1281 [self _setMouseDownEvent:event]; 1282 bool result = _data->_page->shouldDelayWindowOrderingForEvent(WebEventFactory::createWebMouseEvent(event, self)); 1283 [self _setMouseDownEvent:nil]; 1284 return result; 1285} 1286 1287- (void)_disableComplexTextInputIfNecessary 1288{ 1289 if (!_data->_pluginComplexTextInputIdentifier) 1290 return; 1291 1292 if (_data->_pluginComplexTextInputState != PluginComplexTextInputEnabled) 1293 return; 1294 1295 // Check if the text input window has been dismissed. 1296 if (![[WKTextInputWindowController sharedTextInputWindowController] hasMarkedText]) 1297 [self _setPluginComplexTextInputState:PluginComplexTextInputDisabled]; 1298} 1299 1300- (BOOL)_handlePluginComplexTextInputKeyDown:(NSEvent *)event 1301{ 1302 ASSERT(_data->_pluginComplexTextInputIdentifier); 1303 ASSERT(_data->_pluginComplexTextInputState != PluginComplexTextInputDisabled); 1304 1305 BOOL usingLegacyCocoaTextInput = _data->_pluginComplexTextInputState == PluginComplexTextInputEnabledLegacy; 1306 1307 NSString *string = nil; 1308 BOOL didHandleEvent = [[WKTextInputWindowController sharedTextInputWindowController] interpretKeyEvent:event usingLegacyCocoaTextInput:usingLegacyCocoaTextInput string:&string]; 1309 1310 if (string) { 1311 _data->_page->sendComplexTextInputToPlugin(_data->_pluginComplexTextInputIdentifier, string); 1312 1313 if (!usingLegacyCocoaTextInput) 1314 _data->_pluginComplexTextInputState = PluginComplexTextInputDisabled; 1315 } 1316 1317 return didHandleEvent; 1318} 1319 1320- (BOOL)_tryHandlePluginComplexTextInputKeyDown:(NSEvent *)event 1321{ 1322 if (!_data->_pluginComplexTextInputIdentifier || _data->_pluginComplexTextInputState == PluginComplexTextInputDisabled) 1323 return NO; 1324 1325 // Check if the text input window has been dismissed and let the plug-in process know. 1326 // This is only valid with the updated Cocoa text input spec. 1327 [self _disableComplexTextInputIfNecessary]; 1328 1329 // Try feeding the keyboard event directly to the plug-in. 1330 if (_data->_pluginComplexTextInputState == PluginComplexTextInputEnabledLegacy) 1331 return [self _handlePluginComplexTextInputKeyDown:event]; 1332 1333 return NO; 1334} 1335 1336static void extractUnderlines(NSAttributedString *string, Vector<CompositionUnderline>& result) 1337{ 1338 int length = [[string string] length]; 1339 1340 int i = 0; 1341 while (i < length) { 1342 NSRange range; 1343 NSDictionary *attrs = [string attributesAtIndex:i longestEffectiveRange:&range inRange:NSMakeRange(i, length - i)]; 1344 1345 if (NSNumber *style = [attrs objectForKey:NSUnderlineStyleAttributeName]) { 1346 Color color = Color::black; 1347 if (NSColor *colorAttr = [attrs objectForKey:NSUnderlineColorAttributeName]) 1348 color = colorFromNSColor([colorAttr colorUsingColorSpaceName:NSDeviceRGBColorSpace]); 1349 result.append(CompositionUnderline(range.location, NSMaxRange(range), color, [style intValue] > 1)); 1350 } 1351 1352 i = range.location + range.length; 1353 } 1354} 1355 1356#if USE(ASYNC_NSTEXTINPUTCLIENT) 1357 1358- (void)_collectKeyboardLayoutCommandsForEvent:(NSEvent *)event to:(Vector<KeypressCommand>&)commands 1359{ 1360 if ([event type] != NSKeyDown) 1361 return; 1362 1363 ASSERT(!_data->_collectedKeypressCommands); 1364 _data->_collectedKeypressCommands = &commands; 1365 1366 if (NSTextInputContext *context = [self inputContext]) 1367 [context handleEventByKeyboardLayout:event]; 1368 else 1369 [self interpretKeyEvents:[NSArray arrayWithObject:event]]; 1370 1371 _data->_collectedKeypressCommands = nullptr; 1372} 1373 1374- (void)_interpretKeyEvent:(NSEvent *)event completionHandler:(void(^)(BOOL handled, const Vector<KeypressCommand>& commands))completionHandler 1375{ 1376 // For regular Web content, input methods run before passing a keydown to DOM, but plug-ins get an opportunity to handle the event first. 1377 // There is no need to collect commands, as the plug-in cannot execute them. 1378 if (_data->_pluginComplexTextInputIdentifier) { 1379 completionHandler(NO, Vector<KeypressCommand>()); 1380 return; 1381 } 1382 1383 if (![self inputContext]) { 1384 Vector<KeypressCommand> commands; 1385 [self _collectKeyboardLayoutCommandsForEvent:event to:commands]; 1386 completionHandler(NO, commands); 1387 return; 1388 } 1389 1390 LOG(TextInput, "-> handleEventByInputMethod:%p %@", event, event); 1391 [[self inputContext] handleEventByInputMethod:event completionHandler:^(BOOL handled) { 1392 1393 LOG(TextInput, "... handleEventByInputMethod%s handled", handled ? "" : " not"); 1394 if (handled) { 1395 completionHandler(YES, Vector<KeypressCommand>()); 1396 return; 1397 } 1398 1399 Vector<KeypressCommand> commands; 1400 [self _collectKeyboardLayoutCommandsForEvent:event to:commands]; 1401 completionHandler(NO, commands); 1402 }]; 1403} 1404 1405- (void)doCommandBySelector:(SEL)selector 1406{ 1407 LOG(TextInput, "doCommandBySelector:\"%s\"", sel_getName(selector)); 1408 1409 Vector<KeypressCommand>* keypressCommands = _data->_collectedKeypressCommands; 1410 1411 if (keypressCommands) { 1412 KeypressCommand command(NSStringFromSelector(selector)); 1413 keypressCommands->append(command); 1414 LOG(TextInput, "...stored"); 1415 _data->_page->registerKeypressCommandName(command.commandName); 1416 } else { 1417 // FIXME: Send the command to Editor synchronously and only send it along the 1418 // responder chain if it's a selector that does not correspond to an editing command. 1419 [super doCommandBySelector:selector]; 1420 } 1421} 1422 1423- (void)insertText:(id)string 1424{ 1425 // Unlike an NSTextInputClient variant with replacementRange, this NSResponder method is called when there is no input context, 1426 // so text input processing isn't performed. We are not going to actually insert any text in that case, but saving an insertText 1427 // command ensures that a keypress event is dispatched as appropriate. 1428 [self insertText:string replacementRange:NSMakeRange(NSNotFound, 0)]; 1429} 1430 1431- (void)insertText:(id)string replacementRange:(NSRange)replacementRange 1432{ 1433 BOOL isAttributedString = [string isKindOfClass:[NSAttributedString class]]; 1434 ASSERT(isAttributedString || [string isKindOfClass:[NSString class]]); 1435 1436 if (replacementRange.location != NSNotFound) 1437 LOG(TextInput, "insertText:\"%@\" replacementRange:(%u, %u)", isAttributedString ? [string string] : string, replacementRange.location, replacementRange.length); 1438 else 1439 LOG(TextInput, "insertText:\"%@\"", isAttributedString ? [string string] : string); 1440 1441 NSString *text; 1442 Vector<TextAlternativeWithRange> dictationAlternatives; 1443 1444 bool registerUndoGroup = false; 1445 if (isAttributedString) { 1446#if USE(DICTATION_ALTERNATIVES) 1447 collectDictationTextAlternatives(string, dictationAlternatives); 1448#endif 1449#if USE(INSERTION_UNDO_GROUPING) 1450 registerUndoGroup = shouldRegisterInsertionUndoGroup(string); 1451#endif 1452 // FIXME: We ignore most attributes from the string, so for example inserting from Character Palette loses font and glyph variation data. 1453 text = [string string]; 1454 } else 1455 text = string; 1456 1457 // insertText can be called for several reasons: 1458 // - If it's from normal key event processing (including key bindings), we save the action to perform it later. 1459 // - If it's from an input method, then we should go ahead and insert the text now. 1460 // - If it's sent outside of keyboard event processing (e.g. from Character Viewer, or when confirming an inline input area with a mouse), 1461 // then we also execute it immediately, as there will be no other chance. 1462 Vector<KeypressCommand>* keypressCommands = _data->_collectedKeypressCommands; 1463 if (keypressCommands) { 1464 ASSERT(replacementRange.location == NSNotFound); 1465 KeypressCommand command("insertText:", text); 1466 keypressCommands->append(command); 1467 LOG(TextInput, "...stored"); 1468 _data->_page->registerKeypressCommandName(command.commandName); 1469 return; 1470 } 1471 1472 String eventText = text; 1473 eventText.replace(NSBackTabCharacter, NSTabCharacter); // same thing is done in KeyEventMac.mm in WebCore 1474 if (!dictationAlternatives.isEmpty()) 1475 _data->_page->insertDictatedTextAsync(eventText, replacementRange, dictationAlternatives, registerUndoGroup); 1476 else 1477 _data->_page->insertTextAsync(eventText, replacementRange, registerUndoGroup); 1478} 1479 1480- (void)selectedRangeWithCompletionHandler:(void(^)(NSRange selectedRange))completionHandlerPtr 1481{ 1482 RetainPtr<id> completionHandler = adoptNS([completionHandlerPtr copy]); 1483 1484 LOG(TextInput, "selectedRange"); 1485 _data->_page->getSelectedRangeAsync([completionHandler](const EditingRange& editingRangeResult, CallbackBase::Error error) { 1486 void (^completionHandlerBlock)(NSRange) = (void (^)(NSRange))completionHandler.get(); 1487 if (error != CallbackBase::Error::None) { 1488 LOG(TextInput, " ...selectedRange failed."); 1489 completionHandlerBlock(NSMakeRange(NSNotFound, 0)); 1490 return; 1491 } 1492 NSRange result = editingRangeResult; 1493 if (result.location == NSNotFound) 1494 LOG(TextInput, " -> selectedRange returned (NSNotFound, %llu)", result.length); 1495 else 1496 LOG(TextInput, " -> selectedRange returned (%llu, %llu)", result.location, result.length); 1497 completionHandlerBlock(result); 1498 }); 1499} 1500 1501- (void)markedRangeWithCompletionHandler:(void(^)(NSRange markedRange))completionHandlerPtr 1502{ 1503 RetainPtr<id> completionHandler = adoptNS([completionHandlerPtr copy]); 1504 1505 LOG(TextInput, "markedRange"); 1506 _data->_page->getMarkedRangeAsync([completionHandler](const EditingRange& editingRangeResult, CallbackBase::Error error) { 1507 void (^completionHandlerBlock)(NSRange) = (void (^)(NSRange))completionHandler.get(); 1508 if (error != CallbackBase::Error::None) { 1509 LOG(TextInput, " ...markedRange failed."); 1510 completionHandlerBlock(NSMakeRange(NSNotFound, 0)); 1511 return; 1512 } 1513 NSRange result = editingRangeResult; 1514 if (result.location == NSNotFound) 1515 LOG(TextInput, " -> markedRange returned (NSNotFound, %llu)", result.length); 1516 else 1517 LOG(TextInput, " -> markedRange returned (%llu, %llu)", result.location, result.length); 1518 completionHandlerBlock(result); 1519 }); 1520} 1521 1522- (void)hasMarkedTextWithCompletionHandler:(void(^)(BOOL hasMarkedText))completionHandlerPtr 1523{ 1524 RetainPtr<id> completionHandler = adoptNS([completionHandlerPtr copy]); 1525 1526 LOG(TextInput, "hasMarkedText"); 1527 _data->_page->getMarkedRangeAsync([completionHandler](const EditingRange& editingRangeResult, CallbackBase::Error error) { 1528 void (^completionHandlerBlock)(BOOL) = (void (^)(BOOL))completionHandler.get(); 1529 if (error != CallbackBase::Error::None) { 1530 LOG(TextInput, " ...hasMarkedText failed."); 1531 completionHandlerBlock(NO); 1532 return; 1533 } 1534 BOOL hasMarkedText = editingRangeResult.location != notFound; 1535 LOG(TextInput, " -> hasMarkedText returned %u", hasMarkedText); 1536 completionHandlerBlock(hasMarkedText); 1537 }); 1538} 1539 1540- (void)attributedSubstringForProposedRange:(NSRange)nsRange completionHandler:(void(^)(NSAttributedString *attrString, NSRange actualRange))completionHandlerPtr 1541{ 1542 RetainPtr<id> completionHandler = adoptNS([completionHandlerPtr copy]); 1543 1544 LOG(TextInput, "attributedSubstringFromRange:(%u, %u)", nsRange.location, nsRange.length); 1545 _data->_page->attributedSubstringForCharacterRangeAsync(nsRange, [completionHandler](const AttributedString& string, const EditingRange& actualRange, CallbackBase::Error error) { 1546 void (^completionHandlerBlock)(NSAttributedString *, NSRange) = (void (^)(NSAttributedString *, NSRange))completionHandler.get(); 1547 if (error != CallbackBase::Error::None) { 1548 LOG(TextInput, " ...attributedSubstringFromRange failed."); 1549 completionHandlerBlock(0, NSMakeRange(NSNotFound, 0)); 1550 return; 1551 } 1552 LOG(TextInput, " -> attributedSubstringFromRange returned %@", [string.string.get() string]); 1553 completionHandlerBlock([[string.string.get() retain] autorelease], actualRange); 1554 }); 1555} 1556 1557- (void)firstRectForCharacterRange:(NSRange)theRange completionHandler:(void(^)(NSRect firstRect, NSRange actualRange))completionHandlerPtr 1558{ 1559 RetainPtr<id> completionHandler = adoptNS([completionHandlerPtr copy]); 1560 1561 LOG(TextInput, "firstRectForCharacterRange:(%u, %u)", theRange.location, theRange.length); 1562 1563 // Just to match NSTextView's behavior. Regression tests cannot detect this; 1564 // to reproduce, use a test application from http://bugs.webkit.org/show_bug.cgi?id=4682 1565 // (type something; try ranges (1, -1) and (2, -1). 1566 if ((theRange.location + theRange.length < theRange.location) && (theRange.location + theRange.length != 0)) 1567 theRange.length = 0; 1568 1569 if (theRange.location == NSNotFound) { 1570 LOG(TextInput, " -> NSZeroRect"); 1571 completionHandlerPtr(NSZeroRect, theRange); 1572 return; 1573 } 1574 1575 _data->_page->firstRectForCharacterRangeAsync(theRange, [self, completionHandler](const IntRect& rect, const EditingRange& actualRange, CallbackBase::Error error) { 1576 void (^completionHandlerBlock)(NSRect, NSRange) = (void (^)(NSRect, NSRange))completionHandler.get(); 1577 if (error != CallbackBase::Error::None) { 1578 LOG(TextInput, " ...firstRectForCharacterRange failed."); 1579 completionHandlerBlock(NSZeroRect, NSMakeRange(NSNotFound, 0)); 1580 return; 1581 } 1582 1583 NSRect resultRect = [self convertRect:rect toView:nil]; 1584 resultRect = [self.window convertRectToScreen:resultRect]; 1585 1586 LOG(TextInput, " -> firstRectForCharacterRange returned (%f, %f, %f, %f)", resultRect.origin.x, resultRect.origin.y, resultRect.size.width, resultRect.size.height); 1587 completionHandlerBlock(resultRect, actualRange); 1588 }); 1589} 1590 1591- (void)characterIndexForPoint:(NSPoint)thePoint completionHandler:(void(^)(NSUInteger))completionHandlerPtr 1592{ 1593 RetainPtr<id> completionHandler = adoptNS([completionHandlerPtr copy]); 1594 1595 LOG(TextInput, "characterIndexForPoint:(%f, %f)", thePoint.x, thePoint.y); 1596 1597 NSWindow *window = [self window]; 1598 1599#pragma clang diagnostic push 1600#pragma clang diagnostic ignored "-Wdeprecated-declarations" 1601 if (window) 1602 thePoint = [window convertScreenToBase:thePoint]; 1603#pragma clang diagnostic pop 1604 thePoint = [self convertPoint:thePoint fromView:nil]; // the point is relative to the main frame 1605 1606 _data->_page->characterIndexForPointAsync(IntPoint(thePoint), [completionHandler](uint64_t result, CallbackBase::Error error) { 1607 void (^completionHandlerBlock)(NSUInteger) = (void (^)(NSUInteger))completionHandler.get(); 1608 if (error != CallbackBase::Error::None) { 1609 LOG(TextInput, " ...characterIndexForPoint failed."); 1610 completionHandlerBlock(0); 1611 return; 1612 } 1613 if (result == notFound) 1614 result = NSNotFound; 1615 LOG(TextInput, " -> characterIndexForPoint returned %lu", result); 1616 completionHandlerBlock(result); 1617 }); 1618} 1619 1620- (NSTextInputContext *)inputContext 1621{ 1622 if (_data->_pluginComplexTextInputIdentifier) { 1623 ASSERT(!_data->_collectedKeypressCommands); // Should not get here from -_interpretKeyEvent:completionHandler:, we only use WKTextInputWindowController after giving the plug-in a chance to handle keydown natively. 1624 return [[WKTextInputWindowController sharedTextInputWindowController] inputContext]; 1625 } 1626 1627 // Disable text input machinery when in non-editable content. An invisible inline input area affects performance, and can prevent Expose from working. 1628 if (!_data->_page->editorState().isContentEditable) 1629 return nil; 1630 1631 return [super inputContext]; 1632} 1633 1634- (void)unmarkText 1635{ 1636 LOG(TextInput, "unmarkText"); 1637 1638 _data->_page->confirmCompositionAsync(); 1639} 1640 1641- (void)setMarkedText:(id)string selectedRange:(NSRange)selectedRange replacementRange:(NSRange)replacementRange 1642{ 1643 BOOL isAttributedString = [string isKindOfClass:[NSAttributedString class]]; 1644 ASSERT(isAttributedString || [string isKindOfClass:[NSString class]]); 1645 1646 LOG(TextInput, "setMarkedText:\"%@\" selectedRange:(%u, %u) replacementRange:(%u, %u)", isAttributedString ? [string string] : string, selectedRange.location, selectedRange.length, replacementRange.location, replacementRange.length); 1647 1648 Vector<CompositionUnderline> underlines; 1649 NSString *text; 1650 1651 if (isAttributedString) { 1652 // FIXME: We ignore most attributes from the string, so an input method cannot specify e.g. a font or a glyph variation. 1653 text = [string string]; 1654 extractUnderlines(string, underlines); 1655 } else 1656 text = string; 1657 1658 if (_data->_inSecureInputState) { 1659 // In password fields, we only allow ASCII dead keys, and don't allow inline input, matching NSSecureTextInputField. 1660 // Allowing ASCII dead keys is necessary to enable full Roman input when using a Vietnamese keyboard. 1661 ASSERT(!_data->_page->editorState().hasComposition); 1662 [self _notifyInputContextAboutDiscardedComposition]; 1663 // FIXME: We should store the command to handle it after DOM event processing, as it's regular keyboard input now, not a composition. 1664 if ([text length] == 1 && isASCII([text characterAtIndex:0])) 1665 _data->_page->insertTextAsync(text, replacementRange); 1666 else 1667 NSBeep(); 1668 return; 1669 } 1670 1671 _data->_page->setCompositionAsync(text, underlines, selectedRange, replacementRange); 1672} 1673 1674// Synchronous NSTextInputClient is still implemented to catch spurious sync calls. Remove when that is no longer needed. 1675 1676- (NSRange)selectedRange NO_RETURN_DUE_TO_ASSERT 1677{ 1678 ASSERT_NOT_REACHED(); 1679 return NSMakeRange(NSNotFound, 0); 1680} 1681 1682- (BOOL)hasMarkedText NO_RETURN_DUE_TO_ASSERT 1683{ 1684 ASSERT_NOT_REACHED(); 1685 return NO; 1686} 1687 1688- (NSRange)markedRange NO_RETURN_DUE_TO_ASSERT 1689{ 1690 ASSERT_NOT_REACHED(); 1691 return NSMakeRange(NSNotFound, 0); 1692} 1693 1694- (NSAttributedString *)attributedSubstringForProposedRange:(NSRange)nsRange actualRange:(NSRangePointer)actualRange NO_RETURN_DUE_TO_ASSERT 1695{ 1696 ASSERT_NOT_REACHED(); 1697 return nil; 1698} 1699 1700- (NSUInteger)characterIndexForPoint:(NSPoint)thePoint NO_RETURN_DUE_TO_ASSERT 1701{ 1702 ASSERT_NOT_REACHED(); 1703 return 0; 1704} 1705 1706- (NSRect)firstRectForCharacterRange:(NSRange)theRange actualRange:(NSRangePointer)actualRange NO_RETURN_DUE_TO_ASSERT 1707{ 1708 ASSERT_NOT_REACHED(); 1709 return NSMakeRect(0, 0, 0, 0); 1710} 1711 1712- (BOOL)performKeyEquivalent:(NSEvent *)event 1713{ 1714 // There's a chance that responding to this event will run a nested event loop, and 1715 // fetching a new event might release the old one. Retaining and then autoreleasing 1716 // the current event prevents that from causing a problem inside WebKit or AppKit code. 1717 [[event retain] autorelease]; 1718 1719 // We get Esc key here after processing either Esc or Cmd+period. The former starts as a keyDown, and the latter starts as a key equivalent, 1720 // but both get transformed to a cancelOperation: command, executing which passes an Esc key event to -performKeyEquivalent:. 1721 // Don't interpret this event again, avoiding re-entrancy and infinite loops. 1722 if ([[event charactersIgnoringModifiers] isEqualToString:@"\e"] && !([event modifierFlags] & NSDeviceIndependentModifierFlagsMask)) 1723 return [super performKeyEquivalent:event]; 1724 1725 if (_data->_keyDownEventBeingResent) { 1726 // WebCore has already seen the event, no need for custom processing. 1727 // Note that we can get multiple events for each event being re-sent. For example, for Cmd+'=' AppKit 1728 // first performs the original key equivalent, and if that isn't handled, it dispatches a synthetic Cmd+'+'. 1729 return [super performKeyEquivalent:event]; 1730 } 1731 1732 ASSERT(event == [NSApp currentEvent]); 1733 1734 [self _disableComplexTextInputIfNecessary]; 1735 1736 // Pass key combos through WebCore if there is a key binding available for 1737 // this event. This lets webpages have a crack at intercepting key-modified keypresses. 1738 // FIXME: Why is the firstResponder check needed? 1739 if (self == [[self window] firstResponder]) { 1740 [self _interpretKeyEvent:event completionHandler:^(BOOL handledByInputMethod, const Vector<KeypressCommand>& commands) { 1741 _data->_page->handleKeyboardEvent(NativeWebKeyboardEvent(event, handledByInputMethod, commands)); 1742 }]; 1743 return YES; 1744 } 1745 1746 return [super performKeyEquivalent:event]; 1747} 1748 1749- (void)keyUp:(NSEvent *)theEvent 1750{ 1751 LOG(TextInput, "keyUp:%p %@", theEvent, theEvent); 1752 1753 [self _interpretKeyEvent:theEvent completionHandler:^(BOOL handledByInputMethod, const Vector<KeypressCommand>& commands) { 1754 ASSERT(!handledByInputMethod || commands.isEmpty()); 1755 _data->_page->handleKeyboardEvent(NativeWebKeyboardEvent(theEvent, handledByInputMethod, commands)); 1756 }]; 1757} 1758 1759- (void)keyDown:(NSEvent *)theEvent 1760{ 1761 LOG(TextInput, "keyDown:%p %@%s", theEvent, theEvent, (theEvent == _data->_keyDownEventBeingResent) ? " (re-sent)" : ""); 1762 1763 if ([self _tryHandlePluginComplexTextInputKeyDown:theEvent]) { 1764 LOG(TextInput, "...handled by plug-in"); 1765 return; 1766 } 1767 1768 // We could be receiving a key down from AppKit if we have re-sent an event 1769 // that maps to an action that is currently unavailable (for example a copy when 1770 // there is no range selection). 1771 // If this is the case we should ignore the key down. 1772 if (_data->_keyDownEventBeingResent == theEvent) { 1773 [super keyDown:theEvent]; 1774 return; 1775 } 1776 1777 [self _interpretKeyEvent:theEvent completionHandler:^(BOOL handledByInputMethod, const Vector<KeypressCommand>& commands) { 1778 ASSERT(!handledByInputMethod || commands.isEmpty()); 1779 _data->_page->handleKeyboardEvent(NativeWebKeyboardEvent(theEvent, handledByInputMethod, commands)); 1780 }]; 1781} 1782 1783- (void)flagsChanged:(NSEvent *)theEvent 1784{ 1785 LOG(TextInput, "flagsChanged:%p %@", theEvent, theEvent); 1786 1787 unsigned short keyCode = [theEvent keyCode]; 1788 1789 // Don't make an event from the num lock and function keys 1790 if (!keyCode || keyCode == 10 || keyCode == 63) 1791 return; 1792 1793 [self _interpretKeyEvent:theEvent completionHandler:^(BOOL handledByInputMethod, const Vector<KeypressCommand>& commands) { 1794 _data->_page->handleKeyboardEvent(NativeWebKeyboardEvent(theEvent, handledByInputMethod, commands)); 1795 }]; 1796} 1797 1798#else // USE(ASYNC_NSTEXTINPUTCLIENT) 1799 1800- (BOOL)_interpretKeyEvent:(NSEvent *)event savingCommandsTo:(Vector<WebCore::KeypressCommand>&)commands 1801{ 1802 ASSERT(!_data->_interpretKeyEventsParameters); 1803 ASSERT(commands.isEmpty()); 1804 1805 if ([event type] == NSFlagsChanged) 1806 return NO; 1807 1808 WKViewInterpretKeyEventsParameters parameters; 1809 parameters.eventInterpretationHadSideEffects = false; 1810 parameters.executingSavedKeypressCommands = false; 1811 // We assume that an input method has consumed the event, and only change this assumption if one of the NSTextInput methods is called. 1812 // We assume the IM will *not* consume hotkey sequences. 1813 parameters.consumedByIM = !([event modifierFlags] & NSCommandKeyMask); 1814 parameters.commands = &commands; 1815 _data->_interpretKeyEventsParameters = ¶meters; 1816 1817 LOG(TextInput, "-> interpretKeyEvents:%p %@", event, event); 1818 [self interpretKeyEvents:[NSArray arrayWithObject:event]]; 1819 1820 _data->_interpretKeyEventsParameters = nullptr; 1821 1822 // An input method may consume an event and not tell us (e.g. when displaying a candidate window), 1823 // in which case we should not bubble the event up the DOM. 1824 if (parameters.consumedByIM) { 1825 ASSERT(commands.isEmpty()); 1826 LOG(TextInput, "...event %p was consumed by an input method", event); 1827 return YES; 1828 } 1829 1830 LOG(TextInput, "...interpretKeyEvents for event %p done, returns %d", event, parameters.eventInterpretationHadSideEffects); 1831 1832 // If we have already executed all or some of the commands, the event is "handled". Note that there are additional checks on web process side. 1833 return parameters.eventInterpretationHadSideEffects; 1834} 1835 1836- (void)_executeSavedKeypressCommands 1837{ 1838 WKViewInterpretKeyEventsParameters* parameters = _data->_interpretKeyEventsParameters; 1839 if (!parameters || parameters->commands->isEmpty()) 1840 return; 1841 1842 // We could be called again if the execution of one command triggers a call to selectedRange. 1843 // In this case, the state is up to date, and we don't need to execute any more saved commands to return a result. 1844 if (parameters->executingSavedKeypressCommands) 1845 return; 1846 1847 LOG(TextInput, "Executing %u saved keypress commands...", parameters->commands->size()); 1848 1849 parameters->executingSavedKeypressCommands = true; 1850 parameters->eventInterpretationHadSideEffects |= _data->_page->executeKeypressCommands(*parameters->commands); 1851 parameters->commands->clear(); 1852 parameters->executingSavedKeypressCommands = false; 1853 1854 LOG(TextInput, "...done executing saved keypress commands."); 1855} 1856 1857- (void)doCommandBySelector:(SEL)selector 1858{ 1859 LOG(TextInput, "doCommandBySelector:\"%s\"", sel_getName(selector)); 1860 1861 WKViewInterpretKeyEventsParameters* parameters = _data->_interpretKeyEventsParameters; 1862 if (parameters) 1863 parameters->consumedByIM = false; 1864 1865 // As in insertText:replacementRange:, we assume that the call comes from an input method if there is marked text. 1866 bool isFromInputMethod = _data->_page->editorState().hasComposition; 1867 1868 if (parameters && !isFromInputMethod) { 1869 KeypressCommand command(NSStringFromSelector(selector)); 1870 parameters->commands->append(command); 1871 LOG(TextInput, "...stored"); 1872 _data->_page->registerKeypressCommandName(command.commandName); 1873 } else { 1874 // FIXME: Send the command to Editor synchronously and only send it along the 1875 // responder chain if it's a selector that does not correspond to an editing command. 1876 [super doCommandBySelector:selector]; 1877 } 1878} 1879 1880- (void)insertText:(id)string 1881{ 1882 // Unlike an NSTextInputClient variant with replacementRange, this NSResponder method is called when there is no input context, 1883 // so text input processing isn't performed. We are not going to actually insert any text in that case, but saving an insertText 1884 // command ensures that a keypress event is dispatched as appropriate. 1885 [self insertText:string replacementRange:NSMakeRange(NSNotFound, 0)]; 1886} 1887 1888- (void)insertText:(id)string replacementRange:(NSRange)replacementRange 1889{ 1890 BOOL isAttributedString = [string isKindOfClass:[NSAttributedString class]]; 1891 ASSERT(isAttributedString || [string isKindOfClass:[NSString class]]); 1892 1893 if (replacementRange.location != NSNotFound) 1894 LOG(TextInput, "insertText:\"%@\" replacementRange:(%u, %u)", isAttributedString ? [string string] : string, replacementRange.location, replacementRange.length); 1895 else 1896 LOG(TextInput, "insertText:\"%@\"", isAttributedString ? [string string] : string); 1897 WKViewInterpretKeyEventsParameters* parameters = _data->_interpretKeyEventsParameters; 1898 if (parameters) 1899 parameters->consumedByIM = false; 1900 1901 NSString *text; 1902 bool isFromInputMethod = _data->_page->editorState().hasComposition; 1903 1904 Vector<TextAlternativeWithRange> dictationAlternatives; 1905 1906 if (isAttributedString) { 1907#if USE(DICTATION_ALTERNATIVES) 1908 collectDictationTextAlternatives(string, dictationAlternatives); 1909#endif 1910 // FIXME: We ignore most attributes from the string, so for example inserting from Character Palette loses font and glyph variation data. 1911 text = [string string]; 1912 } else 1913 text = string; 1914 1915 // insertText can be called for several reasons: 1916 // - If it's from normal key event processing (including key bindings), we may need to save the action to perform it later. 1917 // - If it's from an input method, then we should go ahead and insert the text now. We assume it's from the input method if we have marked text. 1918 // FIXME: In theory, this could be wrong for some input methods, so we should try to find another way to determine if the call is from the input method. 1919 // - If it's sent outside of keyboard event processing (e.g. from Character Viewer, or when confirming an inline input area with a mouse), 1920 // then we also execute it immediately, as there will be no other chance. 1921 if (parameters && !isFromInputMethod) { 1922 // FIXME: Handle replacementRange in this case, too. It's known to occur in practice when canceling Press and Hold (see <rdar://11940670>). 1923 ASSERT(replacementRange.location == NSNotFound); 1924 KeypressCommand command("insertText:", text); 1925 parameters->commands->append(command); 1926 _data->_page->registerKeypressCommandName(command.commandName); 1927 return; 1928 } 1929 1930 String eventText = text; 1931 eventText.replace(NSBackTabCharacter, NSTabCharacter); // same thing is done in KeyEventMac.mm in WebCore 1932 bool eventHandled; 1933 if (!dictationAlternatives.isEmpty()) 1934 eventHandled = _data->_page->insertDictatedText(eventText, replacementRange, dictationAlternatives); 1935 else 1936 eventHandled = _data->_page->insertText(eventText, replacementRange); 1937 1938 if (parameters) 1939 parameters->eventInterpretationHadSideEffects |= eventHandled; 1940} 1941 1942- (NSTextInputContext *)inputContext 1943{ 1944 WKViewInterpretKeyEventsParameters* parameters = _data->_interpretKeyEventsParameters; 1945 1946 if (_data->_pluginComplexTextInputIdentifier && !parameters) 1947 return [[WKTextInputWindowController sharedTextInputWindowController] inputContext]; 1948 1949 // Disable text input machinery when in non-editable content. An invisible inline input area affects performance, and can prevent Expose from working. 1950 if (!_data->_page->editorState().isContentEditable) 1951 return nil; 1952 1953 return [super inputContext]; 1954} 1955 1956- (NSRange)selectedRange 1957{ 1958 [self _executeSavedKeypressCommands]; 1959 1960 EditingRange selectedRange; 1961 _data->_page->getSelectedRange(selectedRange); 1962 1963 NSRange result = selectedRange; 1964 if (result.location == NSNotFound) 1965 LOG(TextInput, "selectedRange -> (NSNotFound, %u)", result.length); 1966 else 1967 LOG(TextInput, "selectedRange -> (%u, %u)", result.location, result.length); 1968 1969 return result; 1970} 1971 1972- (BOOL)hasMarkedText 1973{ 1974 WKViewInterpretKeyEventsParameters* parameters = _data->_interpretKeyEventsParameters; 1975 1976 BOOL result; 1977 if (parameters) { 1978 result = _data->_page->editorState().hasComposition; 1979 if (result) { 1980 // A saved command can confirm a composition, but it cannot start a new one. 1981 [self _executeSavedKeypressCommands]; 1982 result = _data->_page->editorState().hasComposition; 1983 } 1984 } else { 1985 EditingRange markedRange; 1986 _data->_page->getMarkedRange(markedRange); 1987 result = markedRange.location != notFound; 1988 } 1989 1990 LOG(TextInput, "hasMarkedText -> %u", result); 1991 return result; 1992} 1993 1994- (void)unmarkText 1995{ 1996 [self _executeSavedKeypressCommands]; 1997 1998 LOG(TextInput, "unmarkText"); 1999 2000 // Use pointer to get parameters passed to us by the caller of interpretKeyEvents. 2001 WKViewInterpretKeyEventsParameters* parameters = _data->_interpretKeyEventsParameters; 2002 2003 if (parameters) { 2004 parameters->eventInterpretationHadSideEffects = true; 2005 parameters->consumedByIM = false; 2006 } 2007 2008 _data->_page->confirmComposition(); 2009} 2010 2011- (void)setMarkedText:(id)string selectedRange:(NSRange)newSelectedRange replacementRange:(NSRange)replacementRange 2012{ 2013 [self _executeSavedKeypressCommands]; 2014 2015 BOOL isAttributedString = [string isKindOfClass:[NSAttributedString class]]; 2016 ASSERT(isAttributedString || [string isKindOfClass:[NSString class]]); 2017 2018 LOG(TextInput, "setMarkedText:\"%@\" selectedRange:(%u, %u)", isAttributedString ? [string string] : string, newSelectedRange.location, newSelectedRange.length); 2019 2020 // Use pointer to get parameters passed to us by the caller of interpretKeyEvents. 2021 WKViewInterpretKeyEventsParameters* parameters = _data->_interpretKeyEventsParameters; 2022 2023 if (parameters) { 2024 parameters->eventInterpretationHadSideEffects = true; 2025 parameters->consumedByIM = false; 2026 } 2027 2028 Vector<CompositionUnderline> underlines; 2029 NSString *text; 2030 2031 if (isAttributedString) { 2032 // FIXME: We ignore most attributes from the string, so an input method cannot specify e.g. a font or a glyph variation. 2033 text = [string string]; 2034 extractUnderlines(string, underlines); 2035 } else 2036 text = string; 2037 2038 if (_data->_page->editorState().isInPasswordField) { 2039 // In password fields, we only allow ASCII dead keys, and don't allow inline input, matching NSSecureTextInputField. 2040 // Allowing ASCII dead keys is necessary to enable full Roman input when using a Vietnamese keyboard. 2041 ASSERT(!_data->_page->editorState().hasComposition); 2042 [self _notifyInputContextAboutDiscardedComposition]; 2043 if ([text length] == 1 && [[text decomposedStringWithCanonicalMapping] characterAtIndex:0] < 0x80) { 2044 _data->_page->insertText(text, replacementRange); 2045 } else 2046 NSBeep(); 2047 return; 2048 } 2049 2050 _data->_page->setComposition(text, underlines, newSelectedRange, replacementRange); 2051} 2052 2053- (NSRange)markedRange 2054{ 2055 [self _executeSavedKeypressCommands]; 2056 2057 EditingRange markedRange; 2058 _data->_page->getMarkedRange(markedRange); 2059 2060 NSRange result = markedRange; 2061 if (result.location == NSNotFound) 2062 LOG(TextInput, "markedRange -> (NSNotFound, %u)", result.length); 2063 else 2064 LOG(TextInput, "markedRange -> (%u, %u)", result.location, result.length); 2065 2066 return result; 2067} 2068 2069- (NSAttributedString *)attributedSubstringForProposedRange:(NSRange)nsRange actualRange:(NSRangePointer)actualRange 2070{ 2071 [self _executeSavedKeypressCommands]; 2072 2073 if (!_data->_page->editorState().isContentEditable) { 2074 LOG(TextInput, "attributedSubstringFromRange:(%u, %u) -> nil", nsRange.location, nsRange.length); 2075 return nil; 2076 } 2077 2078 if (_data->_page->editorState().isInPasswordField) 2079 return nil; 2080 2081 AttributedString result; 2082 _data->_page->getAttributedSubstringFromRange(nsRange, result); 2083 2084 if (actualRange) { 2085 *actualRange = nsRange; 2086 actualRange->length = [result.string length]; 2087 } 2088 2089 LOG(TextInput, "attributedSubstringFromRange:(%u, %u) -> \"%@\"", nsRange.location, nsRange.length, [result.string string]); 2090 return [[result.string retain] autorelease]; 2091} 2092 2093- (NSUInteger)characterIndexForPoint:(NSPoint)thePoint 2094{ 2095 [self _executeSavedKeypressCommands]; 2096 2097 NSWindow *window = [self window]; 2098 2099#pragma clang diagnostic push 2100#pragma clang diagnostic ignored "-Wdeprecated-declarations" 2101 if (window) 2102 thePoint = [window convertScreenToBase:thePoint]; 2103#pragma clang diagnostic pop 2104 thePoint = [self convertPoint:thePoint fromView:nil]; // the point is relative to the main frame 2105 2106 uint64_t result = _data->_page->characterIndexForPoint(IntPoint(thePoint)); 2107 if (result == notFound) 2108 result = NSNotFound; 2109 LOG(TextInput, "characterIndexForPoint:(%f, %f) -> %u", thePoint.x, thePoint.y, result); 2110 return result; 2111} 2112 2113- (NSRect)firstRectForCharacterRange:(NSRange)theRange actualRange:(NSRangePointer)actualRange 2114{ 2115 [self _executeSavedKeypressCommands]; 2116 2117 // Just to match NSTextView's behavior. Regression tests cannot detect this; 2118 // to reproduce, use a test application from http://bugs.webkit.org/show_bug.cgi?id=4682 2119 // (type something; try ranges (1, -1) and (2, -1). 2120 if ((theRange.location + theRange.length < theRange.location) && (theRange.location + theRange.length != 0)) 2121 theRange.length = 0; 2122 2123 if (theRange.location == NSNotFound) { 2124 if (actualRange) 2125 *actualRange = theRange; 2126 LOG(TextInput, "firstRectForCharacterRange:(NSNotFound, %u) -> NSZeroRect", theRange.length); 2127 return NSZeroRect; 2128 } 2129 2130 NSRect resultRect = _data->_page->firstRectForCharacterRange(theRange); 2131 resultRect = [self convertRect:resultRect toView:nil]; 2132 resultRect = [self.window convertRectToScreen:resultRect]; 2133 2134 if (actualRange) { 2135 // FIXME: Update actualRange to match the range of first rect. 2136 *actualRange = theRange; 2137 } 2138 2139 LOG(TextInput, "firstRectForCharacterRange:(%u, %u) -> (%f, %f, %f, %f)", theRange.location, theRange.length, resultRect.origin.x, resultRect.origin.y, resultRect.size.width, resultRect.size.height); 2140 return resultRect; 2141} 2142 2143- (BOOL)performKeyEquivalent:(NSEvent *)event 2144{ 2145 // There's a chance that responding to this event will run a nested event loop, and 2146 // fetching a new event might release the old one. Retaining and then autoreleasing 2147 // the current event prevents that from causing a problem inside WebKit or AppKit code. 2148 [[event retain] autorelease]; 2149 2150 // We get Esc key here after processing either Esc or Cmd+period. The former starts as a keyDown, and the latter starts as a key equivalent, 2151 // but both get transformed to a cancelOperation: command, executing which passes an Esc key event to -performKeyEquivalent:. 2152 // Don't interpret this event again, avoiding re-entrancy and infinite loops. 2153 if ([[event charactersIgnoringModifiers] isEqualToString:@"\e"] && !([event modifierFlags] & NSDeviceIndependentModifierFlagsMask)) 2154 return [super performKeyEquivalent:event]; 2155 2156 if (_data->_keyDownEventBeingResent) { 2157 // WebCore has already seen the event, no need for custom processing. 2158 // Note that we can get multiple events for each event being re-sent. For example, for Cmd+'=' AppKit 2159 // first performs the original key equivalent, and if that isn't handled, it dispatches a synthetic Cmd+'+'. 2160 return [super performKeyEquivalent:event]; 2161 } 2162 2163 ASSERT(event == [NSApp currentEvent]); 2164 2165 [self _disableComplexTextInputIfNecessary]; 2166 2167 // Pass key combos through WebCore if there is a key binding available for 2168 // this event. This lets webpages have a crack at intercepting key-modified keypresses. 2169 // FIXME: Why is the firstResponder check needed? 2170 if (self == [[self window] firstResponder]) { 2171 Vector<KeypressCommand> commands; 2172 BOOL handledByInputMethod = [self _interpretKeyEvent:event savingCommandsTo:commands]; 2173 _data->_page->handleKeyboardEvent(NativeWebKeyboardEvent(event, handledByInputMethod, commands)); 2174 return YES; 2175 } 2176 2177 return [super performKeyEquivalent:event]; 2178} 2179 2180- (void)keyUp:(NSEvent *)theEvent 2181{ 2182 LOG(TextInput, "keyUp:%p %@", theEvent, theEvent); 2183 // We don't interpret the keyUp event, as this breaks key bindings (see <https://bugs.webkit.org/show_bug.cgi?id=130100>). 2184 _data->_page->handleKeyboardEvent(NativeWebKeyboardEvent(theEvent, false, Vector<KeypressCommand>())); 2185} 2186 2187- (void)keyDown:(NSEvent *)theEvent 2188{ 2189 LOG(TextInput, "keyDown:%p %@%s", theEvent, theEvent, (theEvent == _data->_keyDownEventBeingResent) ? " (re-sent)" : ""); 2190 2191 // There's a chance that responding to this event will run a nested event loop, and 2192 // fetching a new event might release the old one. Retaining and then autoreleasing 2193 // the current event prevents that from causing a problem inside WebKit or AppKit code. 2194 [[theEvent retain] autorelease]; 2195 2196 if ([self _tryHandlePluginComplexTextInputKeyDown:theEvent]) { 2197 LOG(TextInput, "...handled by plug-in"); 2198 return; 2199 } 2200 2201 // We could be receiving a key down from AppKit if we have re-sent an event 2202 // that maps to an action that is currently unavailable (for example a copy when 2203 // there is no range selection). 2204 // If this is the case we should ignore the key down. 2205 if (_data->_keyDownEventBeingResent == theEvent) { 2206 [super keyDown:theEvent]; 2207 return; 2208 } 2209 2210 Vector<KeypressCommand> commands; 2211 BOOL handledByInputMethod = [self _interpretKeyEvent:theEvent savingCommandsTo:commands]; 2212 if (!commands.isEmpty()) { 2213 // An input method may make several actions per keypress. For example, pressing Return with Korean IM both confirms it and sends a newline. 2214 // IM-like actions are handled immediately (so the return value from UI process is true), but there are saved commands that 2215 // should be handled like normal text input after DOM event dispatch. 2216 handledByInputMethod = NO; 2217 } 2218 2219 _data->_page->handleKeyboardEvent(NativeWebKeyboardEvent(theEvent, handledByInputMethod, commands)); 2220} 2221 2222- (void)flagsChanged:(NSEvent *)theEvent 2223{ 2224 LOG(TextInput, "flagsChanged:%p %@", theEvent, theEvent); 2225 2226 // There's a chance that responding to this event will run a nested event loop, and 2227 // fetching a new event might release the old one. Retaining and then autoreleasing 2228 // the current event prevents that from causing a problem inside WebKit or AppKit code. 2229 [[theEvent retain] autorelease]; 2230 2231 unsigned short keyCode = [theEvent keyCode]; 2232 2233 // Don't make an event from the num lock and function keys 2234 if (!keyCode || keyCode == 10 || keyCode == 63) 2235 return; 2236 2237 _data->_page->handleKeyboardEvent(NativeWebKeyboardEvent(theEvent, false, Vector<KeypressCommand>())); 2238} 2239 2240#endif // USE(ASYNC_NSTEXTINPUTCLIENT) 2241 2242- (NSArray *)validAttributesForMarkedText 2243{ 2244 static NSArray *validAttributes; 2245 if (!validAttributes) { 2246 validAttributes = [[NSArray alloc] initWithObjects: 2247 NSUnderlineStyleAttributeName, NSUnderlineColorAttributeName, 2248 NSMarkedClauseSegmentAttributeName, 2249#if USE(DICTATION_ALTERNATIVES) 2250 NSTextAlternativesAttributeName, 2251#endif 2252#if USE(INSERTION_UNDO_GROUPING) 2253 NSTextInsertionUndoableAttributeName, 2254#endif 2255 nil]; 2256 // NSText also supports the following attributes, but it's 2257 // hard to tell which are really required for text input to 2258 // work well; I have not seen any input method make use of them yet. 2259 // NSFontAttributeName, NSForegroundColorAttributeName, 2260 // NSBackgroundColorAttributeName, NSLanguageAttributeName. 2261 CFRetain(validAttributes); 2262 } 2263 LOG(TextInput, "validAttributesForMarkedText -> (...)"); 2264 return validAttributes; 2265} 2266 2267#if ENABLE(DRAG_SUPPORT) 2268- (void)draggedImage:(NSImage *)anImage endedAt:(NSPoint)aPoint operation:(NSDragOperation)operation 2269{ 2270 NSPoint windowImageLoc = [[self window] convertScreenToBase:aPoint]; 2271 NSPoint windowMouseLoc = windowImageLoc; 2272 2273 // Prevent queued mouseDragged events from coming after the drag and fake mouseUp event. 2274 _data->_ignoringMouseDraggedEvents = YES; 2275 2276 _data->_page->dragEnded(IntPoint(windowMouseLoc), globalPoint(windowMouseLoc, [self window]), operation); 2277} 2278 2279- (DragApplicationFlags)applicationFlags:(id <NSDraggingInfo>)draggingInfo 2280{ 2281 uint32_t flags = 0; 2282 if ([NSApp modalWindow]) 2283 flags = DragApplicationIsModal; 2284 if ([[self window] attachedSheet]) 2285 flags |= DragApplicationHasAttachedSheet; 2286 if ([draggingInfo draggingSource] == self) 2287 flags |= DragApplicationIsSource; 2288 if ([[NSApp currentEvent] modifierFlags] & NSAlternateKeyMask) 2289 flags |= DragApplicationIsCopyKeyDown; 2290 return static_cast<DragApplicationFlags>(flags); 2291} 2292 2293- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)draggingInfo 2294{ 2295 IntPoint client([self convertPoint:[draggingInfo draggingLocation] fromView:nil]); 2296 IntPoint global(globalPoint([draggingInfo draggingLocation], [self window])); 2297 DragData dragData(draggingInfo, client, global, static_cast<DragOperation>([draggingInfo draggingSourceOperationMask]), [self applicationFlags:draggingInfo]); 2298 2299 _data->_page->resetCurrentDragInformation(); 2300 _data->_page->dragEntered(dragData, [[draggingInfo draggingPasteboard] name]); 2301 return NSDragOperationCopy; 2302} 2303 2304- (NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)draggingInfo 2305{ 2306 IntPoint client([self convertPoint:[draggingInfo draggingLocation] fromView:nil]); 2307 IntPoint global(globalPoint([draggingInfo draggingLocation], [self window])); 2308 DragData dragData(draggingInfo, client, global, static_cast<DragOperation>([draggingInfo draggingSourceOperationMask]), [self applicationFlags:draggingInfo]); 2309 _data->_page->dragUpdated(dragData, [[draggingInfo draggingPasteboard] name]); 2310 2311 NSInteger numberOfValidItemsForDrop = _data->_page->currentDragNumberOfFilesToBeAccepted(); 2312 NSDraggingFormation draggingFormation = NSDraggingFormationNone; 2313 if (_data->_page->currentDragIsOverFileInput() && numberOfValidItemsForDrop > 0) 2314 draggingFormation = NSDraggingFormationList; 2315 2316 if ([draggingInfo numberOfValidItemsForDrop] != numberOfValidItemsForDrop) 2317 [draggingInfo setNumberOfValidItemsForDrop:numberOfValidItemsForDrop]; 2318 if ([draggingInfo draggingFormation] != draggingFormation) 2319 [draggingInfo setDraggingFormation:draggingFormation]; 2320 2321 return _data->_page->currentDragOperation(); 2322} 2323 2324- (void)draggingExited:(id <NSDraggingInfo>)draggingInfo 2325{ 2326 IntPoint client([self convertPoint:[draggingInfo draggingLocation] fromView:nil]); 2327 IntPoint global(globalPoint([draggingInfo draggingLocation], [self window])); 2328 DragData dragData(draggingInfo, client, global, static_cast<DragOperation>([draggingInfo draggingSourceOperationMask]), [self applicationFlags:draggingInfo]); 2329 _data->_page->dragExited(dragData, [[draggingInfo draggingPasteboard] name]); 2330 _data->_page->resetCurrentDragInformation(); 2331} 2332 2333- (BOOL)prepareForDragOperation:(id <NSDraggingInfo>)draggingInfo 2334{ 2335 return YES; 2336} 2337 2338// FIXME: This code is more or less copied from Pasteboard::getBestURL. 2339// It would be nice to be able to share the code somehow. 2340static bool maybeCreateSandboxExtensionFromPasteboard(NSPasteboard *pasteboard, SandboxExtension::Handle& sandboxExtensionHandle) 2341{ 2342 NSArray *types = [pasteboard types]; 2343 if (![types containsObject:NSFilenamesPboardType]) 2344 return false; 2345 2346 NSArray *files = [pasteboard propertyListForType:NSFilenamesPboardType]; 2347 if ([files count] != 1) 2348 return false; 2349 2350 NSString *file = [files objectAtIndex:0]; 2351 BOOL isDirectory; 2352 if (![[NSFileManager defaultManager] fileExistsAtPath:file isDirectory:&isDirectory]) 2353 return false; 2354 2355 if (isDirectory) 2356 return false; 2357 2358 SandboxExtension::createHandle("/", SandboxExtension::ReadOnly, sandboxExtensionHandle); 2359 return true; 2360} 2361 2362static void createSandboxExtensionsForFileUpload(NSPasteboard *pasteboard, SandboxExtension::HandleArray& handles) 2363{ 2364 NSArray *types = [pasteboard types]; 2365 if (![types containsObject:NSFilenamesPboardType]) 2366 return; 2367 2368 NSArray *files = [pasteboard propertyListForType:NSFilenamesPboardType]; 2369 handles.allocate([files count]); 2370 for (unsigned i = 0; i < [files count]; i++) { 2371 NSString *file = [files objectAtIndex:i]; 2372 if (![[NSFileManager defaultManager] fileExistsAtPath:file]) 2373 continue; 2374 SandboxExtension::Handle handle; 2375 SandboxExtension::createHandle(file, SandboxExtension::ReadOnly, handles[i]); 2376 } 2377} 2378 2379- (BOOL)performDragOperation:(id <NSDraggingInfo>)draggingInfo 2380{ 2381 IntPoint client([self convertPoint:[draggingInfo draggingLocation] fromView:nil]); 2382 IntPoint global(globalPoint([draggingInfo draggingLocation], [self window])); 2383 DragData dragData(draggingInfo, client, global, static_cast<DragOperation>([draggingInfo draggingSourceOperationMask]), [self applicationFlags:draggingInfo]); 2384 2385 SandboxExtension::Handle sandboxExtensionHandle; 2386 bool createdExtension = maybeCreateSandboxExtensionFromPasteboard([draggingInfo draggingPasteboard], sandboxExtensionHandle); 2387 if (createdExtension) 2388 _data->_page->process().willAcquireUniversalFileReadSandboxExtension(); 2389 2390 SandboxExtension::HandleArray sandboxExtensionForUpload; 2391 createSandboxExtensionsForFileUpload([draggingInfo draggingPasteboard], sandboxExtensionForUpload); 2392 2393 _data->_page->performDragOperation(dragData, [[draggingInfo draggingPasteboard] name], sandboxExtensionHandle, sandboxExtensionForUpload); 2394 2395 return YES; 2396} 2397 2398// This code is needed to support drag and drop when the drag types cannot be matched. 2399// This is the case for elements that do not place content 2400// in the drag pasteboard automatically when the drag start (i.e. dragging a DIV element). 2401- (NSView *)_hitTest:(NSPoint *)point dragTypes:(NSSet *)types 2402{ 2403 if ([[self superview] mouse:*point inRect:[self frame]]) 2404 return self; 2405 return nil; 2406} 2407#endif // ENABLE(DRAG_SUPPORT) 2408 2409- (BOOL)_windowResizeMouseLocationIsInVisibleScrollerThumb:(NSPoint)loc 2410{ 2411 NSPoint localPoint = [self convertPoint:loc fromView:nil]; 2412 NSRect visibleThumbRect = NSRect(_data->_page->visibleScrollerThumbRect()); 2413 return NSMouseInRect(localPoint, visibleThumbRect, [self isFlipped]); 2414} 2415 2416#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101000 2417static void* keyValueObservingContext = &keyValueObservingContext; 2418#endif 2419 2420- (void)addWindowObserversForWindow:(NSWindow *)window 2421{ 2422 if (window) { 2423 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_windowDidBecomeKey:) 2424 name:NSWindowDidBecomeKeyNotification object:nil]; 2425 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_windowDidResignKey:) 2426 name:NSWindowDidResignKeyNotification object:nil]; 2427 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_windowDidMiniaturize:) 2428 name:NSWindowDidMiniaturizeNotification object:window]; 2429 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_windowDidDeminiaturize:) 2430 name:NSWindowDidDeminiaturizeNotification object:window]; 2431 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_windowDidMove:) 2432 name:NSWindowDidMoveNotification object:window]; 2433 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_windowDidResize:) 2434 name:NSWindowDidResizeNotification object:window]; 2435 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_windowDidOrderOffScreen:) 2436 name:@"NSWindowDidOrderOffScreenNotification" object:window]; 2437 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_windowDidOrderOnScreen:) 2438 name:@"_NSWindowDidBecomeVisible" object:window]; 2439 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_windowDidChangeBackingProperties:) 2440 name:NSWindowDidChangeBackingPropertiesNotification object:window]; 2441 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_windowDidChangeScreen:) 2442 name:NSWindowDidChangeScreenNotification object:window]; 2443 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_windowDidChangeLayerHosting:) 2444 name:@"_NSWindowDidChangeContentsHostedInLayerSurfaceNotification" object:window]; 2445#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1090 2446 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_windowDidChangeOcclusionState:) 2447 name:NSWindowDidChangeOcclusionStateNotification object:window]; 2448#endif 2449#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101000 2450 [window addObserver:self forKeyPath:@"contentLayoutRect" options:NSKeyValueObservingOptionInitial context:keyValueObservingContext]; 2451 [window addObserver:self forKeyPath:@"titlebarAppearsTransparent" options:NSKeyValueObservingOptionInitial context:keyValueObservingContext]; 2452#endif 2453 } 2454} 2455 2456- (void)removeWindowObservers 2457{ 2458 NSWindow *window = [self window]; 2459 if (!window) 2460 return; 2461 2462 [[NSNotificationCenter defaultCenter] removeObserver:self name:NSWindowDidBecomeKeyNotification object:nil]; 2463 [[NSNotificationCenter defaultCenter] removeObserver:self name:NSWindowDidResignKeyNotification object:nil]; 2464 [[NSNotificationCenter defaultCenter] removeObserver:self name:NSWindowDidMiniaturizeNotification object:window]; 2465 [[NSNotificationCenter defaultCenter] removeObserver:self name:NSWindowDidDeminiaturizeNotification object:window]; 2466 [[NSNotificationCenter defaultCenter] removeObserver:self name:NSWindowDidMoveNotification object:window]; 2467 [[NSNotificationCenter defaultCenter] removeObserver:self name:NSWindowDidResizeNotification object:window]; 2468 [[NSNotificationCenter defaultCenter] removeObserver:self name:@"NSWindowWillOrderOffScreenNotification" object:window]; 2469 [[NSNotificationCenter defaultCenter] removeObserver:self name:@"NSWindowDidOrderOffScreenNotification" object:window]; 2470 [[NSNotificationCenter defaultCenter] removeObserver:self name:@"_NSWindowDidBecomeVisible" object:window]; 2471 [[NSNotificationCenter defaultCenter] removeObserver:self name:NSWindowDidChangeBackingPropertiesNotification object:window]; 2472 [[NSNotificationCenter defaultCenter] removeObserver:self name:NSWindowDidChangeScreenNotification object:window]; 2473 [[NSNotificationCenter defaultCenter] removeObserver:self name:@"_NSWindowDidChangeContentsHostedInLayerSurfaceNotification" object:window]; 2474#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1090 2475 [[NSNotificationCenter defaultCenter] removeObserver:self name:NSWindowDidChangeOcclusionStateNotification object:window]; 2476#endif 2477#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101000 2478 [window removeObserver:self forKeyPath:@"contentLayoutRect" context:keyValueObservingContext]; 2479 [window removeObserver:self forKeyPath:@"titlebarAppearsTransparent" context:keyValueObservingContext]; 2480#endif 2481} 2482 2483- (void)viewWillMoveToWindow:(NSWindow *)window 2484{ 2485 NSWindow *currentWindow = [self window]; 2486 if (window == currentWindow) 2487 return; 2488 2489 _data->_pageClient->viewWillMoveToAnotherWindow(); 2490 2491 [self removeWindowObservers]; 2492 [self addWindowObserversForWindow:window]; 2493} 2494 2495- (void)viewDidMoveToWindow 2496{ 2497 if ([self window]) { 2498 [self doWindowDidChangeScreen]; 2499 2500 ViewState::Flags viewStateChanges = ViewState::WindowIsActive | ViewState::IsVisible; 2501 if ([self isDeferringViewInWindowChanges]) 2502 _data->_viewInWindowChangeWasDeferred = YES; 2503 else 2504 viewStateChanges |= ViewState::IsInWindow; 2505 _data->_page->viewStateDidChange(viewStateChanges); 2506 2507 [self _updateWindowAndViewFrames]; 2508 2509 // FIXME(135509) This call becomes unnecessary once 135509 is fixed; remove. 2510 _data->_page->layerHostingModeDidChange(); 2511 2512 if (!_data->_flagsChangedEventMonitor) { 2513 _data->_flagsChangedEventMonitor = [NSEvent addLocalMonitorForEventsMatchingMask:NSFlagsChangedMask handler:^(NSEvent *flagsChangedEvent) { 2514 [self _postFakeMouseMovedEventForFlagsChangedEvent:flagsChangedEvent]; 2515 return flagsChangedEvent; 2516 }]; 2517 } 2518 2519 [self _accessibilityRegisterUIProcessTokens]; 2520 } else { 2521 ViewState::Flags viewStateChanges = ViewState::WindowIsActive | ViewState::IsVisible; 2522 if ([self isDeferringViewInWindowChanges]) 2523 _data->_viewInWindowChangeWasDeferred = YES; 2524 else 2525 viewStateChanges |= ViewState::IsInWindow; 2526 _data->_page->viewStateDidChange(viewStateChanges); 2527 2528 [NSEvent removeMonitor:_data->_flagsChangedEventMonitor]; 2529 _data->_flagsChangedEventMonitor = nil; 2530 2531 WKHideWordDefinitionWindow(); 2532 } 2533 2534 _data->_page->setIntrinsicDeviceScaleFactor([self _intrinsicDeviceScaleFactor]); 2535} 2536 2537- (void)doWindowDidChangeScreen 2538{ 2539 _data->_page->windowScreenDidChange((PlatformDisplayID)[[[[[self window] screen] deviceDescription] objectForKey:@"NSScreenNumber"] intValue]); 2540} 2541 2542- (void)_windowDidBecomeKey:(NSNotification *)notification 2543{ 2544 NSWindow *keyWindow = [notification object]; 2545 if (keyWindow == [self window] || keyWindow == [[self window] attachedSheet]) { 2546 [self _updateSecureInputState]; 2547 _data->_page->viewStateDidChange(ViewState::WindowIsActive); 2548 } 2549} 2550 2551- (void)_windowDidChangeScreen:(NSNotification *)notification 2552{ 2553 [self doWindowDidChangeScreen]; 2554} 2555 2556- (void)_windowDidChangeLayerHosting:(NSNotification *)notification 2557{ 2558 _data->_page->layerHostingModeDidChange(); 2559} 2560 2561- (void)_windowDidResignKey:(NSNotification *)notification 2562{ 2563 NSWindow *formerKeyWindow = [notification object]; 2564 if (formerKeyWindow == [self window] || formerKeyWindow == [[self window] attachedSheet]) { 2565 [self _updateSecureInputState]; 2566 _data->_page->viewStateDidChange(ViewState::WindowIsActive); 2567 } 2568} 2569 2570- (void)_windowDidMiniaturize:(NSNotification *)notification 2571{ 2572 _data->_page->viewStateDidChange(ViewState::IsVisible); 2573} 2574 2575- (void)_windowDidDeminiaturize:(NSNotification *)notification 2576{ 2577 _data->_page->viewStateDidChange(ViewState::IsVisible); 2578} 2579 2580- (void)_windowDidMove:(NSNotification *)notification 2581{ 2582 [self _updateWindowAndViewFrames]; 2583} 2584 2585- (void)_windowDidResize:(NSNotification *)notification 2586{ 2587 [self _updateWindowAndViewFrames]; 2588} 2589 2590- (void)_windowDidOrderOffScreen:(NSNotification *)notification 2591{ 2592 _data->_page->viewStateDidChange(ViewState::IsVisible | ViewState::WindowIsActive); 2593} 2594 2595- (void)_windowDidOrderOnScreen:(NSNotification *)notification 2596{ 2597 _data->_page->viewStateDidChange(ViewState::IsVisible | ViewState::WindowIsActive); 2598} 2599 2600- (void)_windowDidChangeBackingProperties:(NSNotification *)notification 2601{ 2602 CGFloat oldBackingScaleFactor = [[notification.userInfo objectForKey:NSBackingPropertyOldScaleFactorKey] doubleValue]; 2603 CGFloat newBackingScaleFactor = [self _intrinsicDeviceScaleFactor]; 2604 if (oldBackingScaleFactor == newBackingScaleFactor) 2605 return; 2606 2607 _data->_page->setIntrinsicDeviceScaleFactor(newBackingScaleFactor); 2608} 2609 2610#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1090 2611- (void)_windowDidChangeOcclusionState:(NSNotification *)notification 2612{ 2613 _data->_page->viewStateDidChange(ViewState::IsVisible); 2614} 2615#endif 2616 2617- (void)drawRect:(NSRect)rect 2618{ 2619 LOG(View, "drawRect: x:%g, y:%g, width:%g, height:%g", rect.origin.x, rect.origin.y, rect.size.width, rect.size.height); 2620 _data->_page->endPrinting(); 2621} 2622 2623- (BOOL)isOpaque 2624{ 2625 return _data->_page->drawsBackground(); 2626} 2627 2628- (BOOL)mouseDownCanMoveWindow 2629{ 2630 // -[NSView mouseDownCanMoveWindow] returns YES when the NSView is transparent, 2631 // but we don't want a drag in the NSView to move the window, even if it's transparent. 2632 return NO; 2633} 2634 2635- (void)viewDidHide 2636{ 2637 _data->_page->viewStateDidChange(ViewState::IsVisible); 2638} 2639 2640- (void)viewDidUnhide 2641{ 2642 _data->_page->viewStateDidChange(ViewState::IsVisible); 2643} 2644 2645- (void)viewDidChangeBackingProperties 2646{ 2647 NSColorSpace *colorSpace = [[self window] colorSpace]; 2648 if ([colorSpace isEqualTo:_data->_colorSpace.get()]) 2649 return; 2650 2651 _data->_colorSpace = nullptr; 2652 if (DrawingAreaProxy *drawingArea = _data->_page->drawingArea()) 2653 drawingArea->colorSpaceDidChange(); 2654} 2655 2656- (void)_activeSpaceDidChange:(NSNotification *)notification 2657{ 2658 _data->_page->viewStateDidChange(ViewState::IsVisible); 2659} 2660 2661- (void)_applicationWillTerminate:(NSNotification *)notification 2662{ 2663 _data->_page->process().context().applicationWillTerminate(); 2664} 2665 2666- (void)_accessibilityRegisterUIProcessTokens 2667{ 2668 // Initialize remote accessibility when the window connection has been established. 2669 NSData *remoteElementToken = WKAXRemoteTokenForElement(self); 2670 NSData *remoteWindowToken = WKAXRemoteTokenForElement([self window]); 2671 IPC::DataReference elementToken = IPC::DataReference(reinterpret_cast<const uint8_t*>([remoteElementToken bytes]), [remoteElementToken length]); 2672 IPC::DataReference windowToken = IPC::DataReference(reinterpret_cast<const uint8_t*>([remoteWindowToken bytes]), [remoteWindowToken length]); 2673 _data->_page->registerUIProcessAccessibilityTokens(elementToken, windowToken); 2674} 2675 2676- (void)_updateRemoteAccessibilityRegistration:(BOOL)registerProcess 2677{ 2678 // When the tree is connected/disconnected, the remote accessibility registration 2679 // needs to be updated with the pid of the remote process. If the process is going 2680 // away, that information is not present in WebProcess 2681 pid_t pid = 0; 2682 if (registerProcess) 2683 pid = _data->_page->process().processIdentifier(); 2684 else if (!registerProcess) { 2685 pid = WKAXRemoteProcessIdentifier(_data->_remoteAccessibilityChild.get()); 2686 _data->_remoteAccessibilityChild = nil; 2687 } 2688 if (pid) 2689 WKAXRegisterRemoteProcess(registerProcess, pid); 2690} 2691 2692- (void)enableAccessibilityIfNecessary 2693{ 2694 if (WebCore::AXObjectCache::accessibilityEnabled()) 2695 return; 2696 2697 // After enabling accessibility update the window frame on the web process so that the 2698 // correct accessibility position is transmitted (when AX is off, that position is not calculated). 2699 WebCore::AXObjectCache::enableAccessibility(); 2700 [self _updateWindowAndViewFrames]; 2701} 2702 2703- (id)accessibilityFocusedUIElement 2704{ 2705 [self enableAccessibilityIfNecessary]; 2706 return _data->_remoteAccessibilityChild.get(); 2707} 2708 2709- (BOOL)accessibilityIsIgnored 2710{ 2711 return NO; 2712} 2713 2714- (id)accessibilityHitTest:(NSPoint)point 2715{ 2716 [self enableAccessibilityIfNecessary]; 2717 return _data->_remoteAccessibilityChild.get(); 2718} 2719 2720- (id)accessibilityAttributeValue:(NSString*)attribute 2721{ 2722 [self enableAccessibilityIfNecessary]; 2723 2724 if ([attribute isEqualToString:NSAccessibilityChildrenAttribute]) { 2725 2726 id child = nil; 2727 if (_data->_remoteAccessibilityChild) 2728 child = _data->_remoteAccessibilityChild.get(); 2729 2730 if (!child) 2731 return nil; 2732 return [NSArray arrayWithObject:child]; 2733 } 2734 if ([attribute isEqualToString:NSAccessibilityRoleAttribute]) 2735 return NSAccessibilityGroupRole; 2736 if ([attribute isEqualToString:NSAccessibilityRoleDescriptionAttribute]) 2737 return NSAccessibilityRoleDescription(NSAccessibilityGroupRole, nil); 2738 if ([attribute isEqualToString:NSAccessibilityParentAttribute]) 2739 return NSAccessibilityUnignoredAncestor([self superview]); 2740 if ([attribute isEqualToString:NSAccessibilityEnabledAttribute]) 2741 return [NSNumber numberWithBool:YES]; 2742 2743 return [super accessibilityAttributeValue:attribute]; 2744} 2745 2746- (NSView *)hitTest:(NSPoint)point 2747{ 2748 NSView *hitView = [super hitTest:point]; 2749 if (hitView && _data && hitView == _data->_layerHostingView) 2750 hitView = self; 2751 2752 return hitView; 2753} 2754 2755- (void)_postFakeMouseMovedEventForFlagsChangedEvent:(NSEvent *)flagsChangedEvent 2756{ 2757 NSEvent *fakeEvent = [NSEvent mouseEventWithType:NSMouseMoved location:[[flagsChangedEvent window] mouseLocationOutsideOfEventStream] 2758 modifierFlags:[flagsChangedEvent modifierFlags] timestamp:[flagsChangedEvent timestamp] windowNumber:[flagsChangedEvent windowNumber] 2759 context:[flagsChangedEvent context] eventNumber:0 clickCount:0 pressure:0]; 2760 NativeWebMouseEvent webEvent(fakeEvent, self); 2761 _data->_page->handleMouseEvent(webEvent); 2762} 2763 2764- (NSInteger)conversationIdentifier 2765{ 2766 return (NSInteger)self; 2767} 2768 2769- (float)_intrinsicDeviceScaleFactor 2770{ 2771 NSWindow *window = [self window]; 2772 if (window) 2773 return [window backingScaleFactor]; 2774 return [[NSScreen mainScreen] backingScaleFactor]; 2775} 2776 2777- (void)_setDrawingAreaSize:(NSSize)size 2778{ 2779 if (!_data->_page->drawingArea()) 2780 return; 2781 2782 _data->_page->drawingArea()->setSize(IntSize(size), IntSize(0, 0), IntSize(_data->_resizeScrollOffset)); 2783 _data->_resizeScrollOffset = NSZeroSize; 2784} 2785 2786#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1080 2787- (void)quickLookWithEvent:(NSEvent *)event 2788{ 2789 NSPoint locationInViewCoordinates = [self convertPoint:[event locationInWindow] fromView:nil]; 2790 _data->_page->performDictionaryLookupAtLocation(FloatPoint(locationInViewCoordinates.x, locationInViewCoordinates.y)); 2791} 2792#endif 2793 2794- (std::unique_ptr<WebKit::DrawingAreaProxy>)_createDrawingAreaProxy 2795{ 2796 if ([[[NSUserDefaults standardUserDefaults] objectForKey:@"WebKit2UseRemoteLayerTreeDrawingArea"] boolValue]) 2797 return std::make_unique<RemoteLayerTreeDrawingAreaProxy>(_data->_page.get()); 2798 2799 return std::make_unique<TiledCoreAnimationDrawingAreaProxy>(_data->_page.get()); 2800} 2801 2802- (BOOL)_isFocused 2803{ 2804 if (_data->_inBecomeFirstResponder) 2805 return YES; 2806 if (_data->_inResignFirstResponder) 2807 return NO; 2808 return [[self window] firstResponder] == self; 2809} 2810 2811- (WebKit::ColorSpaceData)_colorSpace 2812{ 2813 if (!_data->_colorSpace) { 2814 if ([self window]) 2815 _data->_colorSpace = [[self window] colorSpace]; 2816 else 2817 _data->_colorSpace = [[NSScreen mainScreen] colorSpace]; 2818 } 2819 2820 ColorSpaceData colorSpaceData; 2821 colorSpaceData.cgColorSpace = [_data->_colorSpace CGColorSpace]; 2822 2823 return colorSpaceData; 2824} 2825 2826- (void)_processDidExit 2827{ 2828 if (_data->_layerHostingView) 2829 [self _setAcceleratedCompositingModeRootLayer:nil]; 2830 2831 [self _updateRemoteAccessibilityRegistration:NO]; 2832 2833 _data->_gestureController = nullptr; 2834} 2835 2836- (void)_pageClosed 2837{ 2838 [self _updateRemoteAccessibilityRegistration:NO]; 2839} 2840 2841- (void)_didRelaunchProcess 2842{ 2843 [self _accessibilityRegisterUIProcessTokens]; 2844} 2845 2846- (void)_preferencesDidChange 2847{ 2848 BOOL needsViewFrameInWindowCoordinates = _data->_page->preferences().pluginsEnabled(); 2849 2850 if (!!needsViewFrameInWindowCoordinates == !!_data->_needsViewFrameInWindowCoordinates) 2851 return; 2852 2853 _data->_needsViewFrameInWindowCoordinates = needsViewFrameInWindowCoordinates; 2854 if ([self window]) 2855 [self _updateWindowAndViewFrames]; 2856} 2857 2858- (void)_setUserInterfaceItemState:(NSString *)commandName enabled:(BOOL)isEnabled state:(int)newState 2859{ 2860 ValidationVector items = _data->_validationMap.take(commandName); 2861 size_t size = items.size(); 2862 for (size_t i = 0; i < size; ++i) { 2863 ValidationItem item = items[i].get(); 2864 [menuItem(item) setState:newState]; 2865 [menuItem(item) setEnabled:isEnabled]; 2866 [toolbarItem(item) setEnabled:isEnabled]; 2867 // FIXME <rdar://problem/8803392>: If the item is neither a menu nor toolbar item, it will be left enabled. 2868 } 2869} 2870 2871- (BOOL)_tryPostProcessPluginComplexTextInputKeyDown:(NSEvent *)event 2872{ 2873 if (!_data->_pluginComplexTextInputIdentifier || _data->_pluginComplexTextInputState == PluginComplexTextInputDisabled) 2874 return NO; 2875 2876 // In the legacy text input model, the event has already been sent to the input method. 2877 if (_data->_pluginComplexTextInputState == PluginComplexTextInputEnabledLegacy) 2878 return NO; 2879 2880 return [self _handlePluginComplexTextInputKeyDown:event]; 2881} 2882 2883- (void)_doneWithKeyEvent:(NSEvent *)event eventWasHandled:(BOOL)eventWasHandled 2884{ 2885 if ([event type] != NSKeyDown) 2886 return; 2887 2888 if ([self _tryPostProcessPluginComplexTextInputKeyDown:event]) 2889 return; 2890 2891 if (eventWasHandled) { 2892 [NSCursor setHiddenUntilMouseMoves:YES]; 2893 return; 2894 } 2895 2896 // resending the event may destroy this WKView 2897 RetainPtr<WKView> protector(self); 2898 2899 ASSERT(!_data->_keyDownEventBeingResent); 2900 _data->_keyDownEventBeingResent = event; 2901 [NSApp _setCurrentEvent:event]; 2902 [NSApp sendEvent:event]; 2903 2904 _data->_keyDownEventBeingResent = nullptr; 2905} 2906 2907- (NSRect)_convertToDeviceSpace:(NSRect)rect 2908{ 2909 return toDeviceSpace(rect, [self window]); 2910} 2911 2912- (NSRect)_convertToUserSpace:(NSRect)rect 2913{ 2914 return toUserSpace(rect, [self window]); 2915} 2916 2917// Any non-zero value will do, but using something recognizable might help us debug some day. 2918#define TRACKING_RECT_TAG 0xBADFACE 2919 2920- (NSTrackingRectTag)addTrackingRect:(NSRect)rect owner:(id)owner userData:(void *)data assumeInside:(BOOL)assumeInside 2921{ 2922 ASSERT(_data->_trackingRectOwner == nil); 2923 _data->_trackingRectOwner = owner; 2924 _data->_trackingRectUserData = data; 2925 return TRACKING_RECT_TAG; 2926} 2927 2928- (NSTrackingRectTag)_addTrackingRect:(NSRect)rect owner:(id)owner userData:(void *)data assumeInside:(BOOL)assumeInside useTrackingNum:(int)tag 2929{ 2930 ASSERT(tag == 0 || tag == TRACKING_RECT_TAG); 2931 ASSERT(_data->_trackingRectOwner == nil); 2932 _data->_trackingRectOwner = owner; 2933 _data->_trackingRectUserData = data; 2934 return TRACKING_RECT_TAG; 2935} 2936 2937- (void)_addTrackingRects:(NSRect *)rects owner:(id)owner userDataList:(void **)userDataList assumeInsideList:(BOOL *)assumeInsideList trackingNums:(NSTrackingRectTag *)trackingNums count:(int)count 2938{ 2939 ASSERT(count == 1); 2940 ASSERT(trackingNums[0] == 0 || trackingNums[0] == TRACKING_RECT_TAG); 2941 ASSERT(_data->_trackingRectOwner == nil); 2942 _data->_trackingRectOwner = owner; 2943 _data->_trackingRectUserData = userDataList[0]; 2944 trackingNums[0] = TRACKING_RECT_TAG; 2945} 2946 2947- (void)removeTrackingRect:(NSTrackingRectTag)tag 2948{ 2949 if (!_data) 2950 return; 2951 2952 if (tag == 0) 2953 return; 2954 2955 if (tag == TRACKING_RECT_TAG) { 2956 _data->_trackingRectOwner = nil; 2957 return; 2958 } 2959 2960 if (tag == _data->_lastToolTipTag) { 2961 [super removeTrackingRect:tag]; 2962 _data->_lastToolTipTag = 0; 2963 return; 2964 } 2965 2966 // If any other tracking rect is being removed, we don't know how it was created 2967 // and it's possible there's a leak involved (see 3500217) 2968 ASSERT_NOT_REACHED(); 2969} 2970 2971- (void)_removeTrackingRects:(NSTrackingRectTag *)tags count:(int)count 2972{ 2973 int i; 2974 for (i = 0; i < count; ++i) { 2975 int tag = tags[i]; 2976 if (tag == 0) 2977 continue; 2978 ASSERT(tag == TRACKING_RECT_TAG); 2979 if (_data != nil) { 2980 _data->_trackingRectOwner = nil; 2981 } 2982 } 2983} 2984 2985- (void)_sendToolTipMouseExited 2986{ 2987 // Nothing matters except window, trackingNumber, and userData. 2988 NSEvent *fakeEvent = [NSEvent enterExitEventWithType:NSMouseExited 2989 location:NSMakePoint(0, 0) 2990 modifierFlags:0 2991 timestamp:0 2992 windowNumber:[[self window] windowNumber] 2993 context:NULL 2994 eventNumber:0 2995 trackingNumber:TRACKING_RECT_TAG 2996 userData:_data->_trackingRectUserData]; 2997 [_data->_trackingRectOwner mouseExited:fakeEvent]; 2998} 2999 3000- (void)_sendToolTipMouseEntered 3001{ 3002 // Nothing matters except window, trackingNumber, and userData. 3003 NSEvent *fakeEvent = [NSEvent enterExitEventWithType:NSMouseEntered 3004 location:NSMakePoint(0, 0) 3005 modifierFlags:0 3006 timestamp:0 3007 windowNumber:[[self window] windowNumber] 3008 context:NULL 3009 eventNumber:0 3010 trackingNumber:TRACKING_RECT_TAG 3011 userData:_data->_trackingRectUserData]; 3012 [_data->_trackingRectOwner mouseEntered:fakeEvent]; 3013} 3014 3015- (NSString *)view:(NSView *)view stringForToolTip:(NSToolTipTag)tag point:(NSPoint)point userData:(void *)data 3016{ 3017 return nsStringFromWebCoreString(_data->_page->toolTip()); 3018} 3019 3020- (void)_toolTipChangedFrom:(NSString *)oldToolTip to:(NSString *)newToolTip 3021{ 3022 if (oldToolTip) 3023 [self _sendToolTipMouseExited]; 3024 3025 if (newToolTip && [newToolTip length] > 0) { 3026 // See radar 3500217 for why we remove all tooltips rather than just the single one we created. 3027 [self removeAllToolTips]; 3028 NSRect wideOpenRect = NSMakeRect(-100000, -100000, 200000, 200000); 3029 _data->_lastToolTipTag = [self addToolTipRect:wideOpenRect owner:self userData:NULL]; 3030 [self _sendToolTipMouseEntered]; 3031 } 3032} 3033 3034- (void)_setFindIndicator:(PassRefPtr<FindIndicator>)findIndicator fadeOut:(BOOL)fadeOut animate:(BOOL)animate 3035{ 3036 if (!findIndicator) { 3037 _data->_findIndicatorWindow = nullptr; 3038 return; 3039 } 3040 3041 if (!_data->_findIndicatorWindow) 3042 _data->_findIndicatorWindow = std::make_unique<FindIndicatorWindow>(self); 3043 3044 _data->_findIndicatorWindow->setFindIndicator(findIndicator, fadeOut, animate); 3045} 3046 3047- (CALayer *)_rootLayer 3048{ 3049 return [_data->_layerHostingView layer]; 3050} 3051 3052- (void)_setAcceleratedCompositingModeRootLayer:(CALayer *)rootLayer 3053{ 3054 [rootLayer web_disableAllActions]; 3055 3056 _data->_rootLayer = rootLayer; 3057 3058#if WK_API_ENABLED 3059 if (_data->_thumbnailView) { 3060 [self _updateThumbnailViewLayer]; 3061 return; 3062 } 3063#endif 3064 3065 [CATransaction begin]; 3066 [CATransaction setDisableActions:YES]; 3067 3068 if (rootLayer) { 3069 if (!_data->_layerHostingView) { 3070 // Create an NSView that will host our layer tree. 3071 _data->_layerHostingView = adoptNS([[WKFlippedView alloc] initWithFrame:[self bounds]]); 3072 [_data->_layerHostingView setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; 3073 3074 3075 [self addSubview:_data->_layerHostingView.get() positioned:NSWindowBelow relativeTo:nil]; 3076 3077 // Create a root layer that will back the NSView. 3078 RetainPtr<CALayer> layer = adoptNS([[CALayer alloc] init]); 3079 [layer setDelegate:[WebActionDisablingCALayerDelegate shared]]; 3080#ifndef NDEBUG 3081 [layer setName:@"Hosting root layer"]; 3082#endif 3083 3084 [_data->_layerHostingView setLayer:layer.get()]; 3085 [_data->_layerHostingView setWantsLayer:YES]; 3086 } 3087 3088 [_data->_layerHostingView layer].sublayers = [NSArray arrayWithObject:rootLayer]; 3089 } else { 3090 if (_data->_layerHostingView) { 3091 [_data->_layerHostingView removeFromSuperview]; 3092 [_data->_layerHostingView setLayer:nil]; 3093 [_data->_layerHostingView setWantsLayer:NO]; 3094 3095 _data->_layerHostingView = nullptr; 3096 } 3097 } 3098 3099 [CATransaction commit]; 3100} 3101 3102- (CALayer *)_acceleratedCompositingModeRootLayer 3103{ 3104 return _data->_rootLayer.get(); 3105} 3106 3107- (PassRefPtr<ViewSnapshot>)_takeViewSnapshot 3108{ 3109 NSWindow *window = self.window; 3110 3111 CGSWindowID windowID = (CGSWindowID)[window windowNumber]; 3112 if (!windowID || ![window isVisible]) 3113 return nullptr; 3114 3115 RetainPtr<CGImageRef> windowSnapshotImage = adoptCF(CGWindowListCreateImage(CGRectNull, kCGWindowListOptionIncludingWindow, windowID, kCGWindowImageBoundsIgnoreFraming | kCGWindowImageShouldBeOpaque)); 3116 3117 // Work around <rdar://problem/17084993>; re-request the snapshot at kCGWindowImageNominalResolution if it was captured at the wrong scale. 3118 CGFloat desiredSnapshotWidth = window.frame.size.width * window.screen.backingScaleFactor; 3119 if (CGImageGetWidth(windowSnapshotImage.get()) != desiredSnapshotWidth) 3120 windowSnapshotImage = adoptCF(CGWindowListCreateImage(CGRectNull, kCGWindowListOptionIncludingWindow, windowID, kCGWindowImageBoundsIgnoreFraming | kCGWindowImageShouldBeOpaque | kCGWindowImageNominalResolution)); 3121 3122 [self _ensureGestureController]; 3123 3124 NSRect windowCaptureRect; 3125 FloatRect boundsForCustomSwipeViews = _data->_gestureController->windowRelativeBoundsForCustomSwipeViews(); 3126 if (!boundsForCustomSwipeViews.isEmpty()) 3127 windowCaptureRect = boundsForCustomSwipeViews; 3128 else { 3129 NSRect unobscuredBounds = self.bounds; 3130 float topContentInset = _data->_page->topContentInset(); 3131 unobscuredBounds.origin.y += topContentInset; 3132 unobscuredBounds.size.height -= topContentInset; 3133 windowCaptureRect = [self convertRect:unobscuredBounds toView:nil]; 3134 } 3135 3136 NSRect windowCaptureScreenRect = [window convertRectToScreen:windowCaptureRect]; 3137 CGRect windowScreenRect; 3138 CGSGetScreenRectForWindow(CGSMainConnectionID(), (CGSWindowID)[window windowNumber], &windowScreenRect); 3139 3140 NSRect croppedImageRect = windowCaptureRect; 3141 croppedImageRect.origin.y = windowScreenRect.size.height - windowCaptureScreenRect.size.height - NSMinY(windowCaptureRect); 3142 3143 auto croppedSnapshotImage = adoptCF(CGImageCreateWithImageInRect(windowSnapshotImage.get(), NSRectToCGRect([window convertRectToBacking:croppedImageRect]))); 3144 3145 auto surface = IOSurface::createFromImage(croppedSnapshotImage.get()); 3146 if (!surface) 3147 return nullptr; 3148 surface->setIsVolatile(true); 3149 3150 return ViewSnapshot::create(surface.get(), surface->size(), surface->totalBytes()); 3151} 3152 3153- (void)_wheelEventWasNotHandledByWebCore:(NSEvent *)event 3154{ 3155 if (_data->_gestureController) 3156 _data->_gestureController->wheelEventWasNotHandledByWebCore(event); 3157} 3158 3159- (void)_setAccessibilityWebProcessToken:(NSData *)data 3160{ 3161 _data->_remoteAccessibilityChild = WKAXRemoteElementForToken(data); 3162 [self _updateRemoteAccessibilityRegistration:YES]; 3163} 3164 3165- (void)_pluginFocusOrWindowFocusChanged:(BOOL)pluginHasFocusAndWindowHasFocus pluginComplexTextInputIdentifier:(uint64_t)pluginComplexTextInputIdentifier 3166{ 3167 BOOL inputSourceChanged = _data->_pluginComplexTextInputIdentifier; 3168 3169 if (pluginHasFocusAndWindowHasFocus) { 3170 // Check if we're already allowing text input for this plug-in. 3171 if (pluginComplexTextInputIdentifier == _data->_pluginComplexTextInputIdentifier) 3172 return; 3173 3174 _data->_pluginComplexTextInputIdentifier = pluginComplexTextInputIdentifier; 3175 3176 } else { 3177 // Check if we got a request to unfocus a plug-in that isn't focused. 3178 if (pluginComplexTextInputIdentifier != _data->_pluginComplexTextInputIdentifier) 3179 return; 3180 3181 _data->_pluginComplexTextInputIdentifier = 0; 3182 } 3183 3184 if (inputSourceChanged) { 3185 // The input source changed, go ahead and discard any entered text. 3186 [[WKTextInputWindowController sharedTextInputWindowController] unmarkText]; 3187 } 3188 3189 // This will force the current input context to be updated to its correct value. 3190 [NSApp updateWindows]; 3191} 3192 3193- (void)_setPluginComplexTextInputState:(PluginComplexTextInputState)pluginComplexTextInputState pluginComplexTextInputIdentifier:(uint64_t)pluginComplexTextInputIdentifier 3194{ 3195 if (pluginComplexTextInputIdentifier != _data->_pluginComplexTextInputIdentifier) { 3196 // We're asked to update the state for a plug-in that doesn't have focus. 3197 return; 3198 } 3199 3200 [self _setPluginComplexTextInputState:pluginComplexTextInputState]; 3201} 3202 3203- (void)_setDragImage:(NSImage *)image at:(NSPoint)clientPoint linkDrag:(BOOL)linkDrag 3204{ 3205 IntSize size([image size]); 3206 size.scale(1.0 / _data->_page->deviceScaleFactor()); 3207 [image setSize:size]; 3208 3209 // The call below could release this WKView. 3210 RetainPtr<WKView> protector(self); 3211 3212#pragma clang diagnostic push 3213#pragma clang diagnostic ignored "-Wdeprecated-declarations" 3214 [self dragImage:image 3215 at:clientPoint 3216 offset:NSZeroSize 3217 event:(linkDrag) ? [NSApp currentEvent] :_data->_mouseDownEvent 3218 pasteboard:[NSPasteboard pasteboardWithName:NSDragPboard] 3219 source:self 3220 slideBack:YES]; 3221#pragma clang diagnostic pop 3222} 3223 3224static bool matchesExtensionOrEquivalent(NSString *filename, NSString *extension) 3225{ 3226 NSString *extensionAsSuffix = [@"." stringByAppendingString:extension]; 3227 return hasCaseInsensitiveSuffix(filename, extensionAsSuffix) || (stringIsCaseInsensitiveEqualToString(extension, @"jpeg") 3228 && hasCaseInsensitiveSuffix(filename, @".jpg")); 3229} 3230 3231- (void)_setPromisedData:(WebCore::Image *)image withFileName:(NSString *)filename withExtension:(NSString *)extension withTitle:(NSString *)title withURL:(NSString *)url withVisibleURL:(NSString *)visibleUrl withArchive:(WebCore::SharedBuffer*) archiveBuffer forPasteboard:(NSString *)pasteboardName 3232 3233{ 3234 NSPasteboard *pasteboard = [NSPasteboard pasteboardWithName:pasteboardName]; 3235 RetainPtr<NSMutableArray> types = adoptNS([[NSMutableArray alloc] initWithObjects:NSFilesPromisePboardType, nil]); 3236 3237 [types addObjectsFromArray:archiveBuffer ? PasteboardTypes::forImagesWithArchive() : PasteboardTypes::forImages()]; 3238 [pasteboard declareTypes:types.get() owner:self]; 3239 if (!matchesExtensionOrEquivalent(filename, extension)) 3240 filename = [[filename stringByAppendingString:@"."] stringByAppendingString:extension]; 3241 3242 [pasteboard setString:visibleUrl forType:NSStringPboardType]; 3243 [pasteboard setString:visibleUrl forType:PasteboardTypes::WebURLPboardType]; 3244 [pasteboard setString:title forType:PasteboardTypes::WebURLNamePboardType]; 3245 [pasteboard setPropertyList:[NSArray arrayWithObjects:[NSArray arrayWithObject:visibleUrl], [NSArray arrayWithObject:title], nil] forType:PasteboardTypes::WebURLsWithTitlesPboardType]; 3246 [pasteboard setPropertyList:[NSArray arrayWithObject:extension] forType:NSFilesPromisePboardType]; 3247 3248 if (archiveBuffer) 3249 [pasteboard setData:archiveBuffer->createNSData().get() forType:PasteboardTypes::WebArchivePboardType]; 3250 3251 _data->_promisedImage = image; 3252 _data->_promisedFilename = filename; 3253 _data->_promisedURL = url; 3254} 3255 3256- (void)pasteboardChangedOwner:(NSPasteboard *)pasteboard 3257{ 3258 _data->_promisedImage = 0; 3259 _data->_promisedFilename = ""; 3260 _data->_promisedURL = ""; 3261} 3262 3263- (void)pasteboard:(NSPasteboard *)pasteboard provideDataForType:(NSString *)type 3264{ 3265 // FIXME: need to support NSRTFDPboardType 3266 3267 if ([type isEqual:NSTIFFPboardType] && _data->_promisedImage) { 3268 [pasteboard setData:(NSData *)_data->_promisedImage->getTIFFRepresentation() forType:NSTIFFPboardType]; 3269 _data->_promisedImage = 0; 3270 } 3271} 3272 3273static BOOL fileExists(NSString *path) 3274{ 3275 struct stat statBuffer; 3276 return !lstat([path fileSystemRepresentation], &statBuffer); 3277} 3278 3279static NSString *pathWithUniqueFilenameForPath(NSString *path) 3280{ 3281 // "Fix" the filename of the path. 3282 NSString *filename = filenameByFixingIllegalCharacters([path lastPathComponent]); 3283 path = [[path stringByDeletingLastPathComponent] stringByAppendingPathComponent:filename]; 3284 3285 if (fileExists(path)) { 3286 // Don't overwrite existing file by appending "-n", "-n.ext" or "-n.ext.ext" to the filename. 3287 NSString *extensions = nil; 3288 NSString *pathWithoutExtensions; 3289 NSString *lastPathComponent = [path lastPathComponent]; 3290 NSRange periodRange = [lastPathComponent rangeOfString:@"."]; 3291 3292 if (periodRange.location == NSNotFound) { 3293 pathWithoutExtensions = path; 3294 } else { 3295 extensions = [lastPathComponent substringFromIndex:periodRange.location + 1]; 3296 lastPathComponent = [lastPathComponent substringToIndex:periodRange.location]; 3297 pathWithoutExtensions = [[path stringByDeletingLastPathComponent] stringByAppendingPathComponent:lastPathComponent]; 3298 } 3299 3300 for (unsigned i = 1; ; i++) { 3301 NSString *pathWithAppendedNumber = [NSString stringWithFormat:@"%@-%d", pathWithoutExtensions, i]; 3302 path = [extensions length] ? [pathWithAppendedNumber stringByAppendingPathExtension:extensions] : pathWithAppendedNumber; 3303 if (!fileExists(path)) 3304 break; 3305 } 3306 } 3307 3308 return path; 3309} 3310 3311- (NSArray *)namesOfPromisedFilesDroppedAtDestination:(NSURL *)dropDestination 3312{ 3313 RetainPtr<NSFileWrapper> wrapper; 3314 RetainPtr<NSData> data; 3315 3316 if (_data->_promisedImage) { 3317 data = _data->_promisedImage->data()->createNSData(); 3318 wrapper = adoptNS([[NSFileWrapper alloc] initRegularFileWithContents:data.get()]); 3319 [wrapper setPreferredFilename:_data->_promisedFilename]; 3320 } 3321 3322 if (!wrapper) { 3323 LOG_ERROR("Failed to create image file."); 3324 return nil; 3325 } 3326 3327 // FIXME: Report an error if we fail to create a file. 3328 NSString *path = [[dropDestination path] stringByAppendingPathComponent:[wrapper preferredFilename]]; 3329 path = pathWithUniqueFilenameForPath(path); 3330 if (![wrapper writeToURL:[NSURL fileURLWithPath:path] options:NSFileWrapperWritingWithNameUpdating originalContentsURL:nil error:nullptr]) 3331 LOG_ERROR("Failed to create image file via -[NSFileWrapper writeToURL:options:originalContentsURL:error:]"); 3332 3333 if (!_data->_promisedURL.isEmpty()) 3334 WebCore::setMetadataURL(_data->_promisedURL, "", String(path)); 3335 3336 return [NSArray arrayWithObject:[path lastPathComponent]]; 3337} 3338 3339- (void)_updateSecureInputState 3340{ 3341 if (![[self window] isKeyWindow] || ![self _isFocused]) { 3342 if (_data->_inSecureInputState) { 3343 DisableSecureEventInput(); 3344 _data->_inSecureInputState = NO; 3345 } 3346 return; 3347 } 3348 // WKView has a single input context for all editable areas (except for plug-ins). 3349 NSTextInputContext *context = [super inputContext]; 3350 bool isInPasswordField = _data->_page->editorState().isInPasswordField; 3351 3352 if (isInPasswordField) { 3353 if (!_data->_inSecureInputState) 3354 EnableSecureEventInput(); 3355 static NSArray *romanInputSources = [[NSArray alloc] initWithObjects:&NSAllRomanInputSourcesLocaleIdentifier count:1]; 3356 LOG(TextInput, "-> setAllowedInputSourceLocales:romanInputSources"); 3357 [context setAllowedInputSourceLocales:romanInputSources]; 3358 } else { 3359 if (_data->_inSecureInputState) 3360 DisableSecureEventInput(); 3361 LOG(TextInput, "-> setAllowedInputSourceLocales:nil"); 3362 [context setAllowedInputSourceLocales:nil]; 3363 } 3364 _data->_inSecureInputState = isInPasswordField; 3365} 3366 3367- (void)_resetSecureInputState 3368{ 3369 if (_data->_inSecureInputState) { 3370 DisableSecureEventInput(); 3371 _data->_inSecureInputState = NO; 3372 } 3373} 3374 3375- (void)_notifyInputContextAboutDiscardedComposition 3376{ 3377 // <rdar://problem/9359055>: -discardMarkedText can only be called for active contexts. 3378 // FIXME: We fail to ever notify the input context if something (e.g. a navigation) happens while the window is not key. 3379 // This is not a problem when the window is key, because we discard marked text on resigning first responder. 3380 if (![[self window] isKeyWindow] || self != [[self window] firstResponder]) 3381 return; 3382 3383 LOG(TextInput, "-> discardMarkedText"); 3384 [[super inputContext] discardMarkedText]; // Inform the input method that we won't have an inline input area despite having been asked to. 3385} 3386 3387#if ENABLE(FULLSCREEN_API) 3388- (BOOL)_hasFullScreenWindowController 3389{ 3390 return (bool)_data->_fullScreenWindowController; 3391} 3392 3393- (WKFullScreenWindowController *)_fullScreenWindowController 3394{ 3395 if (!_data->_fullScreenWindowController) 3396 _data->_fullScreenWindowController = adoptNS([[WKFullScreenWindowController alloc] initWithWindow:[self createFullScreenWindow] webView:self]); 3397 3398 return _data->_fullScreenWindowController.get(); 3399} 3400 3401- (void)_closeFullScreenWindowController 3402{ 3403 if (!_data->_fullScreenWindowController) 3404 return; 3405 3406 [_data->_fullScreenWindowController close]; 3407 _data->_fullScreenWindowController = nullptr; 3408} 3409#endif 3410 3411- (bool)_executeSavedCommandBySelector:(SEL)selector 3412{ 3413 LOG(TextInput, "Executing previously saved command %s", sel_getName(selector)); 3414 // The sink does two things: 1) Tells us if the responder went unhandled, and 3415 // 2) prevents any NSBeep; we don't ever want to beep here. 3416 RetainPtr<WKResponderChainSink> sink = adoptNS([[WKResponderChainSink alloc] initWithResponderChain:self]); 3417 [super doCommandBySelector:selector]; 3418 [sink detach]; 3419 return ![sink didReceiveUnhandledCommand]; 3420} 3421 3422- (void)_setIntrinsicContentSize:(NSSize)intrinsicContentSize 3423{ 3424 // If the intrinsic content size is less than the minimum layout width, the content flowed to fit, 3425 // so we can report that that dimension is flexible. If not, we need to report our intrinsic width 3426 // so that autolayout will know to provide space for us. 3427 3428 NSSize intrinsicContentSizeAcknowledgingFlexibleWidth = intrinsicContentSize; 3429 if (intrinsicContentSize.width < _data->_page->minimumLayoutSize().width()) 3430 intrinsicContentSizeAcknowledgingFlexibleWidth.width = NSViewNoInstrinsicMetric; 3431 3432 _data->_intrinsicContentSize = intrinsicContentSizeAcknowledgingFlexibleWidth; 3433 [self invalidateIntrinsicContentSize]; 3434} 3435 3436- (void)_cacheWindowBottomCornerRect 3437{ 3438 // FIXME: We should remove this code when <rdar://problem/9362085> is resolved. 3439 NSWindow *window = [self window]; 3440 if (!window) 3441 return; 3442 3443 _data->_windowBottomCornerIntersectionRect = [window _intersectBottomCornersWithRect:[self convertRect:[self visibleRect] toView:nil]]; 3444 if (!NSIsEmptyRect(_data->_windowBottomCornerIntersectionRect)) 3445 [self setNeedsDisplayInRect:[self convertRect:_data->_windowBottomCornerIntersectionRect fromView:nil]]; 3446} 3447 3448- (NSInteger)spellCheckerDocumentTag 3449{ 3450 if (!_data->_hasSpellCheckerDocumentTag) { 3451 _data->_spellCheckerDocumentTag = [NSSpellChecker uniqueSpellDocumentTag]; 3452 _data->_hasSpellCheckerDocumentTag = YES; 3453 } 3454 return _data->_spellCheckerDocumentTag; 3455} 3456 3457- (void)handleAcceptedAlternativeText:(NSString*)text 3458{ 3459 _data->_page->handleAlternativeTextUIResult(text); 3460} 3461 3462- (void)_setSuppressVisibilityUpdates:(BOOL)suppressVisibilityUpdates 3463{ 3464 _data->_page->setSuppressVisibilityUpdates(suppressVisibilityUpdates); 3465} 3466 3467- (BOOL)_suppressVisibilityUpdates 3468{ 3469 return _data->_page->suppressVisibilityUpdates(); 3470} 3471 3472- (instancetype)initWithFrame:(NSRect)frame context:(WebContext&)context configuration:(WebPageConfiguration)webPageConfiguration webView:(WKWebView *)webView 3473{ 3474 self = [super initWithFrame:frame]; 3475 if (!self) 3476 return nil; 3477 3478 [NSApp registerServicesMenuSendTypes:PasteboardTypes::forSelection() returnTypes:PasteboardTypes::forEditing()]; 3479 3480 InitializeWebKit2(); 3481 3482 // Legacy style scrollbars have design details that rely on tracking the mouse all the time. 3483 NSTrackingAreaOptions options = NSTrackingMouseMoved | NSTrackingMouseEnteredAndExited | NSTrackingInVisibleRect; 3484 if (WKRecommendedScrollerStyle() == NSScrollerStyleLegacy) 3485 options |= NSTrackingActiveAlways; 3486 else 3487 options |= NSTrackingActiveInKeyWindow; 3488 3489 NSTrackingArea *trackingArea = [[NSTrackingArea alloc] initWithRect:frame 3490 options:options 3491 owner:self 3492 userInfo:nil]; 3493 [self addTrackingArea:trackingArea]; 3494 [trackingArea release]; 3495 3496 _data = [[WKViewData alloc] init]; 3497 _data->_pageClient = std::make_unique<PageClientImpl>(self, webView); 3498 _data->_page = context.createWebPage(*_data->_pageClient, WTF::move(webPageConfiguration)); 3499 _data->_page->setAddsVisitedLinks(context.historyClient().addsVisitedLinks()); 3500 3501 _data->_page->setIntrinsicDeviceScaleFactor([self _intrinsicDeviceScaleFactor]); 3502 _data->_page->initializeWebPage(); 3503 3504 _data->_mouseDownEvent = nil; 3505 _data->_ignoringMouseDraggedEvents = NO; 3506 _data->_clipsToVisibleRect = NO; 3507 _data->_useContentPreparationRectForVisibleRect = NO; 3508 _data->_windowOcclusionDetectionEnabled = YES; 3509 3510 _data->_intrinsicContentSize = NSMakeSize(NSViewNoInstrinsicMetric, NSViewNoInstrinsicMetric); 3511 3512 _data->_needsViewFrameInWindowCoordinates = _data->_page->preferences().pluginsEnabled(); 3513 3514 [self _registerDraggedTypes]; 3515 3516 self.wantsLayer = YES; 3517 3518 // Explicitly set the layer contents placement so AppKit will make sure that our layer has masksToBounds set to YES. 3519 self.layerContentsPlacement = NSViewLayerContentsPlacementTopLeft; 3520 3521 WebContext::statistics().wkViewCount++; 3522 3523 NSNotificationCenter* workspaceNotificationCenter = [[NSWorkspace sharedWorkspace] notificationCenter]; 3524 [workspaceNotificationCenter addObserver:self selector:@selector(_activeSpaceDidChange:) name:NSWorkspaceActiveSpaceDidChangeNotification object:nil]; 3525 3526 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_applicationWillTerminate:) name:NSApplicationWillTerminateNotification object:NSApp]; 3527 3528 return self; 3529} 3530 3531- (void)_registerDraggedTypes 3532{ 3533 NSMutableSet *types = [[NSMutableSet alloc] initWithArray:PasteboardTypes::forEditing()]; 3534 [types addObjectsFromArray:PasteboardTypes::forURL()]; 3535 [self registerForDraggedTypes:[types allObjects]]; 3536 [types release]; 3537} 3538 3539#if WK_API_ENABLED 3540- (void)_setThumbnailView:(_WKThumbnailView *)thumbnailView 3541{ 3542 ASSERT(!_data->_thumbnailView || !thumbnailView); 3543 3544 _data->_thumbnailView = thumbnailView; 3545 3546 if (thumbnailView) 3547 [self _updateThumbnailViewLayer]; 3548 else 3549 [self _setAcceleratedCompositingModeRootLayer:_data->_rootLayer.get()]; 3550} 3551 3552- (_WKThumbnailView *)_thumbnailView 3553{ 3554 return _data->_thumbnailView; 3555} 3556 3557- (void)_updateThumbnailViewLayer 3558{ 3559 _WKThumbnailView *thumbnailView = _data->_thumbnailView; 3560 ASSERT(thumbnailView); 3561 3562 if (thumbnailView._waitingForSnapshot && self.window) 3563 [self _reparentLayerTreeInThumbnailView]; 3564} 3565 3566- (void)_reparentLayerTreeInThumbnailView 3567{ 3568 _data->_thumbnailView._thumbnailLayer = _data->_rootLayer.get(); 3569} 3570#endif // WK_API_ENABLED 3571 3572#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101000 3573 3574- (void)_updateContentInsetsIfAutomatic 3575{ 3576 if (!self._automaticallyAdjustsContentInsets) 3577 return; 3578 3579 if ((self.window.styleMask & NSFullSizeContentViewWindowMask) && !self.window.titlebarAppearsTransparent && ![self enclosingScrollView]) { 3580 NSRect contentLayoutRect = [self convertRect:self.window.contentLayoutRect fromView:nil]; 3581 CGFloat newTopContentInset = NSMaxY(contentLayoutRect) - NSHeight(contentLayoutRect); 3582 if (self._topContentInset != newTopContentInset) 3583 self._topContentInset = newTopContentInset; 3584 } else 3585 self._topContentInset = 0; 3586} 3587 3588- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context 3589{ 3590 if (context == keyValueObservingContext) { 3591 if ([keyPath isEqualToString:@"contentLayoutRect"] || [keyPath isEqualToString:@"titlebarAppearsTransparent"]) 3592 [self _updateContentInsetsIfAutomatic]; 3593 return; 3594 } 3595 3596 [super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; 3597} 3598 3599#endif 3600 3601- (void)_didFirstVisuallyNonEmptyLayoutForMainFrame 3602{ 3603 if (_data->_gestureController) 3604 _data->_gestureController->didFirstVisuallyNonEmptyLayoutForMainFrame(); 3605} 3606 3607- (void)_didFinishLoadForMainFrame 3608{ 3609 if (_data->_gestureController) 3610 _data->_gestureController->didFinishLoadForMainFrame(); 3611} 3612 3613- (void)_didSameDocumentNavigationForMainFrame:(SameDocumentNavigationType)type 3614{ 3615 if (_data->_gestureController) 3616 _data->_gestureController->didSameDocumentNavigationForMainFrame(type); 3617} 3618 3619- (void)_removeNavigationGestureSnapshot 3620{ 3621 if (_data->_gestureController) 3622 _data->_gestureController->removeSwipeSnapshot(); 3623} 3624 3625@end 3626 3627@implementation WKView (Private) 3628 3629- (void)saveBackForwardSnapshotForCurrentItem 3630{ 3631 _data->_page->recordNavigationSnapshot(); 3632} 3633 3634- (id)initWithFrame:(NSRect)frame contextRef:(WKContextRef)contextRef pageGroupRef:(WKPageGroupRef)pageGroupRef 3635{ 3636 return [self initWithFrame:frame contextRef:contextRef pageGroupRef:pageGroupRef relatedToPage:nil]; 3637} 3638 3639- (id)initWithFrame:(NSRect)frame contextRef:(WKContextRef)contextRef pageGroupRef:(WKPageGroupRef)pageGroupRef relatedToPage:(WKPageRef)relatedPage 3640{ 3641 WebPageConfiguration webPageConfiguration; 3642 webPageConfiguration.pageGroup = toImpl(pageGroupRef); 3643 webPageConfiguration.relatedPage = toImpl(relatedPage); 3644 3645 return [self initWithFrame:frame context:*toImpl(contextRef) configuration:webPageConfiguration webView:nil]; 3646} 3647 3648#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1080 3649- (BOOL)wantsUpdateLayer 3650{ 3651 return YES; 3652} 3653 3654- (void)updateLayer 3655{ 3656 if ([self drawsBackground] && ![self drawsTransparentBackground]) 3657 self.layer.backgroundColor = CGColorGetConstantColor(kCGColorWhite); 3658 else 3659 self.layer.backgroundColor = CGColorGetConstantColor(kCGColorClear); 3660 3661 // If asynchronous geometry updates have been sent by forceAsyncDrawingAreaSizeUpdate, 3662 // then subsequent calls to setFrameSize should not result in us waiting for the did 3663 // udpate response if setFrameSize is called. 3664 if ([self frameSizeUpdatesDisabled]) 3665 return; 3666 3667 if (DrawingAreaProxy* drawingArea = _data->_page->drawingArea()) 3668 drawingArea->waitForPossibleGeometryUpdate(); 3669} 3670#endif 3671 3672- (WKPageRef)pageRef 3673{ 3674 return toAPI(_data->_page.get()); 3675} 3676 3677- (BOOL)canChangeFrameLayout:(WKFrameRef)frameRef 3678{ 3679 // PDF documents are already paginated, so we can't change them to add headers and footers. 3680 return !toImpl(frameRef)->isDisplayingPDFDocument(); 3681} 3682 3683- (NSPrintOperation *)printOperationWithPrintInfo:(NSPrintInfo *)printInfo forFrame:(WKFrameRef)frameRef 3684{ 3685 LOG(View, "Creating an NSPrintOperation for frame '%s'", toImpl(frameRef)->url().utf8().data()); 3686 3687 // FIXME: If the frame cannot be printed (e.g. if it contains an encrypted PDF that disallows 3688 // printing), this function should return nil. 3689 RetainPtr<WKPrintingView> printingView = adoptNS([[WKPrintingView alloc] initWithFrameProxy:toImpl(frameRef) view:self]); 3690 // NSPrintOperation takes ownership of the view. 3691 NSPrintOperation *printOperation = [NSPrintOperation printOperationWithView:printingView.get() printInfo:printInfo]; 3692 [printOperation setCanSpawnSeparateThread:YES]; 3693 [printOperation setJobTitle:toImpl(frameRef)->title()]; 3694 printingView->_printOperation = printOperation; 3695 return printOperation; 3696} 3697 3698- (void)setFrame:(NSRect)rect andScrollBy:(NSSize)offset 3699{ 3700 ASSERT(NSEqualSizes(_data->_resizeScrollOffset, NSZeroSize)); 3701 3702 _data->_resizeScrollOffset = offset; 3703 [self setFrame:rect]; 3704} 3705 3706- (void)disableFrameSizeUpdates 3707{ 3708 _data->_frameSizeUpdatesDisabledCount++; 3709} 3710 3711- (void)enableFrameSizeUpdates 3712{ 3713 if (!_data->_frameSizeUpdatesDisabledCount) 3714 return; 3715 3716 if (!(--_data->_frameSizeUpdatesDisabledCount)) { 3717 if (_data->_clipsToVisibleRect) 3718 [self _updateViewExposedRect]; 3719 [self _setDrawingAreaSize:[self frame].size]; 3720 } 3721} 3722 3723- (BOOL)frameSizeUpdatesDisabled 3724{ 3725 return _data->_frameSizeUpdatesDisabledCount > 0; 3726} 3727 3728+ (void)hideWordDefinitionWindow 3729{ 3730 WKHideWordDefinitionWindow(); 3731} 3732 3733- (CGFloat)minimumLayoutWidth 3734{ 3735 static BOOL loggedDeprecationWarning = NO; 3736 3737 if (!loggedDeprecationWarning) { 3738 NSLog(@"Please use minimumSizeForAutoLayout instead of minimumLayoutWidth."); 3739 loggedDeprecationWarning = YES; 3740 } 3741 3742 return self.minimumSizeForAutoLayout.width; 3743} 3744 3745- (void)setMinimumLayoutWidth:(CGFloat)minimumLayoutWidth 3746{ 3747 static BOOL loggedDeprecationWarning = NO; 3748 3749 if (!loggedDeprecationWarning) { 3750 NSLog(@"Please use setMinimumSizeForAutoLayout: instead of setMinimumLayoutWidth:."); 3751 loggedDeprecationWarning = YES; 3752 } 3753 3754 [self setMinimumWidthForAutoLayout:minimumLayoutWidth]; 3755} 3756 3757- (CGFloat)minimumWidthForAutoLayout 3758{ 3759 return self.minimumSizeForAutoLayout.width; 3760} 3761 3762- (void)setMinimumWidthForAutoLayout:(CGFloat)minimumLayoutWidth 3763{ 3764 self.minimumSizeForAutoLayout = NSMakeSize(minimumLayoutWidth, self.minimumSizeForAutoLayout.height); 3765} 3766 3767- (NSSize)minimumSizeForAutoLayout 3768{ 3769 return _data->_page->minimumLayoutSize(); 3770} 3771 3772- (void)setMinimumSizeForAutoLayout:(NSSize)minimumSizeForAutoLayout 3773{ 3774 BOOL expandsToFit = minimumSizeForAutoLayout.width > 0; 3775 3776 _data->_page->setMinimumLayoutSize(IntSize(minimumSizeForAutoLayout.width, minimumSizeForAutoLayout.height)); 3777 _data->_page->setMainFrameIsScrollable(!expandsToFit); 3778 3779 [self setShouldClipToVisibleRect:expandsToFit]; 3780} 3781 3782- (BOOL)shouldExpandToViewHeightForAutoLayout 3783{ 3784 return _data->_page->autoSizingShouldExpandToViewHeight(); 3785} 3786 3787- (void)setShouldExpandToViewHeightForAutoLayout:(BOOL)shouldExpand 3788{ 3789 return _data->_page->setAutoSizingShouldExpandToViewHeight(shouldExpand); 3790} 3791 3792- (BOOL)shouldClipToVisibleRect 3793{ 3794 return _data->_clipsToVisibleRect; 3795} 3796 3797- (void)setShouldClipToVisibleRect:(BOOL)clipsToVisibleRect 3798{ 3799 _data->_clipsToVisibleRect = clipsToVisibleRect; 3800 [self _updateViewExposedRect]; 3801} 3802 3803- (NSColor *)underlayColor 3804{ 3805 Color webColor = _data->_page->underlayColor(); 3806 if (!webColor.isValid()) 3807 return nil; 3808 3809 return nsColor(webColor); 3810} 3811 3812- (void)setUnderlayColor:(NSColor *)underlayColor 3813{ 3814 _data->_page->setUnderlayColor(colorFromNSColor(underlayColor)); 3815} 3816 3817- (NSView *)fullScreenPlaceholderView 3818{ 3819#if ENABLE(FULLSCREEN_API) 3820 if (_data->_fullScreenWindowController && [_data->_fullScreenWindowController isFullScreen]) 3821 return [_data->_fullScreenWindowController webViewPlaceholder]; 3822#endif 3823 return nil; 3824} 3825 3826- (NSWindow *)createFullScreenWindow 3827{ 3828#if ENABLE(FULLSCREEN_API) 3829#if __MAC_OS_X_VERSION_MIN_REQUIRED <= 1080 3830 NSRect contentRect = NSZeroRect; 3831#else 3832 NSRect contentRect = [[NSScreen mainScreen] frame]; 3833#endif 3834 return [[[WebCoreFullScreenWindow alloc] initWithContentRect:contentRect styleMask:(NSBorderlessWindowMask | NSResizableWindowMask) backing:NSBackingStoreBuffered defer:NO] autorelease]; 3835#else 3836 return nil; 3837#endif 3838} 3839 3840// FIXME: All of these "DeferringViewInWindowChanges" methods should be able to be removed once clients are weaned off of them. 3841- (void)beginDeferringViewInWindowChanges 3842{ 3843 if (_data->_shouldDeferViewInWindowChanges) { 3844 NSLog(@"beginDeferringViewInWindowChanges was called while already deferring view-in-window changes!"); 3845 return; 3846 } 3847 3848 _data->_shouldDeferViewInWindowChanges = YES; 3849} 3850 3851- (void)endDeferringViewInWindowChanges 3852{ 3853 if (!_data->_shouldDeferViewInWindowChanges) { 3854 NSLog(@"endDeferringViewInWindowChanges was called without beginDeferringViewInWindowChanges!"); 3855 return; 3856 } 3857 3858 _data->_shouldDeferViewInWindowChanges = NO; 3859 3860 if (_data->_viewInWindowChangeWasDeferred) { 3861 [self _dispatchSetTopContentInset]; 3862 _data->_page->viewStateDidChange(ViewState::IsInWindow); 3863 _data->_viewInWindowChangeWasDeferred = NO; 3864 } 3865} 3866 3867- (void)endDeferringViewInWindowChangesSync 3868{ 3869 if (!_data->_shouldDeferViewInWindowChanges) { 3870 NSLog(@"endDeferringViewInWindowChangesSync was called without beginDeferringViewInWindowChanges!"); 3871 return; 3872 } 3873 3874 _data->_shouldDeferViewInWindowChanges = NO; 3875 3876 if (_data->_viewInWindowChangeWasDeferred) { 3877 [self _dispatchSetTopContentInset]; 3878 _data->_page->viewStateDidChange(ViewState::IsInWindow); 3879 _data->_viewInWindowChangeWasDeferred = NO; 3880 } 3881} 3882 3883- (BOOL)isDeferringViewInWindowChanges 3884{ 3885 return _data->_shouldDeferViewInWindowChanges; 3886} 3887 3888- (BOOL)windowOcclusionDetectionEnabled 3889{ 3890 return _data->_windowOcclusionDetectionEnabled; 3891} 3892 3893- (void)setWindowOcclusionDetectionEnabled:(BOOL)flag 3894{ 3895 _data->_windowOcclusionDetectionEnabled = flag; 3896} 3897 3898- (void)setAllowsBackForwardNavigationGestures:(BOOL)allowsBackForwardNavigationGestures 3899{ 3900 _data->_allowsBackForwardNavigationGestures = allowsBackForwardNavigationGestures; 3901 _data->_page->setShouldRecordNavigationSnapshots(allowsBackForwardNavigationGestures); 3902 _data->_page->setShouldUseImplicitRubberBandControl(allowsBackForwardNavigationGestures); 3903} 3904 3905- (BOOL)allowsBackForwardNavigationGestures 3906{ 3907 return _data->_allowsBackForwardNavigationGestures; 3908} 3909 3910- (void)_dispatchSetTopContentInset 3911{ 3912 if (!_data->_didScheduleSetTopContentInset) 3913 return; 3914 3915 _data->_didScheduleSetTopContentInset = NO; 3916 _data->_page->setTopContentInset(_data->_topContentInset); 3917} 3918 3919- (void)_setTopContentInset:(CGFloat)contentInset 3920{ 3921 _data->_topContentInset = contentInset; 3922 3923 if (_data->_didScheduleSetTopContentInset) 3924 return; 3925 3926 _data->_didScheduleSetTopContentInset = YES; 3927 3928 dispatch_async(dispatch_get_main_queue(), ^{ 3929 [self _dispatchSetTopContentInset]; 3930 }); 3931} 3932 3933- (CGFloat)_topContentInset 3934{ 3935 return _data->_topContentInset; 3936} 3937 3938- (NSColor *)_pageExtendedBackgroundColor 3939{ 3940 WebCore::Color color = _data->_page->pageExtendedBackgroundColor(); 3941 if (!color.isValid()) 3942 return nil; 3943 3944 return nsColor(color); 3945} 3946 3947// This method forces a drawing area geometry update, even if frame size updates are disabled. 3948// The updated is performed asynchronously; we don't wait for the geometry update before returning. 3949// The area drawn need not match the current frame size - if it differs it will be anchored to the 3950// frame according to the current contentAnchor. 3951- (void)forceAsyncDrawingAreaSizeUpdate:(NSSize)size 3952{ 3953 if (_data->_clipsToVisibleRect) 3954 [self _updateViewExposedRect]; 3955 [self _setDrawingAreaSize:size]; 3956 3957 // If a geometry update is pending the new update won't be sent. Poll without waiting for any 3958 // pending did-update message now, such that the new update can be sent. We do so after setting 3959 // the drawing area size such that the latest update is sent. 3960 if (DrawingAreaProxy* drawingArea = _data->_page->drawingArea()) 3961 drawingArea->waitForPossibleGeometryUpdate(std::chrono::milliseconds::zero()); 3962} 3963 3964- (void)waitForAsyncDrawingAreaSizeUpdate 3965{ 3966 if (DrawingAreaProxy* drawingArea = _data->_page->drawingArea()) { 3967 // If a geometry update is still pending then the action of receiving the 3968 // first geometry update may result in another update being scheduled - 3969 // we should wait for this to complete too. 3970 drawingArea->waitForPossibleGeometryUpdate(DrawingAreaProxy::didUpdateBackingStoreStateTimeout() / 2); 3971 drawingArea->waitForPossibleGeometryUpdate(DrawingAreaProxy::didUpdateBackingStoreStateTimeout() / 2); 3972 } 3973} 3974 3975- (BOOL)isUsingUISideCompositing 3976{ 3977 if (DrawingAreaProxy* drawingArea = _data->_page->drawingArea()) 3978 return drawingArea->type() == DrawingAreaTypeRemoteLayerTree; 3979 3980 return NO; 3981} 3982 3983- (void)setAllowsMagnification:(BOOL)allowsMagnification 3984{ 3985 _data->_allowsMagnification = allowsMagnification; 3986} 3987 3988- (BOOL)allowsMagnification 3989{ 3990 return _data->_allowsMagnification; 3991} 3992 3993- (void)magnifyWithEvent:(NSEvent *)event 3994{ 3995 if (!_data->_allowsMagnification) { 3996 [super magnifyWithEvent:event]; 3997 return; 3998 } 3999 4000 [self _ensureGestureController]; 4001 4002 _data->_gestureController->handleMagnificationGesture(event.magnification, [self convertPoint:event.locationInWindow fromView:nil]); 4003} 4004 4005- (void)smartMagnifyWithEvent:(NSEvent *)event 4006{ 4007 if (!_data->_allowsMagnification) { 4008 [super smartMagnifyWithEvent:event]; 4009 return; 4010 } 4011 4012 [self _ensureGestureController]; 4013 4014 _data->_gestureController->handleSmartMagnificationGesture([self convertPoint:event.locationInWindow fromView:nil]); 4015} 4016 4017-(void)endGestureWithEvent:(NSEvent *)event 4018{ 4019 if (!_data->_gestureController) { 4020 [super endGestureWithEvent:event]; 4021 return; 4022 } 4023 4024 _data->_gestureController->endActiveGesture(); 4025} 4026 4027- (void)setMagnification:(double)magnification centeredAtPoint:(NSPoint)point 4028{ 4029 _data->_page->scalePageInViewCoordinates(magnification, roundedIntPoint(point)); 4030} 4031 4032- (void)setMagnification:(double)magnification 4033{ 4034 FloatPoint viewCenter(NSMidX([self bounds]), NSMidY([self bounds])); 4035 _data->_page->scalePageInViewCoordinates(magnification, roundedIntPoint(viewCenter)); 4036} 4037 4038- (double)magnification 4039{ 4040 if (_data->_gestureController) 4041 return _data->_gestureController->magnification(); 4042 4043 return _data->_page->pageScaleFactor(); 4044} 4045 4046- (void)_setCustomSwipeViews:(NSArray *)customSwipeViews 4047{ 4048 if (!customSwipeViews.count && !_data->_gestureController) 4049 return; 4050 4051 [self _ensureGestureController]; 4052 4053 Vector<RetainPtr<NSView>> views; 4054 for (NSView *view in customSwipeViews) 4055 views.append(view); 4056 4057 _data->_gestureController->setCustomSwipeViews(views); 4058} 4059 4060- (void)_setCustomSwipeViewsTopContentInset:(float)topContentInset 4061{ 4062 [self _ensureGestureController]; 4063 _data->_gestureController->setCustomSwipeViewsTopContentInset(topContentInset); 4064} 4065 4066- (BOOL)_tryToSwipeWithEvent:(NSEvent *)event ignoringPinnedState:(BOOL)ignoringPinnedState 4067{ 4068 if (!_data->_allowsBackForwardNavigationGestures) 4069 return NO; 4070 4071 [self _ensureGestureController]; 4072 4073 BOOL wasIgnoringPinnedState = _data->_gestureController->shouldIgnorePinnedState(); 4074 _data->_gestureController->setShouldIgnorePinnedState(ignoringPinnedState); 4075 4076 BOOL handledEvent = _data->_gestureController->handleScrollWheelEvent(event); 4077 4078 _data->_gestureController->setShouldIgnorePinnedState(wasIgnoringPinnedState); 4079 4080 return handledEvent; 4081} 4082 4083- (void)_setDidMoveSwipeSnapshotCallback:(void(^)(CGRect))callback 4084{ 4085 if (!_data->_allowsBackForwardNavigationGestures) 4086 return; 4087 4088 [self _ensureGestureController]; 4089 _data->_gestureController->setDidMoveSwipeSnapshotCallback(callback); 4090} 4091 4092#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101000 4093 4094- (void)_setAutomaticallyAdjustsContentInsets:(BOOL)automaticallyAdjustsContentInsets 4095{ 4096 _data->_automaticallyAdjustsContentInsets = automaticallyAdjustsContentInsets; 4097 [self _updateContentInsetsIfAutomatic]; 4098} 4099 4100- (BOOL)_automaticallyAdjustsContentInsets 4101{ 4102 return _data->_automaticallyAdjustsContentInsets; 4103} 4104 4105#endif 4106 4107@end 4108 4109@implementation WKResponderChainSink 4110 4111- (id)initWithResponderChain:(NSResponder *)chain 4112{ 4113 self = [super init]; 4114 if (!self) 4115 return nil; 4116 _lastResponderInChain = chain; 4117 while (NSResponder *next = [_lastResponderInChain nextResponder]) 4118 _lastResponderInChain = next; 4119 [_lastResponderInChain setNextResponder:self]; 4120 return self; 4121} 4122 4123- (void)detach 4124{ 4125 // This assumes that the responder chain was either unmodified since 4126 // -initWithResponderChain: was called, or was modified in such a way 4127 // that _lastResponderInChain is still in the chain, and self was not 4128 // moved earlier in the chain than _lastResponderInChain. 4129 NSResponder *responderBeforeSelf = _lastResponderInChain; 4130 NSResponder *next = [responderBeforeSelf nextResponder]; 4131 for (; next && next != self; next = [next nextResponder]) 4132 responderBeforeSelf = next; 4133 4134 // Nothing to be done if we are no longer in the responder chain. 4135 if (next != self) 4136 return; 4137 4138 [responderBeforeSelf setNextResponder:[self nextResponder]]; 4139 _lastResponderInChain = nil; 4140} 4141 4142- (bool)didReceiveUnhandledCommand 4143{ 4144 return _didReceiveUnhandledCommand; 4145} 4146 4147- (void)noResponderFor:(SEL)selector 4148{ 4149 _didReceiveUnhandledCommand = true; 4150} 4151 4152- (void)doCommandBySelector:(SEL)selector 4153{ 4154 _didReceiveUnhandledCommand = true; 4155} 4156 4157- (BOOL)tryToPerform:(SEL)action with:(id)object 4158{ 4159 _didReceiveUnhandledCommand = true; 4160 return YES; 4161} 4162 4163@end 4164 4165#endif // PLATFORM(MAC) 4166