WKView.mm revision 2fc2651226baac27029e38c9d6ef883fa32084db
1/* 2 * Copyright (C) 2010 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#import "ChunkedUpdateDrawingAreaProxy.h" 30#import "DataReference.h" 31#import "DrawingAreaProxyImpl.h" 32#import "FindIndicator.h" 33#import "FindIndicatorWindow.h" 34#import "LayerBackedDrawingAreaProxy.h" 35#import "LayerTreeContext.h" 36#import "Logging.h" 37#import "NativeWebKeyboardEvent.h" 38#import "PDFViewController.h" 39#import "PageClientImpl.h" 40#import "PasteboardTypes.h" 41#import "Region.h" 42#import "RunLoop.h" 43#import "TextChecker.h" 44#import "TextCheckerState.h" 45#import "WKAPICast.h" 46#import "WKPrintingView.h" 47#import "WKStringCF.h" 48#import "WKTextInputWindowController.h" 49#import "WKViewInternal.h" 50#import "WKViewPrivate.h" 51#import "WebContext.h" 52#import "WebEventFactory.h" 53#import "WebPage.h" 54#import "WebPageProxy.h" 55#import "WebProcessManager.h" 56#import "WebProcessProxy.h" 57#import "WebSystemInterface.h" 58#import <QuartzCore/QuartzCore.h> 59#import <WebCore/ColorMac.h> 60#import <WebCore/DragController.h> 61#import <WebCore/DragData.h> 62#import <WebCore/FloatRect.h> 63#import <WebCore/IntRect.h> 64#import <WebCore/KeyboardEvent.h> 65#import <WebCore/PlatformMouseEvent.h> 66#import <WebCore/PlatformScreen.h> 67#import <WebKitSystemInterface.h> 68#import <wtf/RefPtr.h> 69#import <wtf/RetainPtr.h> 70 71// FIXME (WebKit2) <rdar://problem/8728860> WebKit2 needs to be localized 72#define UI_STRING(__str, __desc) [NSString stringWithUTF8String:__str] 73 74@interface NSApplication (Details) 75- (void)speakString:(NSString *)string; 76@end 77 78@interface NSWindow (Details) 79- (NSRect)_growBoxRect; 80- (void)_setShowOpaqueGrowBoxForOwner:(id)owner; 81- (BOOL)_updateGrowBoxForWindowFrameChange; 82@end 83 84extern "C" { 85 // Need to declare this attribute name because AppKit exports it but does not make it available in API or SPI headers. 86 // <rdar://problem/8631468> tracks the request to make it available. This code should be removed when the bug is closed. 87 extern NSString *NSTextInputReplacementRangeAttributeName; 88} 89 90using namespace WebKit; 91using namespace WebCore; 92 93namespace WebKit { 94 95typedef id <NSValidatedUserInterfaceItem> ValidationItem; 96typedef Vector<RetainPtr<ValidationItem> > ValidationVector; 97typedef HashMap<String, ValidationVector> ValidationMap; 98 99} 100 101@interface WKViewData : NSObject { 102@public 103 OwnPtr<PageClientImpl> _pageClient; 104 RefPtr<WebPageProxy> _page; 105 106 // For ToolTips. 107 NSToolTipTag _lastToolTipTag; 108 id _trackingRectOwner; 109 void* _trackingRectUserData; 110 111 RetainPtr<NSView> _layerHostingView; 112 113 // FIXME: Remove _oldLayerHostingView. 114#if USE(ACCELERATED_COMPOSITING) 115 NSView *_oldLayerHostingView; 116#endif 117 118 RetainPtr<id> _remoteAccessibilityChild; 119 120 // For asynchronous validation. 121 ValidationMap _validationMap; 122 123 OwnPtr<PDFViewController> _pdfViewController; 124 125 OwnPtr<FindIndicatorWindow> _findIndicatorWindow; 126 // We keep here the event when resending it to 127 // the application to distinguish the case of a new event from one 128 // that has been already sent to WebCore. 129 NSEvent *_keyDownEventBeingResent; 130 Vector<KeypressCommand> _commandsList; 131 132 NSSize _resizeScrollOffset; 133 134 // The identifier of the plug-in we want to send complex text input to, or 0 if there is none. 135 uint64_t _pluginComplexTextInputIdentifier; 136 137 Vector<CompositionUnderline> _underlines; 138 unsigned _selectionStart; 139 unsigned _selectionEnd; 140 141 bool _inBecomeFirstResponder; 142 bool _inResignFirstResponder; 143 NSEvent *_mouseDownEvent; 144 BOOL _ignoringMouseDraggedEvents; 145 BOOL _dragHasStarted; 146 147#if ENABLE(GESTURE_EVENTS) 148 id _endGestureMonitor; 149#endif 150} 151@end 152 153@implementation WKViewData 154@end 155 156@interface NSObject (NSTextInputContextDetails) 157- (BOOL)wantsToHandleMouseEvents; 158- (BOOL)handleMouseEvent:(NSEvent *)event; 159@end 160 161@implementation WKView 162 163// FIXME: Remove this once we no longer want to be able to go back to the old drawing area. 164static bool useNewDrawingArea() 165{ 166 return true; 167} 168 169- (id)initWithFrame:(NSRect)frame 170{ 171 return [self initWithFrame:frame contextRef:toAPI(WebContext::sharedProcessContext())]; 172} 173 174- (id)initWithFrame:(NSRect)frame contextRef:(WKContextRef)contextRef 175{ 176 return [self initWithFrame:frame contextRef:contextRef pageGroupRef:nil]; 177} 178 179- (void)_registerDraggedTypes 180{ 181 NSMutableSet *types = [[NSMutableSet alloc] initWithArray:PasteboardTypes::forEditing()]; 182 [types addObjectsFromArray:PasteboardTypes::forURL()]; 183 [self registerForDraggedTypes:[types allObjects]]; 184 [types release]; 185} 186 187- (void)_updateRemoteAccessibilityRegistration:(BOOL)registerProcess 188{ 189#if !defined(BUILDING_ON_SNOW_LEOPARD) 190 // When the tree is connected/disconnected, the remote accessibility registration 191 // needs to be updated with the pid of the remote process. If the process is going 192 // away, that information is not present in WebProcess 193 pid_t pid = 0; 194 if (registerProcess && _data->_page->process()) 195 pid = _data->_page->process()->processIdentifier(); 196 else if (!registerProcess) { 197 pid = WKAXRemoteProcessIdentifier(_data->_remoteAccessibilityChild.get()); 198 _data->_remoteAccessibilityChild = nil; 199 } 200 if (pid) 201 WKAXRegisterRemoteProcess(registerProcess, pid); 202#endif 203} 204 205- (id)initWithFrame:(NSRect)frame contextRef:(WKContextRef)contextRef pageGroupRef:(WKPageGroupRef)pageGroupRef 206{ 207 self = [super initWithFrame:frame]; 208 if (!self) 209 return nil; 210 211 InitWebCoreSystemInterface(); 212 RunLoop::initializeMainRunLoop(); 213 214 NSTrackingArea *trackingArea = [[NSTrackingArea alloc] initWithRect:frame 215 options:(NSTrackingMouseMoved | NSTrackingMouseEnteredAndExited | NSTrackingActiveInKeyWindow | NSTrackingInVisibleRect) 216 owner:self 217 userInfo:nil]; 218 [self addTrackingArea:trackingArea]; 219 [trackingArea release]; 220 221 _data = [[WKViewData alloc] init]; 222 223 _data->_pageClient = PageClientImpl::create(self); 224 _data->_page = toImpl(contextRef)->createWebPage(_data->_pageClient.get(), toImpl(pageGroupRef)); 225 _data->_page->initializeWebPage(); 226 _data->_mouseDownEvent = nil; 227 _data->_ignoringMouseDraggedEvents = NO; 228 229 [self _registerDraggedTypes]; 230 231 WebContext::statistics().wkViewCount++; 232 233 return self; 234} 235 236- (void)dealloc 237{ 238 _data->_page->close(); 239 240 [_data release]; 241 242 WebContext::statistics().wkViewCount--; 243 244 [super dealloc]; 245} 246 247- (WKPageRef)pageRef 248{ 249 return toAPI(_data->_page.get()); 250} 251 252- (void)setDrawsBackground:(BOOL)drawsBackground 253{ 254 _data->_page->setDrawsBackground(drawsBackground); 255} 256 257- (BOOL)drawsBackground 258{ 259 return _data->_page->drawsBackground(); 260} 261 262- (void)setDrawsTransparentBackground:(BOOL)drawsTransparentBackground 263{ 264 _data->_page->setDrawsTransparentBackground(drawsTransparentBackground); 265} 266 267- (BOOL)drawsTransparentBackground 268{ 269 return _data->_page->drawsTransparentBackground(); 270} 271 272- (BOOL)acceptsFirstResponder 273{ 274 return YES; 275} 276 277- (BOOL)becomeFirstResponder 278{ 279 NSSelectionDirection direction = [[self window] keyViewSelectionDirection]; 280 281 _data->_inBecomeFirstResponder = true; 282 _data->_page->viewStateDidChange(WebPageProxy::ViewIsFocused); 283 _data->_inBecomeFirstResponder = false; 284 285 if (direction != NSDirectSelection) 286 _data->_page->setInitialFocus(direction == NSSelectingNext); 287 288 return YES; 289} 290 291- (BOOL)resignFirstResponder 292{ 293 _data->_inResignFirstResponder = true; 294 _data->_page->viewStateDidChange(WebPageProxy::ViewIsFocused); 295 _data->_inResignFirstResponder = false; 296 297 return YES; 298} 299 300- (void)viewWillStartLiveResize 301{ 302 _data->_page->viewWillStartLiveResize(); 303} 304 305- (void)viewDidEndLiveResize 306{ 307 _data->_page->viewWillEndLiveResize(); 308} 309 310- (BOOL)isFlipped 311{ 312 return YES; 313} 314 315- (void)setFrame:(NSRect)rect andScrollBy:(NSSize)offset 316{ 317 ASSERT(NSEqualSizes(_data->_resizeScrollOffset, NSZeroSize)); 318 319 _data->_resizeScrollOffset = offset; 320 [self setFrame:rect]; 321} 322 323- (void)setFrameSize:(NSSize)size 324{ 325 [super setFrameSize:size]; 326 327 if (![self frameSizeUpdatesDisabled]) 328 [self _setDrawingAreaSize:size]; 329} 330 331- (void)_updateWindowAndViewFrames 332{ 333 NSWindow *window = [self window]; 334 ASSERT(window); 335 336 NSRect windowFrameInScreenCoordinates = [window frame]; 337 NSRect viewFrameInWindowCoordinates = [self convertRect:[self frame] toView:nil]; 338 NSPoint accessibilityPosition = [[self accessibilityAttributeValue:NSAccessibilityPositionAttribute] pointValue]; 339 340 _data->_page->windowAndViewFramesChanged(enclosingIntRect(windowFrameInScreenCoordinates), enclosingIntRect(viewFrameInWindowCoordinates), IntPoint(accessibilityPosition)); 341} 342 343- (void)renewGState 344{ 345 // Hide the find indicator. 346 _data->_findIndicatorWindow = nullptr; 347 348 // Update the view frame. 349 if ([self window]) 350 [self _updateWindowAndViewFrames]; 351 352 [super renewGState]; 353} 354 355typedef HashMap<SEL, String> SelectorNameMap; 356 357// Map selectors into Editor command names. 358// This is not needed for any selectors that have the same name as the Editor command. 359static const SelectorNameMap* createSelectorExceptionMap() 360{ 361 SelectorNameMap* map = new HashMap<SEL, String>; 362 363 map->add(@selector(insertNewlineIgnoringFieldEditor:), "InsertNewline"); 364 map->add(@selector(insertParagraphSeparator:), "InsertNewline"); 365 map->add(@selector(insertTabIgnoringFieldEditor:), "InsertTab"); 366 map->add(@selector(pageDown:), "MovePageDown"); 367 map->add(@selector(pageDownAndModifySelection:), "MovePageDownAndModifySelection"); 368 map->add(@selector(pageUp:), "MovePageUp"); 369 map->add(@selector(pageUpAndModifySelection:), "MovePageUpAndModifySelection"); 370 map->add(@selector(scrollPageDown:), "ScrollPageForward"); 371 map->add(@selector(scrollPageUp:), "ScrollPageBackward"); 372 373 return map; 374} 375 376static String commandNameForSelector(SEL selector) 377{ 378 // Check the exception map first. 379 static const SelectorNameMap* exceptionMap = createSelectorExceptionMap(); 380 SelectorNameMap::const_iterator it = exceptionMap->find(selector); 381 if (it != exceptionMap->end()) 382 return it->second; 383 384 // Remove the trailing colon. 385 // No need to capitalize the command name since Editor command names are 386 // not case sensitive. 387 const char* selectorName = sel_getName(selector); 388 size_t selectorNameLength = strlen(selectorName); 389 if (selectorNameLength < 2 || selectorName[selectorNameLength - 1] != ':') 390 return String(); 391 return String(selectorName, selectorNameLength - 1); 392} 393 394// Editing commands 395 396#define WEBCORE_COMMAND(command) - (void)command:(id)sender { _data->_page->executeEditCommand(commandNameForSelector(_cmd)); } 397 398WEBCORE_COMMAND(alignCenter) 399WEBCORE_COMMAND(alignJustified) 400WEBCORE_COMMAND(alignLeft) 401WEBCORE_COMMAND(alignRight) 402WEBCORE_COMMAND(copy) 403WEBCORE_COMMAND(cut) 404WEBCORE_COMMAND(delete) 405WEBCORE_COMMAND(deleteBackward) 406WEBCORE_COMMAND(deleteBackwardByDecomposingPreviousCharacter) 407WEBCORE_COMMAND(deleteForward) 408WEBCORE_COMMAND(deleteToBeginningOfLine) 409WEBCORE_COMMAND(deleteToBeginningOfParagraph) 410WEBCORE_COMMAND(deleteToEndOfLine) 411WEBCORE_COMMAND(deleteToEndOfParagraph) 412WEBCORE_COMMAND(deleteToMark) 413WEBCORE_COMMAND(deleteWordBackward) 414WEBCORE_COMMAND(deleteWordForward) 415WEBCORE_COMMAND(ignoreSpelling) 416WEBCORE_COMMAND(indent) 417WEBCORE_COMMAND(insertBacktab) 418WEBCORE_COMMAND(insertLineBreak) 419WEBCORE_COMMAND(insertNewline) 420WEBCORE_COMMAND(insertNewlineIgnoringFieldEditor) 421WEBCORE_COMMAND(insertParagraphSeparator) 422WEBCORE_COMMAND(insertTab) 423WEBCORE_COMMAND(insertTabIgnoringFieldEditor) 424WEBCORE_COMMAND(makeTextWritingDirectionLeftToRight) 425WEBCORE_COMMAND(makeTextWritingDirectionNatural) 426WEBCORE_COMMAND(makeTextWritingDirectionRightToLeft) 427WEBCORE_COMMAND(moveBackward) 428WEBCORE_COMMAND(moveBackwardAndModifySelection) 429WEBCORE_COMMAND(moveDown) 430WEBCORE_COMMAND(moveDownAndModifySelection) 431WEBCORE_COMMAND(moveForward) 432WEBCORE_COMMAND(moveForwardAndModifySelection) 433WEBCORE_COMMAND(moveLeft) 434WEBCORE_COMMAND(moveLeftAndModifySelection) 435WEBCORE_COMMAND(moveParagraphBackwardAndModifySelection) 436WEBCORE_COMMAND(moveParagraphForwardAndModifySelection) 437WEBCORE_COMMAND(moveRight) 438WEBCORE_COMMAND(moveRightAndModifySelection) 439WEBCORE_COMMAND(moveToBeginningOfDocument) 440WEBCORE_COMMAND(moveToBeginningOfDocumentAndModifySelection) 441WEBCORE_COMMAND(moveToBeginningOfLine) 442WEBCORE_COMMAND(moveToBeginningOfLineAndModifySelection) 443WEBCORE_COMMAND(moveToBeginningOfParagraph) 444WEBCORE_COMMAND(moveToBeginningOfParagraphAndModifySelection) 445WEBCORE_COMMAND(moveToBeginningOfSentence) 446WEBCORE_COMMAND(moveToBeginningOfSentenceAndModifySelection) 447WEBCORE_COMMAND(moveToEndOfDocument) 448WEBCORE_COMMAND(moveToEndOfDocumentAndModifySelection) 449WEBCORE_COMMAND(moveToEndOfLine) 450WEBCORE_COMMAND(moveToEndOfLineAndModifySelection) 451WEBCORE_COMMAND(moveToEndOfParagraph) 452WEBCORE_COMMAND(moveToEndOfParagraphAndModifySelection) 453WEBCORE_COMMAND(moveToEndOfSentence) 454WEBCORE_COMMAND(moveToEndOfSentenceAndModifySelection) 455WEBCORE_COMMAND(moveToLeftEndOfLine) 456WEBCORE_COMMAND(moveToLeftEndOfLineAndModifySelection) 457WEBCORE_COMMAND(moveToRightEndOfLine) 458WEBCORE_COMMAND(moveToRightEndOfLineAndModifySelection) 459WEBCORE_COMMAND(moveUp) 460WEBCORE_COMMAND(moveUpAndModifySelection) 461WEBCORE_COMMAND(moveWordBackward) 462WEBCORE_COMMAND(moveWordBackwardAndModifySelection) 463WEBCORE_COMMAND(moveWordForward) 464WEBCORE_COMMAND(moveWordForwardAndModifySelection) 465WEBCORE_COMMAND(moveWordLeft) 466WEBCORE_COMMAND(moveWordLeftAndModifySelection) 467WEBCORE_COMMAND(moveWordRight) 468WEBCORE_COMMAND(moveWordRightAndModifySelection) 469WEBCORE_COMMAND(outdent) 470WEBCORE_COMMAND(pageDown) 471WEBCORE_COMMAND(pageDownAndModifySelection) 472WEBCORE_COMMAND(pageUp) 473WEBCORE_COMMAND(pageUpAndModifySelection) 474WEBCORE_COMMAND(paste) 475WEBCORE_COMMAND(pasteAsPlainText) 476WEBCORE_COMMAND(scrollPageDown) 477WEBCORE_COMMAND(scrollPageUp) 478WEBCORE_COMMAND(scrollToBeginningOfDocument) 479WEBCORE_COMMAND(scrollToEndOfDocument) 480WEBCORE_COMMAND(selectAll) 481WEBCORE_COMMAND(selectLine) 482WEBCORE_COMMAND(selectParagraph) 483WEBCORE_COMMAND(selectSentence) 484WEBCORE_COMMAND(selectToMark) 485WEBCORE_COMMAND(selectWord) 486WEBCORE_COMMAND(setMark) 487WEBCORE_COMMAND(subscript) 488WEBCORE_COMMAND(superscript) 489WEBCORE_COMMAND(swapWithMark) 490WEBCORE_COMMAND(takeFindStringFromSelection) 491WEBCORE_COMMAND(transpose) 492WEBCORE_COMMAND(underline) 493WEBCORE_COMMAND(unscript) 494WEBCORE_COMMAND(yank) 495WEBCORE_COMMAND(yankAndSelect) 496 497#undef WEBCORE_COMMAND 498 499/* 500 501When possible, editing-related methods should be implemented in WebCore with the 502EditorCommand mechanism and invoked via WEBCORE_COMMAND, rather than implementing 503individual methods here with Mac-specific code. 504 505Editing-related methods still unimplemented that are implemented in WebKit1: 506 507- (void)capitalizeWord:(id)sender; 508- (void)centerSelectionInVisibleArea:(id)sender; 509- (void)changeFont:(id)sender; 510- (void)complete:(id)sender; 511- (void)copyFont:(id)sender; 512- (void)lowercaseWord:(id)sender; 513- (void)makeBaseWritingDirectionLeftToRight:(id)sender; 514- (void)makeBaseWritingDirectionNatural:(id)sender; 515- (void)makeBaseWritingDirectionRightToLeft:(id)sender; 516- (void)pasteFont:(id)sender; 517- (void)scrollLineDown:(id)sender; 518- (void)scrollLineUp:(id)sender; 519- (void)showGuessPanel:(id)sender; 520- (void)uppercaseWord:(id)sender; 521 522Some other editing-related methods still unimplemented: 523 524- (void)changeCaseOfLetter:(id)sender; 525- (void)copyRuler:(id)sender; 526- (void)insertContainerBreak:(id)sender; 527- (void)insertDoubleQuoteIgnoringSubstitution:(id)sender; 528- (void)insertSingleQuoteIgnoringSubstitution:(id)sender; 529- (void)pasteRuler:(id)sender; 530- (void)toggleRuler:(id)sender; 531- (void)transposeWords:(id)sender; 532 533*/ 534 535// Menu items validation 536 537static NSMenuItem *menuItem(id <NSValidatedUserInterfaceItem> item) 538{ 539 if (![(NSObject *)item isKindOfClass:[NSMenuItem class]]) 540 return nil; 541 return (NSMenuItem *)item; 542} 543 544static NSToolbarItem *toolbarItem(id <NSValidatedUserInterfaceItem> item) 545{ 546 if (![(NSObject *)item isKindOfClass:[NSToolbarItem class]]) 547 return nil; 548 return (NSToolbarItem *)item; 549} 550 551- (BOOL)validateUserInterfaceItem:(id <NSValidatedUserInterfaceItem>)item 552{ 553 SEL action = [item action]; 554 555 if (action == @selector(showGuessPanel:)) { 556 if (NSMenuItem *menuItem = ::menuItem(item)) { 557 BOOL panelShowing = [[[NSSpellChecker sharedSpellChecker] spellingPanel] isVisible]; 558 [menuItem setTitle:panelShowing 559 ? UI_STRING("Hide Spelling and Grammar", "menu item title") 560 : UI_STRING("Show Spelling and Grammar", "menu item title")]; 561 } 562 return _data->_page->selectionState().isContentEditable; 563 } 564 565 if (action == @selector(checkSpelling:) || action == @selector(changeSpelling:)) 566 return _data->_page->selectionState().isContentEditable; 567 568 if (action == @selector(toggleContinuousSpellChecking:)) { 569 bool enabled = TextChecker::isContinuousSpellCheckingAllowed(); 570 bool checked = enabled && TextChecker::state().isContinuousSpellCheckingEnabled; 571 [menuItem(item) setState:checked ? NSOnState : NSOffState]; 572 return enabled; 573 } 574 575 if (action == @selector(toggleGrammarChecking:)) { 576 bool checked = TextChecker::state().isGrammarCheckingEnabled; 577 [menuItem(item) setState:checked ? NSOnState : NSOffState]; 578 return YES; 579 } 580 581 if (action == @selector(toggleAutomaticSpellingCorrection:)) { 582 bool checked = TextChecker::state().isAutomaticSpellingCorrectionEnabled; 583 [menuItem(item) setState:checked ? NSOnState : NSOffState]; 584 return _data->_page->selectionState().isContentEditable; 585 } 586 587 if (action == @selector(orderFrontSubstitutionsPanel:)) { 588 if (NSMenuItem *menuItem = ::menuItem(item)) { 589 BOOL panelShowing = [[[NSSpellChecker sharedSpellChecker] substitutionsPanel] isVisible]; 590 [menuItem setTitle:panelShowing 591 ? UI_STRING("Hide Substitutions", "menu item title") 592 : UI_STRING("Show Substitutions", "menu item title")]; 593 } 594 return _data->_page->selectionState().isContentEditable; 595 } 596 597 if (action == @selector(toggleSmartInsertDelete:)) { 598 bool checked = _data->_page->isSmartInsertDeleteEnabled(); 599 [menuItem(item) setState:checked ? NSOnState : NSOffState]; 600 return _data->_page->selectionState().isContentEditable; 601 } 602 603 if (action == @selector(toggleAutomaticQuoteSubstitution:)) { 604 bool checked = TextChecker::state().isAutomaticQuoteSubstitutionEnabled; 605 [menuItem(item) setState:checked ? NSOnState : NSOffState]; 606 return _data->_page->selectionState().isContentEditable; 607 } 608 609 if (action == @selector(toggleAutomaticDashSubstitution:)) { 610 bool checked = TextChecker::state().isAutomaticDashSubstitutionEnabled; 611 [menuItem(item) setState:checked ? NSOnState : NSOffState]; 612 return _data->_page->selectionState().isContentEditable; 613 } 614 615 if (action == @selector(toggleAutomaticLinkDetection:)) { 616 bool checked = TextChecker::state().isAutomaticLinkDetectionEnabled; 617 [menuItem(item) setState:checked ? NSOnState : NSOffState]; 618 return _data->_page->selectionState().isContentEditable; 619 } 620 621 if (action == @selector(toggleAutomaticTextReplacement:)) { 622 bool checked = TextChecker::state().isAutomaticTextReplacementEnabled; 623 [menuItem(item) setState:checked ? NSOnState : NSOffState]; 624 return _data->_page->selectionState().isContentEditable; 625 } 626 627 if (action == @selector(uppercaseWord:) || action == @selector(lowercaseWord:) || action == @selector(capitalizeWord:)) 628 return _data->_page->selectionState().selectedRangeLength && _data->_page->selectionState().isContentEditable; 629 630 if (action == @selector(stopSpeaking:)) 631 return [NSApp isSpeaking]; 632 633 // Next, handle editor commands. Start by returning YES for anything that is not an editor command. 634 // Returning YES is the default thing to do in an AppKit validate method for any selector that is not recognized. 635 String commandName = commandNameForSelector([item action]); 636 if (!Editor::commandIsSupportedFromMenuOrKeyBinding(commandName)) 637 return YES; 638 639 // Add this item to the vector of items for a given command that are awaiting validation. 640 pair<ValidationMap::iterator, bool> addResult = _data->_validationMap.add(commandName, ValidationVector()); 641 addResult.first->second.append(item); 642 if (addResult.second) { 643 // If we are not already awaiting validation for this command, start the asynchronous validation process. 644 // FIXME: Theoretically, there is a race here; when we get the answer it might be old, from a previous time 645 // we asked for the same command; there is no guarantee the answer is still valid. 646 // FIXME: The function called here should be renamed validateCommand because it is not specific to menu items. 647 _data->_page->validateMenuItem(commandName); 648 } 649 650 // Treat as enabled until we get the result back from the web process and _setUserInterfaceItemState is called. 651 // FIXME <rdar://problem/8803459>: This means disabled items will flash enabled at first for a moment. 652 // But returning NO here would be worse; that would make keyboard commands such as command-C fail. 653 return YES; 654} 655 656static void speakString(WKStringRef string, WKErrorRef error, void*) 657{ 658 if (error) 659 return; 660 if (!string) 661 return; 662 663 NSString *convertedString = toImpl(string)->string(); 664 [NSApp speakString:convertedString]; 665} 666 667- (IBAction)startSpeaking:(id)sender 668{ 669 _data->_page->getSelectionOrContentsAsString(StringCallback::create(0, speakString)); 670} 671 672- (IBAction)stopSpeaking:(id)sender 673{ 674 [NSApp stopSpeaking:sender]; 675} 676 677- (IBAction)showGuessPanel:(id)sender 678{ 679 NSSpellChecker *checker = [NSSpellChecker sharedSpellChecker]; 680 if (!checker) { 681 LOG_ERROR("No NSSpellChecker"); 682 return; 683 } 684 685 NSPanel *spellingPanel = [checker spellingPanel]; 686 if ([spellingPanel isVisible]) { 687 [spellingPanel orderOut:sender]; 688 return; 689 } 690 691 _data->_page->advanceToNextMisspelling(true); 692 [spellingPanel orderFront:sender]; 693} 694 695- (IBAction)checkSpelling:(id)sender 696{ 697 _data->_page->advanceToNextMisspelling(false); 698} 699 700- (void)changeSpelling:(id)sender 701{ 702 NSString *word = [[sender selectedCell] stringValue]; 703 704 _data->_page->changeSpellingToWord(word); 705} 706 707- (IBAction)toggleContinuousSpellChecking:(id)sender 708{ 709 bool spellCheckingEnabled = !TextChecker::state().isContinuousSpellCheckingEnabled; 710 TextChecker::setContinuousSpellCheckingEnabled(spellCheckingEnabled); 711 712 _data->_page->process()->updateTextCheckerState(); 713 714 if (!spellCheckingEnabled) 715 _data->_page->unmarkAllMisspellings(); 716} 717 718- (IBAction)toggleGrammarChecking:(id)sender 719{ 720 bool grammarCheckingEnabled = !TextChecker::state().isGrammarCheckingEnabled; 721 TextChecker::setGrammarCheckingEnabled(grammarCheckingEnabled); 722 723 _data->_page->process()->updateTextCheckerState(); 724 725 if (!grammarCheckingEnabled) 726 _data->_page->unmarkAllBadGrammar(); 727} 728 729- (IBAction)toggleAutomaticSpellingCorrection:(id)sender 730{ 731 TextChecker::setAutomaticSpellingCorrectionEnabled(!TextChecker::state().isAutomaticSpellingCorrectionEnabled); 732 733 _data->_page->process()->updateTextCheckerState(); 734} 735 736- (void)orderFrontSubstitutionsPanel:(id)sender 737{ 738 NSSpellChecker *checker = [NSSpellChecker sharedSpellChecker]; 739 if (!checker) { 740 LOG_ERROR("No NSSpellChecker"); 741 return; 742 } 743 744 NSPanel *substitutionsPanel = [checker substitutionsPanel]; 745 if ([substitutionsPanel isVisible]) { 746 [substitutionsPanel orderOut:sender]; 747 return; 748 } 749 [substitutionsPanel orderFront:sender]; 750} 751 752- (IBAction)toggleSmartInsertDelete:(id)sender 753{ 754 _data->_page->setSmartInsertDeleteEnabled(!_data->_page->isSmartInsertDeleteEnabled()); 755} 756 757- (BOOL)isAutomaticQuoteSubstitutionEnabled 758{ 759 return TextChecker::state().isAutomaticQuoteSubstitutionEnabled; 760} 761 762- (void)setAutomaticQuoteSubstitutionEnabled:(BOOL)flag 763{ 764 if (static_cast<bool>(flag) == TextChecker::state().isAutomaticQuoteSubstitutionEnabled) 765 return; 766 767 TextChecker::setAutomaticQuoteSubstitutionEnabled(flag); 768 _data->_page->process()->updateTextCheckerState(); 769} 770 771- (void)toggleAutomaticQuoteSubstitution:(id)sender 772{ 773 TextChecker::setAutomaticQuoteSubstitutionEnabled(!TextChecker::state().isAutomaticQuoteSubstitutionEnabled); 774 _data->_page->process()->updateTextCheckerState(); 775} 776 777- (BOOL)isAutomaticDashSubstitutionEnabled 778{ 779 return TextChecker::state().isAutomaticDashSubstitutionEnabled; 780} 781 782- (void)setAutomaticDashSubstitutionEnabled:(BOOL)flag 783{ 784 if (static_cast<bool>(flag) == TextChecker::state().isAutomaticDashSubstitutionEnabled) 785 return; 786 787 TextChecker::setAutomaticDashSubstitutionEnabled(flag); 788 _data->_page->process()->updateTextCheckerState(); 789} 790 791- (void)toggleAutomaticDashSubstitution:(id)sender 792{ 793 TextChecker::setAutomaticDashSubstitutionEnabled(!TextChecker::state().isAutomaticDashSubstitutionEnabled); 794 _data->_page->process()->updateTextCheckerState(); 795} 796 797- (BOOL)isAutomaticLinkDetectionEnabled 798{ 799 return TextChecker::state().isAutomaticLinkDetectionEnabled; 800} 801 802- (void)setAutomaticLinkDetectionEnabled:(BOOL)flag 803{ 804 if (static_cast<bool>(flag) == TextChecker::state().isAutomaticLinkDetectionEnabled) 805 return; 806 807 TextChecker::setAutomaticLinkDetectionEnabled(flag); 808 _data->_page->process()->updateTextCheckerState(); 809} 810 811- (void)toggleAutomaticLinkDetection:(id)sender 812{ 813 TextChecker::setAutomaticLinkDetectionEnabled(!TextChecker::state().isAutomaticLinkDetectionEnabled); 814 _data->_page->process()->updateTextCheckerState(); 815} 816 817- (BOOL)isAutomaticTextReplacementEnabled 818{ 819 return TextChecker::state().isAutomaticTextReplacementEnabled; 820} 821 822- (void)setAutomaticTextReplacementEnabled:(BOOL)flag 823{ 824 if (static_cast<bool>(flag) == TextChecker::state().isAutomaticTextReplacementEnabled) 825 return; 826 827 TextChecker::setAutomaticTextReplacementEnabled(flag); 828 _data->_page->process()->updateTextCheckerState(); 829} 830 831- (void)toggleAutomaticTextReplacement:(id)sender 832{ 833 TextChecker::setAutomaticTextReplacementEnabled(!TextChecker::state().isAutomaticTextReplacementEnabled); 834 _data->_page->process()->updateTextCheckerState(); 835} 836 837- (void)uppercaseWord:(id)sender 838{ 839 _data->_page->uppercaseWord(); 840} 841 842- (void)lowercaseWord:(id)sender 843{ 844 _data->_page->lowercaseWord(); 845} 846 847- (void)capitalizeWord:(id)sender 848{ 849 _data->_page->capitalizeWord(); 850} 851 852// Events 853 854// Override this so that AppKit will send us arrow keys as key down events so we can 855// support them via the key bindings mechanism. 856- (BOOL)_wantsKeyDownForEvent:(NSEvent *)event 857{ 858 return YES; 859} 860 861- (void)_setMouseDownEvent:(NSEvent *)event 862{ 863 ASSERT(!event || [event type] == NSLeftMouseDown || [event type] == NSRightMouseDown || [event type] == NSOtherMouseDown); 864 865 if (event == _data->_mouseDownEvent) 866 return; 867 868 [_data->_mouseDownEvent release]; 869 _data->_mouseDownEvent = [event retain]; 870} 871 872#define EVENT_HANDLER(Selector, Type) \ 873 - (void)Selector:(NSEvent *)theEvent \ 874 { \ 875 Web##Type##Event webEvent = WebEventFactory::createWeb##Type##Event(theEvent, self); \ 876 _data->_page->handle##Type##Event(webEvent); \ 877 } 878 879EVENT_HANDLER(mouseEntered, Mouse) 880EVENT_HANDLER(mouseExited, Mouse) 881EVENT_HANDLER(mouseMoved, Mouse) 882EVENT_HANDLER(otherMouseDown, Mouse) 883EVENT_HANDLER(otherMouseDragged, Mouse) 884EVENT_HANDLER(otherMouseMoved, Mouse) 885EVENT_HANDLER(otherMouseUp, Mouse) 886EVENT_HANDLER(rightMouseDown, Mouse) 887EVENT_HANDLER(rightMouseDragged, Mouse) 888EVENT_HANDLER(rightMouseMoved, Mouse) 889EVENT_HANDLER(rightMouseUp, Mouse) 890EVENT_HANDLER(scrollWheel, Wheel) 891 892#undef EVENT_HANDLER 893 894- (void)_mouseHandler:(NSEvent *)event 895{ 896 NSInputManager *currentInputManager = [NSInputManager currentInputManager]; 897 if ([currentInputManager wantsToHandleMouseEvents] && [currentInputManager handleMouseEvent:event]) 898 return; 899 WebMouseEvent webEvent = WebEventFactory::createWebMouseEvent(event, self); 900 _data->_page->handleMouseEvent(webEvent); 901} 902 903- (void)mouseDown:(NSEvent *)event 904{ 905 [self _setMouseDownEvent:event]; 906 _data->_ignoringMouseDraggedEvents = NO; 907 _data->_dragHasStarted = NO; 908 [self _mouseHandler:event]; 909} 910 911- (void)mouseUp:(NSEvent *)event 912{ 913 [self _setMouseDownEvent:nil]; 914 [self _mouseHandler:event]; 915} 916 917- (void)mouseDragged:(NSEvent *)event 918{ 919 if (_data->_ignoringMouseDraggedEvents) 920 return; 921 [self _mouseHandler:event]; 922} 923 924#if ENABLE(GESTURE_EVENTS) 925 926static const short kIOHIDEventTypeScroll = 6; 927 928- (void)shortCircuitedEndGestureWithEvent:(NSEvent *)event 929{ 930 if ([event subtype] != kIOHIDEventTypeScroll) 931 return; 932 933 WebGestureEvent webEvent = WebEventFactory::createWebGestureEvent(event, self); 934 _data->_page->handleGestureEvent(webEvent); 935 936 if (_data->_endGestureMonitor) { 937 [NSEvent removeMonitor:_data->_endGestureMonitor]; 938 _data->_endGestureMonitor = nil; 939 } 940} 941 942- (void)beginGestureWithEvent:(NSEvent *)event 943{ 944 if ([event subtype] != kIOHIDEventTypeScroll) 945 return; 946 947 WebGestureEvent webEvent = WebEventFactory::createWebGestureEvent(event, self); 948 _data->_page->handleGestureEvent(webEvent); 949 950 if (!_data->_endGestureMonitor) { 951 _data->_endGestureMonitor = [NSEvent addLocalMonitorForEventsMatchingMask:NSEventMaskEndGesture handler:^(NSEvent *blockEvent) { 952 [self shortCircuitedEndGestureWithEvent:blockEvent]; 953 return blockEvent; 954 }]; 955 } 956} 957#endif 958 959- (void)doCommandBySelector:(SEL)selector 960{ 961 if (selector != @selector(noop:)) 962 _data->_commandsList.append(KeypressCommand(commandNameForSelector(selector))); 963} 964 965- (void)insertText:(id)string 966{ 967 BOOL isAttributedString = [string isKindOfClass:[NSAttributedString class]]; // Otherwise, NSString 968 969 LOG(TextInput, "insertText:\"%@\"", isAttributedString ? [string string] : string); 970 NSString *text; 971 bool isFromInputMethod = _data->_page->selectionState().hasComposition; 972 973 if (isAttributedString) { 974 text = [string string]; 975 // We deal with the NSTextInputReplacementRangeAttributeName attribute from NSAttributedString here 976 // simply because it is used by at least one Input Method -- it corresonds to the kEventParamTextInputSendReplaceRange 977 // event in TSM. This behaviour matches that of -[WebHTMLView setMarkedText:selectedRange:] when it receives an 978 // NSAttributedString 979 NSString *rangeString = [string attribute:NSTextInputReplacementRangeAttributeName atIndex:0 longestEffectiveRange:NULL inRange:NSMakeRange(0, [text length])]; 980 LOG(TextInput, "ReplacementRange: %@", rangeString); 981 if (rangeString) 982 isFromInputMethod = YES; 983 } else 984 text = string; 985 986 String eventText = text; 987 988 if (!isFromInputMethod) 989 _data->_commandsList.append(KeypressCommand("insertText", text)); 990 else { 991 eventText.replace(NSBackTabCharacter, NSTabCharacter); // same thing is done in KeyEventMac.mm in WebCore 992 _data->_commandsList.append(KeypressCommand("insertText", eventText)); 993 } 994} 995 996- (BOOL)_handleStyleKeyEquivalent:(NSEvent *)event 997{ 998 if (!_data->_page->selectionState().isContentEditable) 999 return NO; 1000 1001 if (([event modifierFlags] & NSDeviceIndependentModifierFlagsMask) != NSCommandKeyMask) 1002 return NO; 1003 1004 // Here we special case cmd+b and cmd+i but not cmd+u, for historic reason. 1005 // This should not be changed, since it could break some Mac applications that 1006 // rely on this inherent behavior. 1007 // See https://bugs.webkit.org/show_bug.cgi?id=24943 1008 1009 NSString *string = [event characters]; 1010 if ([string caseInsensitiveCompare:@"b"] == NSOrderedSame) { 1011 _data->_page->executeEditCommand("ToggleBold"); 1012 return YES; 1013 } 1014 if ([string caseInsensitiveCompare:@"i"] == NSOrderedSame) { 1015 _data->_page->executeEditCommand("ToggleItalic"); 1016 return YES; 1017 } 1018 1019 return NO; 1020} 1021 1022- (BOOL)performKeyEquivalent:(NSEvent *)event 1023{ 1024 // There's a chance that responding to this event will run a nested event loop, and 1025 // fetching a new event might release the old one. Retaining and then autoreleasing 1026 // the current event prevents that from causing a problem inside WebKit or AppKit code. 1027 [[event retain] autorelease]; 1028 1029 BOOL eventWasSentToWebCore = (_data->_keyDownEventBeingResent == event); 1030 1031 // Pass key combos through WebCore if there is a key binding available for 1032 // this event. This lets web pages have a crack at intercepting key-modified keypresses. 1033 // But don't do it if we have already handled the event. 1034 // Pressing Esc results in a fake event being sent - don't pass it to WebCore. 1035 if (!eventWasSentToWebCore && event == [NSApp currentEvent] && self == [[self window] firstResponder]) { 1036 [_data->_keyDownEventBeingResent release]; 1037 _data->_keyDownEventBeingResent = nil; 1038 1039 _data->_page->handleKeyboardEvent(NativeWebKeyboardEvent(event, self)); 1040 return YES; 1041 } 1042 1043 return [self _handleStyleKeyEquivalent:event] || [super performKeyEquivalent:event]; 1044} 1045 1046- (void)keyUp:(NSEvent *)theEvent 1047{ 1048 _data->_page->handleKeyboardEvent(NativeWebKeyboardEvent(theEvent, self)); 1049} 1050 1051- (void)keyDown:(NSEvent *)theEvent 1052{ 1053 if (_data->_pluginComplexTextInputIdentifier) { 1054 // Try feeding the keyboard event directly to the plug-in. 1055 NSString *string = nil; 1056 if ([[WKTextInputWindowController sharedTextInputWindowController] interpretKeyEvent:theEvent string:&string]) { 1057 if (string) 1058 _data->_page->sendComplexTextInputToPlugin(_data->_pluginComplexTextInputIdentifier, string); 1059 return; 1060 } 1061 } 1062 1063 _data->_underlines.clear(); 1064 _data->_selectionStart = 0; 1065 _data->_selectionEnd = 0; 1066 // We could be receiving a key down from AppKit if we have re-sent an event 1067 // that maps to an action that is currently unavailable (for example a copy when 1068 // there is no range selection). 1069 // If this is the case we should ignore the key down. 1070 if (_data->_keyDownEventBeingResent == theEvent) { 1071 [_data->_keyDownEventBeingResent release]; 1072 _data->_keyDownEventBeingResent = nil; 1073 [super keyDown:theEvent]; 1074 return; 1075 } 1076 _data->_page->handleKeyboardEvent(NativeWebKeyboardEvent(theEvent, self)); 1077} 1078 1079- (NSTextInputContext *)inputContext { 1080 if (_data->_pluginComplexTextInputIdentifier) 1081 return [[WKTextInputWindowController sharedTextInputWindowController] inputContext]; 1082 1083 return [super inputContext]; 1084} 1085 1086- (NSRange)selectedRange 1087{ 1088 if (_data->_page->selectionState().isNone || !_data->_page->selectionState().isContentEditable) 1089 return NSMakeRange(NSNotFound, 0); 1090 1091 LOG(TextInput, "selectedRange -> (%u, %u)", _data->_page->selectionState().selectedRangeStart, _data->_page->selectionState().selectedRangeLength); 1092 return NSMakeRange(_data->_page->selectionState().selectedRangeStart, _data->_page->selectionState().selectedRangeLength); 1093} 1094 1095- (BOOL)hasMarkedText 1096{ 1097 LOG(TextInput, "hasMarkedText -> %u", _data->_page->selectionState().hasComposition); 1098 return _data->_page->selectionState().hasComposition; 1099} 1100 1101- (void)unmarkText 1102{ 1103 LOG(TextInput, "unmarkText"); 1104 1105 _data->_commandsList.append(KeypressCommand("unmarkText")); 1106} 1107 1108- (NSArray *)validAttributesForMarkedText 1109{ 1110 static NSArray *validAttributes; 1111 if (!validAttributes) { 1112 validAttributes = [[NSArray alloc] initWithObjects: 1113 NSUnderlineStyleAttributeName, NSUnderlineColorAttributeName, 1114 NSMarkedClauseSegmentAttributeName, NSTextInputReplacementRangeAttributeName, nil]; 1115 // NSText also supports the following attributes, but it's 1116 // hard to tell which are really required for text input to 1117 // work well; I have not seen any input method make use of them yet. 1118 // NSFontAttributeName, NSForegroundColorAttributeName, 1119 // NSBackgroundColorAttributeName, NSLanguageAttributeName. 1120 CFRetain(validAttributes); 1121 } 1122 LOG(TextInput, "validAttributesForMarkedText -> (...)"); 1123 return validAttributes; 1124} 1125 1126static void extractUnderlines(NSAttributedString *string, Vector<CompositionUnderline>& result) 1127{ 1128 int length = [[string string] length]; 1129 1130 int i = 0; 1131 while (i < length) { 1132 NSRange range; 1133 NSDictionary *attrs = [string attributesAtIndex:i longestEffectiveRange:&range inRange:NSMakeRange(i, length - i)]; 1134 1135 if (NSNumber *style = [attrs objectForKey:NSUnderlineStyleAttributeName]) { 1136 Color color = Color::black; 1137 if (NSColor *colorAttr = [attrs objectForKey:NSUnderlineColorAttributeName]) 1138 color = colorFromNSColor([colorAttr colorUsingColorSpaceName:NSDeviceRGBColorSpace]); 1139 result.append(CompositionUnderline(range.location, NSMaxRange(range), color, [style intValue] > 1)); 1140 } 1141 1142 i = range.location + range.length; 1143 } 1144} 1145 1146- (void)setMarkedText:(id)string selectedRange:(NSRange)newSelRange 1147{ 1148 BOOL isAttributedString = [string isKindOfClass:[NSAttributedString class]]; // Otherwise, NSString 1149 1150 LOG(TextInput, "setMarkedText:\"%@\" selectedRange:(%u, %u)", isAttributedString ? [string string] : string, newSelRange.location, newSelRange.length); 1151 1152 NSString *text = string; 1153 1154 if (isAttributedString) { 1155 text = [string string]; 1156 extractUnderlines(string, _data->_underlines); 1157 } 1158 1159 _data->_commandsList.append(KeypressCommand("setMarkedText", text)); 1160 _data->_selectionStart = newSelRange.location; 1161 _data->_selectionEnd = NSMaxRange(newSelRange); 1162} 1163 1164- (NSRange)markedRange 1165{ 1166 uint64_t location; 1167 uint64_t length; 1168 1169 _data->_page->getMarkedRange(location, length); 1170 LOG(TextInput, "markedRange -> (%u, %u)", location, length); 1171 return NSMakeRange(location, length); 1172} 1173 1174- (NSAttributedString *)attributedSubstringFromRange:(NSRange)nsRange 1175{ 1176 // This is not implemented for now. Need to figure out how to serialize the attributed string across processes. 1177 LOG(TextInput, "attributedSubstringFromRange"); 1178 return nil; 1179} 1180 1181- (NSUInteger)characterIndexForPoint:(NSPoint)thePoint 1182{ 1183 NSWindow *window = [self window]; 1184 1185 if (window) 1186 thePoint = [window convertScreenToBase:thePoint]; 1187 thePoint = [self convertPoint:thePoint fromView:nil]; // the point is relative to the main frame 1188 1189 uint64_t result = _data->_page->characterIndexForPoint(IntPoint(thePoint)); 1190 LOG(TextInput, "characterIndexForPoint:(%f, %f) -> %u", thePoint.x, thePoint.y, result); 1191 return result; 1192} 1193 1194- (NSRect)firstRectForCharacterRange:(NSRange)theRange 1195{ 1196 // Just to match NSTextView's behavior. Regression tests cannot detect this; 1197 // to reproduce, use a test application from http://bugs.webkit.org/show_bug.cgi?id=4682 1198 // (type something; try ranges (1, -1) and (2, -1). 1199 if ((theRange.location + theRange.length < theRange.location) && (theRange.location + theRange.length != 0)) 1200 theRange.length = 0; 1201 1202 NSRect resultRect = _data->_page->firstRectForCharacterRange(theRange.location, theRange.length); 1203 resultRect = [self convertRect:resultRect toView:nil]; 1204 1205 NSWindow *window = [self window]; 1206 if (window) 1207 resultRect.origin = [window convertBaseToScreen:resultRect.origin]; 1208 1209 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); 1210 return resultRect; 1211} 1212 1213- (void)draggedImage:(NSImage *)anImage endedAt:(NSPoint)aPoint operation:(NSDragOperation)operation 1214{ 1215 NSPoint windowImageLoc = [[self window] convertScreenToBase:aPoint]; 1216 NSPoint windowMouseLoc = windowImageLoc; 1217 1218 // Prevent queued mouseDragged events from coming after the drag and fake mouseUp event. 1219 _data->_ignoringMouseDraggedEvents = YES; 1220 1221 _data->_page->dragEnded(IntPoint(windowMouseLoc), globalPoint(windowMouseLoc, [self window]), operation); 1222} 1223 1224- (DragApplicationFlags)applicationFlags:(id <NSDraggingInfo>)draggingInfo 1225{ 1226 uint32_t flags = 0; 1227 if ([NSApp modalWindow]) 1228 flags = DragApplicationIsModal; 1229 if ([[self window] attachedSheet]) 1230 flags |= DragApplicationHasAttachedSheet; 1231 if ([draggingInfo draggingSource] == self) 1232 flags |= DragApplicationIsSource; 1233 if ([[NSApp currentEvent] modifierFlags] & NSAlternateKeyMask) 1234 flags |= DragApplicationIsCopyKeyDown; 1235 return static_cast<DragApplicationFlags>(flags); 1236} 1237 1238- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)draggingInfo 1239{ 1240 IntPoint client([self convertPoint:[draggingInfo draggingLocation] fromView:nil]); 1241 IntPoint global(globalPoint([draggingInfo draggingLocation], [self window])); 1242 DragData dragData(draggingInfo, client, global, static_cast<DragOperation>([draggingInfo draggingSourceOperationMask]), [self applicationFlags:draggingInfo]); 1243 1244 _data->_page->resetDragOperation(); 1245 _data->_page->performDragControllerAction(DragControllerActionEntered, &dragData, [[draggingInfo draggingPasteboard] name]); 1246 return NSDragOperationCopy; 1247} 1248 1249- (NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)draggingInfo 1250{ 1251 IntPoint client([self convertPoint:[draggingInfo draggingLocation] fromView:nil]); 1252 IntPoint global(globalPoint([draggingInfo draggingLocation], [self window])); 1253 DragData dragData(draggingInfo, client, global, static_cast<DragOperation>([draggingInfo draggingSourceOperationMask]), [self applicationFlags:draggingInfo]); 1254 _data->_page->performDragControllerAction(DragControllerActionUpdated, &dragData, [[draggingInfo draggingPasteboard] name]); 1255 return _data->_page->dragOperation(); 1256} 1257 1258- (void)draggingExited:(id <NSDraggingInfo>)draggingInfo 1259{ 1260 IntPoint client([self convertPoint:[draggingInfo draggingLocation] fromView:nil]); 1261 IntPoint global(globalPoint([draggingInfo draggingLocation], [self window])); 1262 DragData dragData(draggingInfo, client, global, static_cast<DragOperation>([draggingInfo draggingSourceOperationMask]), [self applicationFlags:draggingInfo]); 1263 _data->_page->performDragControllerAction(DragControllerActionExited, &dragData, [[draggingInfo draggingPasteboard] name]); 1264 _data->_page->resetDragOperation(); 1265} 1266 1267- (BOOL)prepareForDragOperation:(id <NSDraggingInfo>)draggingInfo 1268{ 1269 return YES; 1270} 1271 1272- (BOOL)performDragOperation:(id <NSDraggingInfo>)draggingInfo 1273{ 1274 IntPoint client([self convertPoint:[draggingInfo draggingLocation] fromView:nil]); 1275 IntPoint global(globalPoint([draggingInfo draggingLocation], [self window])); 1276 DragData dragData(draggingInfo, client, global, static_cast<DragOperation>([draggingInfo draggingSourceOperationMask]), [self applicationFlags:draggingInfo]); 1277 _data->_page->performDragControllerAction(DragControllerActionPerformDrag, &dragData, [[draggingInfo draggingPasteboard] name]); 1278 return YES; 1279} 1280 1281- (void)_updateWindowVisibility 1282{ 1283 _data->_page->updateWindowIsVisible(![[self window] isMiniaturized]); 1284} 1285 1286- (BOOL)_ownsWindowGrowBox 1287{ 1288 NSWindow* window = [self window]; 1289 if (!window) 1290 return NO; 1291 1292 NSView *superview = [self superview]; 1293 if (!superview) 1294 return NO; 1295 1296 NSRect growBoxRect = [window _growBoxRect]; 1297 if (NSIsEmptyRect(growBoxRect)) 1298 return NO; 1299 1300 NSRect visibleRect = [self visibleRect]; 1301 if (NSIsEmptyRect(visibleRect)) 1302 return NO; 1303 1304 NSRect visibleRectInWindowCoords = [self convertRect:visibleRect toView:nil]; 1305 if (!NSIntersectsRect(growBoxRect, visibleRectInWindowCoords)) 1306 return NO; 1307 1308 return YES; 1309} 1310 1311- (BOOL)_updateGrowBoxForWindowFrameChange 1312{ 1313 // Temporarily enable the resize indicator to make a the _ownsWindowGrowBox calculation work. 1314 BOOL wasShowingIndicator = [[self window] showsResizeIndicator]; 1315 if (!wasShowingIndicator) 1316 [[self window] setShowsResizeIndicator:YES]; 1317 1318 BOOL ownsGrowBox = [self _ownsWindowGrowBox]; 1319 _data->_page->setWindowResizerSize(ownsGrowBox ? enclosingIntRect([[self window] _growBoxRect]).size() : IntSize()); 1320 1321 if (ownsGrowBox) 1322 [[self window] _setShowOpaqueGrowBoxForOwner:(_data->_page->hasHorizontalScrollbar() || _data->_page->hasVerticalScrollbar() ? self : nil)]; 1323 else 1324 [[self window] _setShowOpaqueGrowBoxForOwner:nil]; 1325 1326 // Once WebCore can draw the window resizer, this should read: 1327 // if (wasShowingIndicator) 1328 // [[self window] setShowsResizeIndicator:!ownsGrowBox]; 1329 if (!wasShowingIndicator) 1330 [[self window] setShowsResizeIndicator:NO]; 1331 1332 return ownsGrowBox; 1333} 1334 1335- (void)addWindowObserversForWindow:(NSWindow *)window 1336{ 1337 if (window) { 1338 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_windowDidBecomeKey:) 1339 name:NSWindowDidBecomeKeyNotification object:nil]; 1340 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_windowDidResignKey:) 1341 name:NSWindowDidResignKeyNotification object:nil]; 1342 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_windowDidMiniaturize:) 1343 name:NSWindowDidMiniaturizeNotification object:window]; 1344 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_windowDidDeminiaturize:) 1345 name:NSWindowDidDeminiaturizeNotification object:window]; 1346 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_windowFrameDidChange:) 1347 name:NSWindowDidMoveNotification object:window]; 1348 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_windowFrameDidChange:) 1349 name:NSWindowDidResizeNotification object:window]; 1350 } 1351} 1352 1353- (void)removeWindowObservers 1354{ 1355 NSWindow *window = [self window]; 1356 if (!window) 1357 return; 1358 1359 [[NSNotificationCenter defaultCenter] removeObserver:self name:NSWindowDidBecomeKeyNotification object:nil]; 1360 [[NSNotificationCenter defaultCenter] removeObserver:self name:NSWindowDidResignKeyNotification object:nil]; 1361 [[NSNotificationCenter defaultCenter] removeObserver:self name:NSWindowDidMiniaturizeNotification object:window]; 1362 [[NSNotificationCenter defaultCenter] removeObserver:self name:NSWindowDidDeminiaturizeNotification object:window]; 1363 [[NSNotificationCenter defaultCenter] removeObserver:self name:NSWindowDidMoveNotification object:window]; 1364 [[NSNotificationCenter defaultCenter] removeObserver:self name:NSWindowDidResizeNotification object:window]; 1365} 1366 1367- (void)viewWillMoveToWindow:(NSWindow *)window 1368{ 1369 if (window != [self window]) { 1370 [self removeWindowObservers]; 1371 [self addWindowObserversForWindow:window]; 1372 } 1373} 1374 1375- (void)viewDidMoveToWindow 1376{ 1377 // We want to make sure to update the active state while hidden, so if the view is about to become visible, we 1378 // update the active state first and then make it visible. If the view is about to be hidden, we hide it first and then 1379 // update the active state. 1380 if ([self window]) { 1381 _data->_page->viewStateDidChange(WebPageProxy::ViewWindowIsActive); 1382 _data->_page->viewStateDidChange(WebPageProxy::ViewIsVisible | WebPageProxy::ViewIsInWindow); 1383 [self _updateWindowVisibility]; 1384 [self _updateWindowAndViewFrames]; 1385 1386 // Initialize remote accessibility when the window connection has been established. 1387#if !defined(BUILDING_ON_SNOW_LEOPARD) 1388 NSData *remoteElementToken = WKAXRemoteTokenForElement(self); 1389 NSData *remoteWindowToken = WKAXRemoteTokenForElement([self window]); 1390 CoreIPC::DataReference elementToken = CoreIPC::DataReference(reinterpret_cast<const uint8_t*>([remoteElementToken bytes]), [remoteElementToken length]); 1391 CoreIPC::DataReference windowToken = CoreIPC::DataReference(reinterpret_cast<const uint8_t*>([remoteWindowToken bytes]), [remoteWindowToken length]); 1392 _data->_page->registerUIProcessAccessibilityTokens(elementToken, windowToken); 1393#endif 1394 1395 } else { 1396 _data->_page->viewStateDidChange(WebPageProxy::ViewIsVisible); 1397 _data->_page->viewStateDidChange(WebPageProxy::ViewWindowIsActive | WebPageProxy::ViewIsInWindow); 1398 1399#if ENABLE(GESTURE_EVENTS) 1400 if (_data->_endGestureMonitor) { 1401 [NSEvent removeMonitor:_data->_endGestureMonitor]; 1402 _data->_endGestureMonitor = nil; 1403 } 1404#endif 1405 } 1406} 1407 1408- (void)_windowDidBecomeKey:(NSNotification *)notification 1409{ 1410 NSWindow *keyWindow = [notification object]; 1411 if (keyWindow == [self window] || keyWindow == [[self window] attachedSheet]) 1412 _data->_page->viewStateDidChange(WebPageProxy::ViewWindowIsActive); 1413} 1414 1415- (void)_windowDidResignKey:(NSNotification *)notification 1416{ 1417 NSWindow *formerKeyWindow = [notification object]; 1418 if (formerKeyWindow == [self window] || formerKeyWindow == [[self window] attachedSheet]) 1419 _data->_page->viewStateDidChange(WebPageProxy::ViewWindowIsActive); 1420} 1421 1422- (void)_windowDidMiniaturize:(NSNotification *)notification 1423{ 1424 [self _updateWindowVisibility]; 1425} 1426 1427- (void)_windowDidDeminiaturize:(NSNotification *)notification 1428{ 1429 [self _updateWindowVisibility]; 1430} 1431 1432- (void)_windowFrameDidChange:(NSNotification *)notification 1433{ 1434 [self _updateWindowAndViewFrames]; 1435} 1436 1437static void drawPageBackground(CGContextRef context, WebPageProxy* page, const IntRect& rect) 1438{ 1439 if (!page->drawsBackground()) 1440 return; 1441 1442 CGContextSaveGState(context); 1443 CGContextSetBlendMode(context, kCGBlendModeCopy); 1444 1445 CGColorRef backgroundColor; 1446 if (page->drawsTransparentBackground()) 1447 backgroundColor = CGColorGetConstantColor(kCGColorClear); 1448 else 1449 backgroundColor = CGColorGetConstantColor(kCGColorWhite); 1450 1451 CGContextSetFillColorWithColor(context, backgroundColor); 1452 CGContextFillRect(context, rect); 1453 1454 CGContextRestoreGState(context); 1455} 1456 1457- (void)drawRect:(NSRect)rect 1458{ 1459 LOG(View, "drawRect: x:%g, y:%g, width:%g, height:%g", rect.origin.x, rect.origin.y, rect.size.width, rect.size.height); 1460 _data->_page->endPrinting(); 1461 if (useNewDrawingArea()) { 1462 CGContextRef context = static_cast<CGContextRef>([[NSGraphicsContext currentContext] graphicsPort]); 1463 1464 if (DrawingAreaProxyImpl* drawingArea = static_cast<DrawingAreaProxyImpl*>(_data->_page->drawingArea())) { 1465 const NSRect *rectsBeingDrawn; 1466 NSInteger numRectsBeingDrawn; 1467 [self getRectsBeingDrawn:&rectsBeingDrawn count:&numRectsBeingDrawn]; 1468 for (NSInteger i = 0; i < numRectsBeingDrawn; ++i) { 1469 Region unpaintedRegion; 1470 IntRect rect = enclosingIntRect(rectsBeingDrawn[i]); 1471 drawingArea->paint(context, rect, unpaintedRegion); 1472 1473 Vector<IntRect> unpaintedRects = unpaintedRegion.rects(); 1474 for (size_t i = 0; i < unpaintedRects.size(); ++i) 1475 drawPageBackground(context, _data->_page.get(), unpaintedRects[i]); 1476 } 1477 } else 1478 drawPageBackground(context, _data->_page.get(), enclosingIntRect(rect)); 1479 1480 _data->_page->didDraw(); 1481 return; 1482 } 1483 1484 if (_data->_page->isValid() && _data->_page->drawingArea()) { 1485 CGContextRef context = static_cast<CGContextRef>([[NSGraphicsContext currentContext] graphicsPort]); 1486 _data->_page->drawingArea()->paint(IntRect(rect), context); 1487 _data->_page->didDraw(); 1488 } else if (_data->_page->drawsBackground()) { 1489 [_data->_page->drawsTransparentBackground() ? [NSColor clearColor] : [NSColor whiteColor] set]; 1490 NSRectFill(rect); 1491 } 1492} 1493 1494- (BOOL)isOpaque 1495{ 1496 return _data->_page->drawsBackground(); 1497} 1498 1499- (void)viewDidHide 1500{ 1501 _data->_page->viewStateDidChange(WebPageProxy::ViewIsVisible); 1502} 1503 1504- (void)viewDidUnhide 1505{ 1506 _data->_page->viewStateDidChange(WebPageProxy::ViewIsVisible); 1507} 1508 1509- (BOOL)accessibilityIsIgnored 1510{ 1511 return NO; 1512} 1513 1514- (id)accessibilityHitTest:(NSPoint)point 1515{ 1516 return _data->_remoteAccessibilityChild.get(); 1517} 1518 1519- (id)accessibilityAttributeValue:(NSString*)attribute 1520{ 1521 if ([attribute isEqualToString:NSAccessibilityChildrenAttribute]) { 1522 if (!_data->_remoteAccessibilityChild) 1523 return nil; 1524 return [NSArray arrayWithObject:_data->_remoteAccessibilityChild.get()]; 1525 } 1526 if ([attribute isEqualToString:NSAccessibilityRoleAttribute]) 1527 return NSAccessibilityGroupRole; 1528 if ([attribute isEqualToString:NSAccessibilityRoleDescriptionAttribute]) 1529 return NSAccessibilityRoleDescription(NSAccessibilityGroupRole, nil); 1530 if ([attribute isEqualToString:NSAccessibilityParentAttribute]) 1531 return NSAccessibilityUnignoredAncestor([self superview]); 1532 if ([attribute isEqualToString:NSAccessibilityEnabledAttribute]) 1533 return [NSNumber numberWithBool:YES]; 1534 1535 return [super accessibilityAttributeValue:attribute]; 1536} 1537 1538- (NSView *)hitTest:(NSPoint)point 1539{ 1540 NSView *hitView = [super hitTest:point]; 1541 if (hitView && _data && hitView == _data->_layerHostingView) 1542 hitView = self; 1543 1544#if USE(ACCELERATED_COMPOSITING) 1545 if (hitView && _data && hitView == _data->_oldLayerHostingView) 1546 hitView = self; 1547#endif 1548 return hitView; 1549} 1550 1551- (NSInteger)conversationIdentifier 1552{ 1553 return (NSInteger)self; 1554} 1555 1556 1557- (BOOL)canChangeFrameLayout:(WKFrameRef)frameRef 1558{ 1559 // PDF documents are already paginated, so we can't change them to add headers and footers. 1560 return !toImpl(frameRef)->isMainFrame() || !_data->_pdfViewController; 1561} 1562 1563- (NSPrintOperation *)printOperationWithPrintInfo:(NSPrintInfo *)printInfo forFrame:(WKFrameRef)frameRef 1564{ 1565 LOG(View, "Creating an NSPrintOperation for frame '%s'", toImpl(frameRef)->url().utf8().data()); 1566 1567 // Only the top frame can currently contain a PDF view. 1568 if (_data->_pdfViewController) { 1569 if (!toImpl(frameRef)->isMainFrame()) 1570 return 0; 1571 return _data->_pdfViewController->makePrintOperation(printInfo); 1572 } else { 1573 RetainPtr<WKPrintingView> printingView(AdoptNS, [[WKPrintingView alloc] initWithFrameProxy:toImpl(frameRef)]); 1574 // NSPrintOperation takes ownership of the view. 1575 NSPrintOperation *printOperation = [NSPrintOperation printOperationWithView:printingView.get()]; 1576 [printOperation setCanSpawnSeparateThread:YES]; 1577 printingView->_printOperation = printOperation; 1578 return printOperation; 1579 } 1580} 1581 1582@end 1583 1584@implementation WKView (Internal) 1585 1586- (PassOwnPtr<WebKit::DrawingAreaProxy>)_createDrawingAreaProxy 1587{ 1588 if (useNewDrawingArea()) 1589 return DrawingAreaProxyImpl::create(_data->_page.get()); 1590 1591 return ChunkedUpdateDrawingAreaProxy::create(self, _data->_page.get()); 1592} 1593 1594- (BOOL)_isFocused 1595{ 1596 if (_data->_inBecomeFirstResponder) 1597 return YES; 1598 if (_data->_inResignFirstResponder) 1599 return NO; 1600 return [[self window] firstResponder] == self; 1601} 1602 1603- (void)_processDidCrash 1604{ 1605 [self setNeedsDisplay:YES]; 1606 [self _updateRemoteAccessibilityRegistration:NO]; 1607} 1608 1609- (void)_pageClosed 1610{ 1611 [self _updateRemoteAccessibilityRegistration:NO]; 1612} 1613 1614- (void)_didRelaunchProcess 1615{ 1616 [self setNeedsDisplay:YES]; 1617} 1618 1619- (void)_takeFocus:(BOOL)forward 1620{ 1621 if (forward) 1622 [[self window] selectKeyViewFollowingView:self]; 1623 else 1624 [[self window] selectKeyViewPrecedingView:self]; 1625} 1626 1627- (void)_setCursor:(NSCursor *)cursor 1628{ 1629 if ([NSCursor currentCursor] == cursor) 1630 return; 1631 [cursor set]; 1632} 1633 1634- (void)_setUserInterfaceItemState:(NSString *)commandName enabled:(BOOL)isEnabled state:(int)newState 1635{ 1636 ValidationVector items = _data->_validationMap.take(commandName); 1637 size_t size = items.size(); 1638 for (size_t i = 0; i < size; ++i) { 1639 ValidationItem item = items[i].get(); 1640 [menuItem(item) setState:newState]; 1641 [menuItem(item) setEnabled:isEnabled]; 1642 [toolbarItem(item) setEnabled:isEnabled]; 1643 // FIXME <rdar://problem/8803392>: If the item is neither a menu nor toolbar item, it will be left enabled. 1644 } 1645} 1646 1647- (void)_setEventBeingResent:(NSEvent *)event 1648{ 1649 _data->_keyDownEventBeingResent = [event retain]; 1650} 1651 1652- (Vector<KeypressCommand>&)_interceptKeyEvent:(NSEvent *)theEvent 1653{ 1654 _data->_commandsList.clear(); 1655 // interpretKeyEvents will trigger one or more calls to doCommandBySelector or setText 1656 // that will populate the commandsList vector. 1657 [self interpretKeyEvents:[NSArray arrayWithObject:theEvent]]; 1658 return _data->_commandsList; 1659} 1660 1661- (void)_getTextInputState:(unsigned)start selectionEnd:(unsigned)end underlines:(Vector<WebCore::CompositionUnderline>&)lines 1662{ 1663 start = _data->_selectionStart; 1664 end = _data->_selectionEnd; 1665 lines = _data->_underlines; 1666} 1667 1668- (NSRect)_convertToDeviceSpace:(NSRect)rect 1669{ 1670 return toDeviceSpace(rect, [self window]); 1671} 1672 1673- (NSRect)_convertToUserSpace:(NSRect)rect 1674{ 1675 return toUserSpace(rect, [self window]); 1676} 1677 1678// Any non-zero value will do, but using something recognizable might help us debug some day. 1679#define TRACKING_RECT_TAG 0xBADFACE 1680 1681- (NSTrackingRectTag)addTrackingRect:(NSRect)rect owner:(id)owner userData:(void *)data assumeInside:(BOOL)assumeInside 1682{ 1683 ASSERT(_data->_trackingRectOwner == nil); 1684 _data->_trackingRectOwner = owner; 1685 _data->_trackingRectUserData = data; 1686 return TRACKING_RECT_TAG; 1687} 1688 1689- (NSTrackingRectTag)_addTrackingRect:(NSRect)rect owner:(id)owner userData:(void *)data assumeInside:(BOOL)assumeInside useTrackingNum:(int)tag 1690{ 1691 ASSERT(tag == 0 || tag == TRACKING_RECT_TAG); 1692 ASSERT(_data->_trackingRectOwner == nil); 1693 _data->_trackingRectOwner = owner; 1694 _data->_trackingRectUserData = data; 1695 return TRACKING_RECT_TAG; 1696} 1697 1698- (void)_addTrackingRects:(NSRect *)rects owner:(id)owner userDataList:(void **)userDataList assumeInsideList:(BOOL *)assumeInsideList trackingNums:(NSTrackingRectTag *)trackingNums count:(int)count 1699{ 1700 ASSERT(count == 1); 1701 ASSERT(trackingNums[0] == 0 || trackingNums[0] == TRACKING_RECT_TAG); 1702 ASSERT(_data->_trackingRectOwner == nil); 1703 _data->_trackingRectOwner = owner; 1704 _data->_trackingRectUserData = userDataList[0]; 1705 trackingNums[0] = TRACKING_RECT_TAG; 1706} 1707 1708- (void)removeTrackingRect:(NSTrackingRectTag)tag 1709{ 1710 if (tag == 0) 1711 return; 1712 1713 if (_data && (tag == TRACKING_RECT_TAG)) { 1714 _data->_trackingRectOwner = nil; 1715 return; 1716 } 1717 1718 if (_data && (tag == _data->_lastToolTipTag)) { 1719 [super removeTrackingRect:tag]; 1720 _data->_lastToolTipTag = 0; 1721 return; 1722 } 1723 1724 // If any other tracking rect is being removed, we don't know how it was created 1725 // and it's possible there's a leak involved (see 3500217) 1726 ASSERT_NOT_REACHED(); 1727} 1728 1729- (void)_removeTrackingRects:(NSTrackingRectTag *)tags count:(int)count 1730{ 1731 int i; 1732 for (i = 0; i < count; ++i) { 1733 int tag = tags[i]; 1734 if (tag == 0) 1735 continue; 1736 ASSERT(tag == TRACKING_RECT_TAG); 1737 if (_data != nil) { 1738 _data->_trackingRectOwner = nil; 1739 } 1740 } 1741} 1742 1743- (void)_sendToolTipMouseExited 1744{ 1745 // Nothing matters except window, trackingNumber, and userData. 1746 NSEvent *fakeEvent = [NSEvent enterExitEventWithType:NSMouseExited 1747 location:NSMakePoint(0, 0) 1748 modifierFlags:0 1749 timestamp:0 1750 windowNumber:[[self window] windowNumber] 1751 context:NULL 1752 eventNumber:0 1753 trackingNumber:TRACKING_RECT_TAG 1754 userData:_data->_trackingRectUserData]; 1755 [_data->_trackingRectOwner mouseExited:fakeEvent]; 1756} 1757 1758- (void)_sendToolTipMouseEntered 1759{ 1760 // Nothing matters except window, trackingNumber, and userData. 1761 NSEvent *fakeEvent = [NSEvent enterExitEventWithType:NSMouseEntered 1762 location:NSMakePoint(0, 0) 1763 modifierFlags:0 1764 timestamp:0 1765 windowNumber:[[self window] windowNumber] 1766 context:NULL 1767 eventNumber:0 1768 trackingNumber:TRACKING_RECT_TAG 1769 userData:_data->_trackingRectUserData]; 1770 [_data->_trackingRectOwner mouseEntered:fakeEvent]; 1771} 1772 1773- (NSString *)view:(NSView *)view stringForToolTip:(NSToolTipTag)tag point:(NSPoint)point userData:(void *)data 1774{ 1775 return nsStringFromWebCoreString(_data->_page->toolTip()); 1776} 1777 1778- (void)_toolTipChangedFrom:(NSString *)oldToolTip to:(NSString *)newToolTip 1779{ 1780 if (oldToolTip) 1781 [self _sendToolTipMouseExited]; 1782 1783 if (newToolTip && [newToolTip length] > 0) { 1784 // See radar 3500217 for why we remove all tooltips rather than just the single one we created. 1785 [self removeAllToolTips]; 1786 NSRect wideOpenRect = NSMakeRect(-100000, -100000, 200000, 200000); 1787 _data->_lastToolTipTag = [self addToolTipRect:wideOpenRect owner:self userData:NULL]; 1788 [self _sendToolTipMouseEntered]; 1789 } 1790} 1791 1792- (void)_setFindIndicator:(PassRefPtr<FindIndicator>)findIndicator fadeOut:(BOOL)fadeOut 1793{ 1794 if (!findIndicator) { 1795 _data->_findIndicatorWindow = 0; 1796 return; 1797 } 1798 1799 if (!_data->_findIndicatorWindow) 1800 _data->_findIndicatorWindow = FindIndicatorWindow::create(self); 1801 1802 _data->_findIndicatorWindow->setFindIndicator(findIndicator, fadeOut); 1803} 1804 1805#if USE(ACCELERATED_COMPOSITING) 1806- (void)_startAcceleratedCompositing:(CALayer *)rootLayer 1807{ 1808 if (!_data->_oldLayerHostingView) { 1809 NSView *hostingView = [[NSView alloc] initWithFrame:[self bounds]]; 1810#if !defined(BUILDING_ON_LEOPARD) 1811 [hostingView setAutoresizingMask:(NSViewWidthSizable | NSViewHeightSizable)]; 1812#endif 1813 1814 [self addSubview:hostingView]; 1815 [hostingView release]; 1816 _data->_oldLayerHostingView = hostingView; 1817 } 1818 1819 // Make a container layer, which will get sized/positioned by AppKit and CA. 1820 CALayer *viewLayer = [CALayer layer]; 1821 1822#ifndef NDEBUG 1823 [viewLayer setName:@"hosting layer"]; 1824#endif 1825 1826#if defined(BUILDING_ON_LEOPARD) 1827 // Turn off default animations. 1828 NSNull *nullValue = [NSNull null]; 1829 NSDictionary *actions = [NSDictionary dictionaryWithObjectsAndKeys: 1830 nullValue, @"anchorPoint", 1831 nullValue, @"bounds", 1832 nullValue, @"contents", 1833 nullValue, @"contentsRect", 1834 nullValue, @"opacity", 1835 nullValue, @"position", 1836 nullValue, @"sublayerTransform", 1837 nullValue, @"sublayers", 1838 nullValue, @"transform", 1839 nil]; 1840 [viewLayer setStyle:[NSDictionary dictionaryWithObject:actions forKey:@"actions"]]; 1841#endif 1842 1843#if !defined(BUILDING_ON_LEOPARD) 1844 // If we aren't in the window yet, we'll use the screen's scale factor now, and reset the scale 1845 // via -viewDidMoveToWindow. 1846 CGFloat scaleFactor = [self window] ? [[self window] userSpaceScaleFactor] : [[NSScreen mainScreen] userSpaceScaleFactor]; 1847 [viewLayer setTransform:CATransform3DMakeScale(scaleFactor, scaleFactor, 1)]; 1848#endif 1849 1850 [_data->_oldLayerHostingView setLayer:viewLayer]; 1851 [_data->_oldLayerHostingView setWantsLayer:YES]; 1852 1853 // Parent our root layer in the container layer 1854 [viewLayer addSublayer:rootLayer]; 1855} 1856 1857- (void)_stopAcceleratedCompositing 1858{ 1859 if (_data->_oldLayerHostingView) { 1860 [_data->_oldLayerHostingView setLayer:nil]; 1861 [_data->_oldLayerHostingView setWantsLayer:NO]; 1862 [_data->_oldLayerHostingView removeFromSuperview]; 1863 _data->_oldLayerHostingView = nil; 1864 } 1865} 1866 1867- (void)_switchToDrawingAreaTypeIfNecessary:(DrawingAreaInfo::Type)type 1868{ 1869 DrawingAreaInfo::Type existingDrawingAreaType = _data->_page->drawingArea() ? _data->_page->drawingArea()->info().type : DrawingAreaInfo::None; 1870 if (existingDrawingAreaType == type) 1871 return; 1872 1873 OwnPtr<DrawingAreaProxy> newDrawingArea; 1874 switch (type) { 1875 case DrawingAreaInfo::Impl: 1876 case DrawingAreaInfo::None: 1877 break; 1878 case DrawingAreaInfo::ChunkedUpdate: { 1879 newDrawingArea = ChunkedUpdateDrawingAreaProxy::create(self, _data->_page.get()); 1880 break; 1881 } 1882 case DrawingAreaInfo::LayerBacked: { 1883 newDrawingArea = LayerBackedDrawingAreaProxy::create(self, _data->_page.get()); 1884 break; 1885 } 1886 } 1887 1888 newDrawingArea->setSize(IntSize([self frame].size), IntSize()); 1889 1890 _data->_page->drawingArea()->detachCompositingContext(); 1891 _data->_page->setDrawingArea(newDrawingArea.release()); 1892} 1893 1894- (void)_enterAcceleratedCompositingMode:(const LayerTreeContext&)layerTreeContext 1895{ 1896 ASSERT(!_data->_layerHostingView); 1897 ASSERT(!layerTreeContext.isEmpty()); 1898 1899 // Create an NSView that will host our layer tree. 1900 _data->_layerHostingView.adoptNS([[NSView alloc] initWithFrame:[self bounds]]); 1901 [_data->_layerHostingView.get() setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; 1902 [self addSubview:_data->_layerHostingView.get()]; 1903 1904 // Create a root layer that will back the NSView. 1905 RetainPtr<CALayer> rootLayer(AdoptNS, [[CALayer alloc] init]); 1906#ifndef NDEBUG 1907 [rootLayer.get() setName:@"Hosting root layer"]; 1908#endif 1909 1910 CALayer *renderLayer = WKMakeRenderLayer(layerTreeContext.contextID); 1911 [rootLayer.get() addSublayer:renderLayer]; 1912 1913 [_data->_layerHostingView.get() setLayer:rootLayer.get()]; 1914 [_data->_layerHostingView.get() setWantsLayer:YES]; 1915} 1916 1917- (void)_exitAcceleratedCompositingMode 1918{ 1919 ASSERT(_data->_layerHostingView); 1920 1921 [_data->_layerHostingView.get() setLayer:nil]; 1922 [_data->_layerHostingView.get() setWantsLayer:NO]; 1923 [_data->_layerHostingView.get() removeFromSuperview]; 1924 1925 _data->_layerHostingView = nullptr; 1926} 1927 1928- (void)_pageDidEnterAcceleratedCompositing 1929{ 1930 [self _switchToDrawingAreaTypeIfNecessary:DrawingAreaInfo::LayerBacked]; 1931} 1932 1933- (void)_pageDidLeaveAcceleratedCompositing 1934{ 1935 // FIXME: we may want to avoid flipping back to the non-layer-backed drawing area until the next page load, to avoid thrashing. 1936 [self _switchToDrawingAreaTypeIfNecessary:DrawingAreaInfo::ChunkedUpdate]; 1937} 1938#endif // USE(ACCELERATED_COMPOSITING) 1939 1940- (void)_setAccessibilityWebProcessToken:(NSData *)data 1941{ 1942#if !defined(BUILDING_ON_SNOW_LEOPARD) 1943 _data->_remoteAccessibilityChild = WKAXRemoteElementForToken(data); 1944 [self _updateRemoteAccessibilityRegistration:YES]; 1945#endif 1946} 1947 1948- (void)_setComplexTextInputEnabled:(BOOL)complexTextInputEnabled pluginComplexTextInputIdentifier:(uint64_t)pluginComplexTextInputIdentifier 1949{ 1950 BOOL inputSourceChanged = _data->_pluginComplexTextInputIdentifier; 1951 1952 if (complexTextInputEnabled) { 1953 // Check if we're already allowing text input for this plug-in. 1954 if (pluginComplexTextInputIdentifier == _data->_pluginComplexTextInputIdentifier) 1955 return; 1956 1957 _data->_pluginComplexTextInputIdentifier = pluginComplexTextInputIdentifier; 1958 1959 } else { 1960 // Check if we got a request to disable complex text input for a plug-in that is not the current plug-in. 1961 if (pluginComplexTextInputIdentifier != _data->_pluginComplexTextInputIdentifier) 1962 return; 1963 1964 _data->_pluginComplexTextInputIdentifier = 0; 1965 } 1966 1967 if (inputSourceChanged) { 1968 // Inform the out of line window that the input source changed. 1969 [[WKTextInputWindowController sharedTextInputWindowController] keyboardInputSourceChanged]; 1970 } 1971} 1972 1973- (void)_setPageHasCustomRepresentation:(BOOL)pageHasCustomRepresentation 1974{ 1975 _data->_pdfViewController = nullptr; 1976 1977 if (pageHasCustomRepresentation) 1978 _data->_pdfViewController = PDFViewController::create(self); 1979} 1980 1981- (void)_didFinishLoadingDataForCustomRepresentation:(const CoreIPC::DataReference&)dataReference 1982{ 1983 ASSERT(_data->_pdfViewController); 1984 1985 _data->_pdfViewController->setPDFDocumentData(_data->_page->mainFrame()->mimeType(), dataReference); 1986} 1987 1988- (double)_customRepresentationZoomFactor 1989{ 1990 if (!_data->_pdfViewController) 1991 return 1; 1992 1993 return _data->_pdfViewController->zoomFactor(); 1994} 1995 1996- (void)_setCustomRepresentationZoomFactor:(double)zoomFactor 1997{ 1998 if (!_data->_pdfViewController) 1999 return; 2000 2001 _data->_pdfViewController->setZoomFactor(zoomFactor); 2002} 2003 2004- (void)_setDragImage:(NSImage *)image at:(NSPoint)clientPoint linkDrag:(BOOL)linkDrag 2005{ 2006 // We need to prevent re-entering this call to avoid crashing in AppKit. 2007 // Given the asynchronous nature of WebKit2 this can now happen. 2008 if (_data->_dragHasStarted) 2009 return; 2010 2011 _data->_dragHasStarted = YES; 2012 [super dragImage:image 2013 at:clientPoint 2014 offset:NSZeroSize 2015 event:(linkDrag) ? [NSApp currentEvent] :_data->_mouseDownEvent 2016 pasteboard:[NSPasteboard pasteboardWithName:NSDragPboard] 2017 source:self 2018 slideBack:YES]; 2019 _data->_dragHasStarted = NO; 2020} 2021 2022- (void)_setDrawingAreaSize:(NSSize)size 2023{ 2024 if (!_data->_page->drawingArea()) 2025 return; 2026 2027 _data->_page->drawingArea()->setSize(IntSize(size), IntSize(_data->_resizeScrollOffset)); 2028 _data->_resizeScrollOffset = NSZeroSize; 2029} 2030 2031- (void)_didChangeScrollbarsForMainFrame 2032{ 2033 [self _updateGrowBoxForWindowFrameChange]; 2034} 2035 2036@end 2037 2038@implementation WKView (Private) 2039 2040- (void)disableFrameSizeUpdates 2041{ 2042 _frameSizeUpdatesDisabledCount++; 2043} 2044 2045- (void)enableFrameSizeUpdates 2046{ 2047 if (!_frameSizeUpdatesDisabledCount) 2048 return; 2049 2050 if (!(--_frameSizeUpdatesDisabledCount)) 2051 [self _setDrawingAreaSize:[self frame].size]; 2052} 2053 2054- (BOOL)frameSizeUpdatesDisabled 2055{ 2056 return _frameSizeUpdatesDisabledCount > 0; 2057} 2058 2059@end 2060 2061