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