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#import "AttributedString.h" 30#import "ChunkedUpdateDrawingAreaProxy.h" 31#import "DataReference.h" 32#import "DrawingAreaProxyImpl.h" 33#import "EditorState.h" 34#import "FindIndicator.h" 35#import "FindIndicatorWindow.h" 36#import "LayerTreeContext.h" 37#import "Logging.h" 38#import "NativeWebKeyboardEvent.h" 39#import "NativeWebMouseEvent.h" 40#import "NativeWebWheelEvent.h" 41#import "PDFViewController.h" 42#import "PageClientImpl.h" 43#import "PasteboardTypes.h" 44#import "Region.h" 45#import "RunLoop.h" 46#import "TextChecker.h" 47#import "TextCheckerState.h" 48#import "WKAPICast.h" 49#import "WKFullScreenWindowController.h" 50#import "WKPrintingView.h" 51#import "WKStringCF.h" 52#import "WKTextInputWindowController.h" 53#import "WKViewInternal.h" 54#import "WKViewPrivate.h" 55#import "WebContext.h" 56#import "WebEventFactory.h" 57#import "WebFullScreenManagerProxy.h" 58#import "WebPage.h" 59#import "WebPageProxy.h" 60#import "WebProcessProxy.h" 61#import "WebSystemInterface.h" 62#import <QuartzCore/QuartzCore.h> 63#import <WebCore/ColorMac.h> 64#import <WebCore/DragController.h> 65#import <WebCore/DragData.h> 66#import <WebCore/LocalizedStrings.h> 67#import <WebCore/FloatRect.h> 68#import <WebCore/IntRect.h> 69#import <WebCore/KeyboardEvent.h> 70#import <WebCore/PlatformMouseEvent.h> 71#import <WebCore/PlatformScreen.h> 72#import <WebKitSystemInterface.h> 73#import <wtf/RefPtr.h> 74#import <wtf/RetainPtr.h> 75 76@interface NSApplication (WKNSApplicationDetails) 77- (void)speakString:(NSString *)string; 78- (void)_setCurrentEvent:(NSEvent *)event; 79@end 80 81@interface NSObject (WKNSTextInputContextDetails) 82- (BOOL)wantsToHandleMouseEvents; 83- (BOOL)handleMouseEvent:(NSEvent *)event; 84@end 85 86@interface NSWindow (WKNSWindowDetails) 87- (NSRect)_growBoxRect; 88- (id)_growBoxOwner; 89- (void)_setShowOpaqueGrowBoxForOwner:(id)owner; 90- (BOOL)_updateGrowBoxForWindowFrameChange; 91- (NSRect)_intersectBottomCornersWithRect:(NSRect)viewRect; 92- (void)_maskRoundedBottomCorners:(NSRect)clipRect; 93@end 94 95using namespace WebKit; 96using namespace WebCore; 97 98namespace WebKit { 99 100typedef id <NSValidatedUserInterfaceItem> ValidationItem; 101typedef Vector<RetainPtr<ValidationItem> > ValidationVector; 102typedef HashMap<String, ValidationVector> ValidationMap; 103 104} 105 106struct WKViewInterpretKeyEventsParameters { 107 bool eventInterpretationHadSideEffects; 108 bool consumedByIM; 109 bool executingSavedKeypressCommands; 110 Vector<KeypressCommand>* commands; 111}; 112 113@interface WKViewData : NSObject { 114@public 115 OwnPtr<PageClientImpl> _pageClient; 116 RefPtr<WebPageProxy> _page; 117 118 // For ToolTips. 119 NSToolTipTag _lastToolTipTag; 120 id _trackingRectOwner; 121 void* _trackingRectUserData; 122 123 RetainPtr<NSView> _layerHostingView; 124 125 RetainPtr<id> _remoteAccessibilityChild; 126 127 // For asynchronous validation. 128 ValidationMap _validationMap; 129 130 OwnPtr<PDFViewController> _pdfViewController; 131 132 OwnPtr<FindIndicatorWindow> _findIndicatorWindow; 133 // We keep here the event when resending it to 134 // the application to distinguish the case of a new event from one 135 // that has been already sent to WebCore. 136 RetainPtr<NSEvent> _keyDownEventBeingResent; 137 WKViewInterpretKeyEventsParameters* _interpretKeyEventsParameters; 138 139 NSSize _resizeScrollOffset; 140 141 // The identifier of the plug-in we want to send complex text input to, or 0 if there is none. 142 uint64_t _pluginComplexTextInputIdentifier; 143 144 bool _inBecomeFirstResponder; 145 bool _inResignFirstResponder; 146 NSEvent *_mouseDownEvent; 147 BOOL _ignoringMouseDraggedEvents; 148 BOOL _dragHasStarted; 149 150#if ENABLE(GESTURE_EVENTS) 151 id _endGestureMonitor; 152#endif 153 154#if ENABLE(FULLSCREEN_API) 155 RetainPtr<WKFullScreenWindowController> _fullScreenWindowController; 156#endif 157 158 BOOL _hasSpellCheckerDocumentTag; 159 NSInteger _spellCheckerDocumentTag; 160 161 BOOL _inSecureInputState; 162 163 NSRect _windowBottomCornerIntersectionRect; 164} 165@end 166 167@interface WKResponderChainSink : NSResponder { 168 NSResponder *_lastResponderInChain; 169 bool _didReceiveUnhandledCommand; 170} 171- (id)initWithResponderChain:(NSResponder *)chain; 172- (void)detach; 173- (bool)didReceiveUnhandledCommand; 174@end 175 176@implementation WKViewData 177@end 178 179@implementation WKView 180 181- (id)initWithFrame:(NSRect)frame 182{ 183 return [self initWithFrame:frame contextRef:toAPI(WebContext::sharedProcessContext())]; 184} 185 186- (id)initWithFrame:(NSRect)frame contextRef:(WKContextRef)contextRef 187{ 188 return [self initWithFrame:frame contextRef:contextRef pageGroupRef:nil]; 189} 190 191- (void)_registerDraggedTypes 192{ 193 NSMutableSet *types = [[NSMutableSet alloc] initWithArray:PasteboardTypes::forEditing()]; 194 [types addObjectsFromArray:PasteboardTypes::forURL()]; 195 [self registerForDraggedTypes:[types allObjects]]; 196 [types release]; 197} 198 199- (void)_updateRemoteAccessibilityRegistration:(BOOL)registerProcess 200{ 201 // When the tree is connected/disconnected, the remote accessibility registration 202 // needs to be updated with the pid of the remote process. If the process is going 203 // away, that information is not present in WebProcess 204 pid_t pid = 0; 205 if (registerProcess && _data->_page->process()) 206 pid = _data->_page->process()->processIdentifier(); 207 else if (!registerProcess) { 208 pid = WKAXRemoteProcessIdentifier(_data->_remoteAccessibilityChild.get()); 209 _data->_remoteAccessibilityChild = nil; 210 } 211 if (pid) 212 WKAXRegisterRemoteProcess(registerProcess, pid); 213} 214 215- (id)initWithFrame:(NSRect)frame contextRef:(WKContextRef)contextRef pageGroupRef:(WKPageGroupRef)pageGroupRef 216{ 217 self = [super initWithFrame:frame]; 218 if (!self) 219 return nil; 220 221 [NSApp registerServicesMenuSendTypes:PasteboardTypes::forSelection() returnTypes:PasteboardTypes::forEditing()]; 222 223 InitWebCoreSystemInterface(); 224 RunLoop::initializeMainRunLoop(); 225 226 NSTrackingArea *trackingArea = [[NSTrackingArea alloc] initWithRect:frame 227 options:(NSTrackingMouseMoved | NSTrackingMouseEnteredAndExited | NSTrackingActiveInKeyWindow | NSTrackingInVisibleRect) 228 owner:self 229 userInfo:nil]; 230 [self addTrackingArea:trackingArea]; 231 [trackingArea release]; 232 233 _data = [[WKViewData alloc] init]; 234 235 _data->_pageClient = PageClientImpl::create(self); 236 _data->_page = toImpl(contextRef)->createWebPage(_data->_pageClient.get(), toImpl(pageGroupRef)); 237 _data->_page->initializeWebPage(); 238#if ENABLE(FULLSCREEN_API) 239 _data->_page->fullScreenManager()->setWebView(self); 240#endif 241 _data->_mouseDownEvent = nil; 242 _data->_ignoringMouseDraggedEvents = NO; 243 244 [self _registerDraggedTypes]; 245 246 WebContext::statistics().wkViewCount++; 247 248 return self; 249} 250 251- (void)dealloc 252{ 253 _data->_page->close(); 254 255 ASSERT(!_data->_inSecureInputState); 256 257 [_data release]; 258 _data = nil; 259 260 WebContext::statistics().wkViewCount--; 261 262 [super dealloc]; 263} 264 265- (WKPageRef)pageRef 266{ 267 return toAPI(_data->_page.get()); 268} 269 270- (void)setDrawsBackground:(BOOL)drawsBackground 271{ 272 _data->_page->setDrawsBackground(drawsBackground); 273} 274 275- (BOOL)drawsBackground 276{ 277 return _data->_page->drawsBackground(); 278} 279 280- (void)setDrawsTransparentBackground:(BOOL)drawsTransparentBackground 281{ 282 _data->_page->setDrawsTransparentBackground(drawsTransparentBackground); 283} 284 285- (BOOL)drawsTransparentBackground 286{ 287 return _data->_page->drawsTransparentBackground(); 288} 289 290- (BOOL)acceptsFirstResponder 291{ 292 return YES; 293} 294 295- (BOOL)becomeFirstResponder 296{ 297 NSSelectionDirection direction = [[self window] keyViewSelectionDirection]; 298 299 _data->_inBecomeFirstResponder = true; 300 301 [self _updateSecureInputState]; 302 _data->_page->viewStateDidChange(WebPageProxy::ViewIsFocused); 303 304 _data->_inBecomeFirstResponder = false; 305 306 if (direction != NSDirectSelection) 307 _data->_page->setInitialFocus(direction == NSSelectingNext); 308 309 return YES; 310} 311 312- (BOOL)resignFirstResponder 313{ 314 _data->_inResignFirstResponder = true; 315 316 if (_data->_page->editorState().hasComposition && !_data->_page->editorState().shouldIgnoreCompositionSelectionChange) 317 _data->_page->confirmCompositionWithoutDisturbingSelection(); 318 [self _resetTextInputState]; 319 320 if (!_data->_page->maintainsInactiveSelection()) 321 _data->_page->clearSelection(); 322 323 _data->_page->viewStateDidChange(WebPageProxy::ViewIsFocused); 324 325 _data->_inResignFirstResponder = false; 326 327 return YES; 328} 329 330- (void)viewWillStartLiveResize 331{ 332 _data->_page->viewWillStartLiveResize(); 333} 334 335- (void)viewDidEndLiveResize 336{ 337 _data->_page->viewWillEndLiveResize(); 338} 339 340- (BOOL)isFlipped 341{ 342 return YES; 343} 344 345- (void)setFrame:(NSRect)rect andScrollBy:(NSSize)offset 346{ 347 ASSERT(NSEqualSizes(_data->_resizeScrollOffset, NSZeroSize)); 348 349 _data->_resizeScrollOffset = offset; 350 [self setFrame:rect]; 351} 352 353- (void)setFrameSize:(NSSize)size 354{ 355 [super setFrameSize:size]; 356 357 if (![self frameSizeUpdatesDisabled]) 358 [self _setDrawingAreaSize:size]; 359} 360 361- (void)_updateWindowAndViewFrames 362{ 363 NSWindow *window = [self window]; 364 ASSERT(window); 365 366 NSRect windowFrameInScreenCoordinates = [window frame]; 367 NSRect viewFrameInWindowCoordinates = [self convertRect:[self frame] toView:nil]; 368 NSPoint accessibilityPosition = [[self accessibilityAttributeValue:NSAccessibilityPositionAttribute] pointValue]; 369 370 _data->_page->windowAndViewFramesChanged(enclosingIntRect(windowFrameInScreenCoordinates), enclosingIntRect(viewFrameInWindowCoordinates), IntPoint(accessibilityPosition)); 371} 372 373- (void)renewGState 374{ 375 // Hide the find indicator. 376 _data->_findIndicatorWindow = nullptr; 377 378 // Update the view frame. 379 if ([self window]) 380 [self _updateWindowAndViewFrames]; 381 382 [super renewGState]; 383} 384 385typedef HashMap<SEL, String> SelectorNameMap; 386 387// Map selectors into Editor command names. 388// This is not needed for any selectors that have the same name as the Editor command. 389static const SelectorNameMap* createSelectorExceptionMap() 390{ 391 SelectorNameMap* map = new HashMap<SEL, String>; 392 393 map->add(@selector(insertNewlineIgnoringFieldEditor:), "InsertNewline"); 394 map->add(@selector(insertParagraphSeparator:), "InsertNewline"); 395 map->add(@selector(insertTabIgnoringFieldEditor:), "InsertTab"); 396 map->add(@selector(pageDown:), "MovePageDown"); 397 map->add(@selector(pageDownAndModifySelection:), "MovePageDownAndModifySelection"); 398 map->add(@selector(pageUp:), "MovePageUp"); 399 map->add(@selector(pageUpAndModifySelection:), "MovePageUpAndModifySelection"); 400 map->add(@selector(scrollPageDown:), "ScrollPageForward"); 401 map->add(@selector(scrollPageUp:), "ScrollPageBackward"); 402 403 return map; 404} 405 406static String commandNameForSelector(SEL selector) 407{ 408 // Check the exception map first. 409 static const SelectorNameMap* exceptionMap = createSelectorExceptionMap(); 410 SelectorNameMap::const_iterator it = exceptionMap->find(selector); 411 if (it != exceptionMap->end()) 412 return it->second; 413 414 // Remove the trailing colon. 415 // No need to capitalize the command name since Editor command names are 416 // not case sensitive. 417 const char* selectorName = sel_getName(selector); 418 size_t selectorNameLength = strlen(selectorName); 419 if (selectorNameLength < 2 || selectorName[selectorNameLength - 1] != ':') 420 return String(); 421 return String(selectorName, selectorNameLength - 1); 422} 423 424// Editing commands 425 426#define WEBCORE_COMMAND(command) - (void)command:(id)sender { _data->_page->executeEditCommand(commandNameForSelector(_cmd)); } 427 428WEBCORE_COMMAND(alignCenter) 429WEBCORE_COMMAND(alignJustified) 430WEBCORE_COMMAND(alignLeft) 431WEBCORE_COMMAND(alignRight) 432WEBCORE_COMMAND(copy) 433WEBCORE_COMMAND(cut) 434WEBCORE_COMMAND(delete) 435WEBCORE_COMMAND(deleteBackward) 436WEBCORE_COMMAND(deleteBackwardByDecomposingPreviousCharacter) 437WEBCORE_COMMAND(deleteForward) 438WEBCORE_COMMAND(deleteToBeginningOfLine) 439WEBCORE_COMMAND(deleteToBeginningOfParagraph) 440WEBCORE_COMMAND(deleteToEndOfLine) 441WEBCORE_COMMAND(deleteToEndOfParagraph) 442WEBCORE_COMMAND(deleteToMark) 443WEBCORE_COMMAND(deleteWordBackward) 444WEBCORE_COMMAND(deleteWordForward) 445WEBCORE_COMMAND(ignoreSpelling) 446WEBCORE_COMMAND(indent) 447WEBCORE_COMMAND(insertBacktab) 448WEBCORE_COMMAND(insertLineBreak) 449WEBCORE_COMMAND(insertNewline) 450WEBCORE_COMMAND(insertNewlineIgnoringFieldEditor) 451WEBCORE_COMMAND(insertParagraphSeparator) 452WEBCORE_COMMAND(insertTab) 453WEBCORE_COMMAND(insertTabIgnoringFieldEditor) 454WEBCORE_COMMAND(makeTextWritingDirectionLeftToRight) 455WEBCORE_COMMAND(makeTextWritingDirectionNatural) 456WEBCORE_COMMAND(makeTextWritingDirectionRightToLeft) 457WEBCORE_COMMAND(moveBackward) 458WEBCORE_COMMAND(moveBackwardAndModifySelection) 459WEBCORE_COMMAND(moveDown) 460WEBCORE_COMMAND(moveDownAndModifySelection) 461WEBCORE_COMMAND(moveForward) 462WEBCORE_COMMAND(moveForwardAndModifySelection) 463WEBCORE_COMMAND(moveLeft) 464WEBCORE_COMMAND(moveLeftAndModifySelection) 465WEBCORE_COMMAND(moveParagraphBackwardAndModifySelection) 466WEBCORE_COMMAND(moveParagraphForwardAndModifySelection) 467WEBCORE_COMMAND(moveRight) 468WEBCORE_COMMAND(moveRightAndModifySelection) 469WEBCORE_COMMAND(moveToBeginningOfDocument) 470WEBCORE_COMMAND(moveToBeginningOfDocumentAndModifySelection) 471WEBCORE_COMMAND(moveToBeginningOfLine) 472WEBCORE_COMMAND(moveToBeginningOfLineAndModifySelection) 473WEBCORE_COMMAND(moveToBeginningOfParagraph) 474WEBCORE_COMMAND(moveToBeginningOfParagraphAndModifySelection) 475WEBCORE_COMMAND(moveToBeginningOfSentence) 476WEBCORE_COMMAND(moveToBeginningOfSentenceAndModifySelection) 477WEBCORE_COMMAND(moveToEndOfDocument) 478WEBCORE_COMMAND(moveToEndOfDocumentAndModifySelection) 479WEBCORE_COMMAND(moveToEndOfLine) 480WEBCORE_COMMAND(moveToEndOfLineAndModifySelection) 481WEBCORE_COMMAND(moveToEndOfParagraph) 482WEBCORE_COMMAND(moveToEndOfParagraphAndModifySelection) 483WEBCORE_COMMAND(moveToEndOfSentence) 484WEBCORE_COMMAND(moveToEndOfSentenceAndModifySelection) 485WEBCORE_COMMAND(moveToLeftEndOfLine) 486WEBCORE_COMMAND(moveToLeftEndOfLineAndModifySelection) 487WEBCORE_COMMAND(moveToRightEndOfLine) 488WEBCORE_COMMAND(moveToRightEndOfLineAndModifySelection) 489WEBCORE_COMMAND(moveUp) 490WEBCORE_COMMAND(moveUpAndModifySelection) 491WEBCORE_COMMAND(moveWordBackward) 492WEBCORE_COMMAND(moveWordBackwardAndModifySelection) 493WEBCORE_COMMAND(moveWordForward) 494WEBCORE_COMMAND(moveWordForwardAndModifySelection) 495WEBCORE_COMMAND(moveWordLeft) 496WEBCORE_COMMAND(moveWordLeftAndModifySelection) 497WEBCORE_COMMAND(moveWordRight) 498WEBCORE_COMMAND(moveWordRightAndModifySelection) 499WEBCORE_COMMAND(outdent) 500WEBCORE_COMMAND(pageDown) 501WEBCORE_COMMAND(pageDownAndModifySelection) 502WEBCORE_COMMAND(pageUp) 503WEBCORE_COMMAND(pageUpAndModifySelection) 504WEBCORE_COMMAND(paste) 505WEBCORE_COMMAND(pasteAsPlainText) 506WEBCORE_COMMAND(scrollPageDown) 507WEBCORE_COMMAND(scrollPageUp) 508WEBCORE_COMMAND(scrollToBeginningOfDocument) 509WEBCORE_COMMAND(scrollToEndOfDocument) 510WEBCORE_COMMAND(selectAll) 511WEBCORE_COMMAND(selectLine) 512WEBCORE_COMMAND(selectParagraph) 513WEBCORE_COMMAND(selectSentence) 514WEBCORE_COMMAND(selectToMark) 515WEBCORE_COMMAND(selectWord) 516WEBCORE_COMMAND(setMark) 517WEBCORE_COMMAND(subscript) 518WEBCORE_COMMAND(superscript) 519WEBCORE_COMMAND(swapWithMark) 520WEBCORE_COMMAND(takeFindStringFromSelection) 521WEBCORE_COMMAND(transpose) 522WEBCORE_COMMAND(underline) 523WEBCORE_COMMAND(unscript) 524WEBCORE_COMMAND(yank) 525WEBCORE_COMMAND(yankAndSelect) 526 527#undef WEBCORE_COMMAND 528 529// This method is needed to support Mac OS X services. 530 531- (BOOL)writeSelectionToPasteboard:(NSPasteboard *)pasteboard types:(NSArray *)types 532{ 533 Vector<String> pasteboardTypes; 534 size_t numTypes = [types count]; 535 for (size_t i = 0; i < numTypes; ++i) 536 pasteboardTypes.append([types objectAtIndex:i]); 537 return _data->_page->writeSelectionToPasteboard([pasteboard name], pasteboardTypes); 538} 539 540// This method is needed to support Mac OS X services. 541 542- (id)validRequestorForSendType:(NSString *)sendType returnType:(NSString *)returnType 543{ 544 BOOL isValidSendType = !sendType || ([PasteboardTypes::forSelection() containsObject:sendType] && !_data->_page->editorState().selectionIsNone); 545 BOOL isValidReturnType = NO; 546 if (!returnType) 547 isValidReturnType = YES; 548 else if ([PasteboardTypes::forEditing() containsObject:returnType] && _data->_page->editorState().isContentEditable) { 549 // We can insert strings in any editable context. We can insert other types, like images, only in rich edit contexts. 550 isValidReturnType = _data->_page->editorState().isContentRichlyEditable || [returnType isEqualToString:NSStringPboardType]; 551 } 552 if (isValidSendType && isValidReturnType) 553 return self; 554 return [[self nextResponder] validRequestorForSendType:sendType returnType:returnType]; 555} 556 557// This method is needed to support Mac OS X services. 558 559- (BOOL)readSelectionFromPasteboard:(NSPasteboard *)pasteboard 560{ 561 return _data->_page->readSelectionFromPasteboard([pasteboard name]); 562} 563 564/* 565 566When possible, editing-related methods should be implemented in WebCore with the 567EditorCommand mechanism and invoked via WEBCORE_COMMAND, rather than implementing 568individual methods here with Mac-specific code. 569 570Editing-related methods still unimplemented that are implemented in WebKit1: 571 572- (void)capitalizeWord:(id)sender; 573- (void)centerSelectionInVisibleArea:(id)sender; 574- (void)changeFont:(id)sender; 575- (void)complete:(id)sender; 576- (void)copyFont:(id)sender; 577- (void)lowercaseWord:(id)sender; 578- (void)makeBaseWritingDirectionLeftToRight:(id)sender; 579- (void)makeBaseWritingDirectionNatural:(id)sender; 580- (void)makeBaseWritingDirectionRightToLeft:(id)sender; 581- (void)pasteFont:(id)sender; 582- (void)scrollLineDown:(id)sender; 583- (void)scrollLineUp:(id)sender; 584- (void)showGuessPanel:(id)sender; 585- (void)uppercaseWord:(id)sender; 586 587Some other editing-related methods still unimplemented: 588 589- (void)changeCaseOfLetter:(id)sender; 590- (void)copyRuler:(id)sender; 591- (void)insertContainerBreak:(id)sender; 592- (void)insertDoubleQuoteIgnoringSubstitution:(id)sender; 593- (void)insertSingleQuoteIgnoringSubstitution:(id)sender; 594- (void)pasteRuler:(id)sender; 595- (void)toggleRuler:(id)sender; 596- (void)transposeWords:(id)sender; 597 598*/ 599 600// Menu items validation 601 602static NSMenuItem *menuItem(id <NSValidatedUserInterfaceItem> item) 603{ 604 if (![(NSObject *)item isKindOfClass:[NSMenuItem class]]) 605 return nil; 606 return (NSMenuItem *)item; 607} 608 609static NSToolbarItem *toolbarItem(id <NSValidatedUserInterfaceItem> item) 610{ 611 if (![(NSObject *)item isKindOfClass:[NSToolbarItem class]]) 612 return nil; 613 return (NSToolbarItem *)item; 614} 615 616static void validateCommandCallback(WKStringRef commandName, bool isEnabled, int32_t state, WKErrorRef error, void* context) 617{ 618 // If the process exits before the command can be validated, we'll be called back with an error. 619 if (error) 620 return; 621 622 WKView* wkView = static_cast<WKView*>(context); 623 ASSERT(wkView); 624 625 [wkView _setUserInterfaceItemState:nsStringFromWebCoreString(toImpl(commandName)->string()) enabled:isEnabled state:state]; 626} 627 628- (BOOL)validateUserInterfaceItem:(id <NSValidatedUserInterfaceItem>)item 629{ 630 SEL action = [item action]; 631 632 if (action == @selector(showGuessPanel:)) { 633 if (NSMenuItem *menuItem = ::menuItem(item)) 634 [menuItem setTitle:contextMenuItemTagShowSpellingPanel(![[[NSSpellChecker sharedSpellChecker] spellingPanel] isVisible])]; 635 return _data->_page->editorState().isContentEditable; 636 } 637 638 if (action == @selector(checkSpelling:) || action == @selector(changeSpelling:)) 639 return _data->_page->editorState().isContentEditable; 640 641 if (action == @selector(toggleContinuousSpellChecking:)) { 642 bool enabled = TextChecker::isContinuousSpellCheckingAllowed(); 643 bool checked = enabled && TextChecker::state().isContinuousSpellCheckingEnabled; 644 [menuItem(item) setState:checked ? NSOnState : NSOffState]; 645 return enabled; 646 } 647 648 if (action == @selector(toggleGrammarChecking:)) { 649 bool checked = TextChecker::state().isGrammarCheckingEnabled; 650 [menuItem(item) setState:checked ? NSOnState : NSOffState]; 651 return YES; 652 } 653 654 if (action == @selector(toggleAutomaticSpellingCorrection:)) { 655 bool checked = TextChecker::state().isAutomaticSpellingCorrectionEnabled; 656 [menuItem(item) setState:checked ? NSOnState : NSOffState]; 657 return _data->_page->editorState().isContentEditable; 658 } 659 660 if (action == @selector(orderFrontSubstitutionsPanel:)) { 661 if (NSMenuItem *menuItem = ::menuItem(item)) 662 [menuItem setTitle:contextMenuItemTagShowSubstitutions(![[[NSSpellChecker sharedSpellChecker] substitutionsPanel] isVisible])]; 663 return _data->_page->editorState().isContentEditable; 664 } 665 666 if (action == @selector(toggleSmartInsertDelete:)) { 667 bool checked = _data->_page->isSmartInsertDeleteEnabled(); 668 [menuItem(item) setState:checked ? NSOnState : NSOffState]; 669 return _data->_page->editorState().isContentEditable; 670 } 671 672 if (action == @selector(toggleAutomaticQuoteSubstitution:)) { 673 bool checked = TextChecker::state().isAutomaticQuoteSubstitutionEnabled; 674 [menuItem(item) setState:checked ? NSOnState : NSOffState]; 675 return _data->_page->editorState().isContentEditable; 676 } 677 678 if (action == @selector(toggleAutomaticDashSubstitution:)) { 679 bool checked = TextChecker::state().isAutomaticDashSubstitutionEnabled; 680 [menuItem(item) setState:checked ? NSOnState : NSOffState]; 681 return _data->_page->editorState().isContentEditable; 682 } 683 684 if (action == @selector(toggleAutomaticLinkDetection:)) { 685 bool checked = TextChecker::state().isAutomaticLinkDetectionEnabled; 686 [menuItem(item) setState:checked ? NSOnState : NSOffState]; 687 return _data->_page->editorState().isContentEditable; 688 } 689 690 if (action == @selector(toggleAutomaticTextReplacement:)) { 691 bool checked = TextChecker::state().isAutomaticTextReplacementEnabled; 692 [menuItem(item) setState:checked ? NSOnState : NSOffState]; 693 return _data->_page->editorState().isContentEditable; 694 } 695 696 if (action == @selector(uppercaseWord:) || action == @selector(lowercaseWord:) || action == @selector(capitalizeWord:)) 697 return _data->_page->editorState().selectionIsRange && _data->_page->editorState().isContentEditable; 698 699 if (action == @selector(stopSpeaking:)) 700 return [NSApp isSpeaking]; 701 702 // Next, handle editor commands. Start by returning YES for anything that is not an editor command. 703 // Returning YES is the default thing to do in an AppKit validate method for any selector that is not recognized. 704 String commandName = commandNameForSelector([item action]); 705 if (!Editor::commandIsSupportedFromMenuOrKeyBinding(commandName)) 706 return YES; 707 708 // Add this item to the vector of items for a given command that are awaiting validation. 709 pair<ValidationMap::iterator, bool> addResult = _data->_validationMap.add(commandName, ValidationVector()); 710 addResult.first->second.append(item); 711 if (addResult.second) { 712 // If we are not already awaiting validation for this command, start the asynchronous validation process. 713 // FIXME: Theoretically, there is a race here; when we get the answer it might be old, from a previous time 714 // we asked for the same command; there is no guarantee the answer is still valid. 715 _data->_page->validateCommand(commandName, ValidateCommandCallback::create(self, validateCommandCallback)); 716 } 717 718 // Treat as enabled until we get the result back from the web process and _setUserInterfaceItemState is called. 719 // FIXME <rdar://problem/8803459>: This means disabled items will flash enabled at first for a moment. 720 // But returning NO here would be worse; that would make keyboard commands such as command-C fail. 721 return YES; 722} 723 724static void speakString(WKStringRef string, WKErrorRef error, void*) 725{ 726 if (error) 727 return; 728 if (!string) 729 return; 730 731 NSString *convertedString = toImpl(string)->string(); 732 [NSApp speakString:convertedString]; 733} 734 735- (IBAction)startSpeaking:(id)sender 736{ 737 _data->_page->getSelectionOrContentsAsString(StringCallback::create(0, speakString)); 738} 739 740- (IBAction)stopSpeaking:(id)sender 741{ 742 [NSApp stopSpeaking:sender]; 743} 744 745- (IBAction)showGuessPanel:(id)sender 746{ 747 NSSpellChecker *checker = [NSSpellChecker sharedSpellChecker]; 748 if (!checker) { 749 LOG_ERROR("No NSSpellChecker"); 750 return; 751 } 752 753 NSPanel *spellingPanel = [checker spellingPanel]; 754 if ([spellingPanel isVisible]) { 755 [spellingPanel orderOut:sender]; 756 return; 757 } 758 759 _data->_page->advanceToNextMisspelling(true); 760 [spellingPanel orderFront:sender]; 761} 762 763- (IBAction)checkSpelling:(id)sender 764{ 765 _data->_page->advanceToNextMisspelling(false); 766} 767 768- (void)changeSpelling:(id)sender 769{ 770 NSString *word = [[sender selectedCell] stringValue]; 771 772 _data->_page->changeSpellingToWord(word); 773} 774 775- (IBAction)toggleContinuousSpellChecking:(id)sender 776{ 777 bool spellCheckingEnabled = !TextChecker::state().isContinuousSpellCheckingEnabled; 778 TextChecker::setContinuousSpellCheckingEnabled(spellCheckingEnabled); 779 780 _data->_page->process()->updateTextCheckerState(); 781} 782 783- (BOOL)isGrammarCheckingEnabled 784{ 785 return TextChecker::state().isGrammarCheckingEnabled; 786} 787 788- (void)setGrammarCheckingEnabled:(BOOL)flag 789{ 790 if (static_cast<bool>(flag) == TextChecker::state().isGrammarCheckingEnabled) 791 return; 792 793 TextChecker::setGrammarCheckingEnabled(flag); 794 _data->_page->process()->updateTextCheckerState(); 795} 796 797- (IBAction)toggleGrammarChecking:(id)sender 798{ 799 bool grammarCheckingEnabled = !TextChecker::state().isGrammarCheckingEnabled; 800 TextChecker::setGrammarCheckingEnabled(grammarCheckingEnabled); 801 802 _data->_page->process()->updateTextCheckerState(); 803} 804 805- (IBAction)toggleAutomaticSpellingCorrection:(id)sender 806{ 807 TextChecker::setAutomaticSpellingCorrectionEnabled(!TextChecker::state().isAutomaticSpellingCorrectionEnabled); 808 809 _data->_page->process()->updateTextCheckerState(); 810} 811 812- (void)orderFrontSubstitutionsPanel:(id)sender 813{ 814 NSSpellChecker *checker = [NSSpellChecker sharedSpellChecker]; 815 if (!checker) { 816 LOG_ERROR("No NSSpellChecker"); 817 return; 818 } 819 820 NSPanel *substitutionsPanel = [checker substitutionsPanel]; 821 if ([substitutionsPanel isVisible]) { 822 [substitutionsPanel orderOut:sender]; 823 return; 824 } 825 [substitutionsPanel orderFront:sender]; 826} 827 828- (IBAction)toggleSmartInsertDelete:(id)sender 829{ 830 _data->_page->setSmartInsertDeleteEnabled(!_data->_page->isSmartInsertDeleteEnabled()); 831} 832 833- (BOOL)isAutomaticQuoteSubstitutionEnabled 834{ 835 return TextChecker::state().isAutomaticQuoteSubstitutionEnabled; 836} 837 838- (void)setAutomaticQuoteSubstitutionEnabled:(BOOL)flag 839{ 840 if (static_cast<bool>(flag) == TextChecker::state().isAutomaticQuoteSubstitutionEnabled) 841 return; 842 843 TextChecker::setAutomaticQuoteSubstitutionEnabled(flag); 844 _data->_page->process()->updateTextCheckerState(); 845} 846 847- (void)toggleAutomaticQuoteSubstitution:(id)sender 848{ 849 TextChecker::setAutomaticQuoteSubstitutionEnabled(!TextChecker::state().isAutomaticQuoteSubstitutionEnabled); 850 _data->_page->process()->updateTextCheckerState(); 851} 852 853- (BOOL)isAutomaticDashSubstitutionEnabled 854{ 855 return TextChecker::state().isAutomaticDashSubstitutionEnabled; 856} 857 858- (void)setAutomaticDashSubstitutionEnabled:(BOOL)flag 859{ 860 if (static_cast<bool>(flag) == TextChecker::state().isAutomaticDashSubstitutionEnabled) 861 return; 862 863 TextChecker::setAutomaticDashSubstitutionEnabled(flag); 864 _data->_page->process()->updateTextCheckerState(); 865} 866 867- (void)toggleAutomaticDashSubstitution:(id)sender 868{ 869 TextChecker::setAutomaticDashSubstitutionEnabled(!TextChecker::state().isAutomaticDashSubstitutionEnabled); 870 _data->_page->process()->updateTextCheckerState(); 871} 872 873- (BOOL)isAutomaticLinkDetectionEnabled 874{ 875 return TextChecker::state().isAutomaticLinkDetectionEnabled; 876} 877 878- (void)setAutomaticLinkDetectionEnabled:(BOOL)flag 879{ 880 if (static_cast<bool>(flag) == TextChecker::state().isAutomaticLinkDetectionEnabled) 881 return; 882 883 TextChecker::setAutomaticLinkDetectionEnabled(flag); 884 _data->_page->process()->updateTextCheckerState(); 885} 886 887- (void)toggleAutomaticLinkDetection:(id)sender 888{ 889 TextChecker::setAutomaticLinkDetectionEnabled(!TextChecker::state().isAutomaticLinkDetectionEnabled); 890 _data->_page->process()->updateTextCheckerState(); 891} 892 893- (BOOL)isAutomaticTextReplacementEnabled 894{ 895 return TextChecker::state().isAutomaticTextReplacementEnabled; 896} 897 898- (void)setAutomaticTextReplacementEnabled:(BOOL)flag 899{ 900 if (static_cast<bool>(flag) == TextChecker::state().isAutomaticTextReplacementEnabled) 901 return; 902 903 TextChecker::setAutomaticTextReplacementEnabled(flag); 904 _data->_page->process()->updateTextCheckerState(); 905} 906 907- (void)toggleAutomaticTextReplacement:(id)sender 908{ 909 TextChecker::setAutomaticTextReplacementEnabled(!TextChecker::state().isAutomaticTextReplacementEnabled); 910 _data->_page->process()->updateTextCheckerState(); 911} 912 913- (void)uppercaseWord:(id)sender 914{ 915 _data->_page->uppercaseWord(); 916} 917 918- (void)lowercaseWord:(id)sender 919{ 920 _data->_page->lowercaseWord(); 921} 922 923- (void)capitalizeWord:(id)sender 924{ 925 _data->_page->capitalizeWord(); 926} 927 928- (void)displayIfNeeded 929{ 930#if !defined(BUILDING_ON_SNOW_LEOPARD) 931 // FIXME: We should remove this code when <rdar://problem/9362085> is resolved. In the meantime, 932 // it is necessary to disable scren updates so we get a chance to redraw the corners before this 933 // display is visible. 934 NSWindow *window = [self window]; 935 BOOL shouldMaskWindow = window && !NSIsEmptyRect(_data->_windowBottomCornerIntersectionRect); 936 if (shouldMaskWindow) 937 NSDisableScreenUpdates(); 938#endif 939 940 [super displayIfNeeded]; 941 942#if !defined(BUILDING_ON_SNOW_LEOPARD) 943 if (shouldMaskWindow) { 944 [window _maskRoundedBottomCorners:_data->_windowBottomCornerIntersectionRect]; 945 NSEnableScreenUpdates(); 946 _data->_windowBottomCornerIntersectionRect = NSZeroRect; 947 } 948#endif 949} 950 951// Events 952 953// Override this so that AppKit will send us arrow keys as key down events so we can 954// support them via the key bindings mechanism. 955- (BOOL)_wantsKeyDownForEvent:(NSEvent *)event 956{ 957 return YES; 958} 959 960- (void)_setMouseDownEvent:(NSEvent *)event 961{ 962 ASSERT(!event || [event type] == NSLeftMouseDown || [event type] == NSRightMouseDown || [event type] == NSOtherMouseDown); 963 964 if (event == _data->_mouseDownEvent) 965 return; 966 967 [_data->_mouseDownEvent release]; 968 _data->_mouseDownEvent = [event retain]; 969} 970 971#define NATIVE_MOUSE_EVENT_HANDLER(Selector) \ 972 - (void)Selector:(NSEvent *)theEvent \ 973 { \ 974 if ([[self inputContext] handleEvent:theEvent]) { \ 975 LOG(TextInput, "%s was handled by text input context", String(#Selector).substring(0, String(#Selector).find("Internal")).ascii().data()); \ 976 return; \ 977 } \ 978 NativeWebMouseEvent webEvent(theEvent, self); \ 979 _data->_page->handleMouseEvent(webEvent); \ 980 } 981 982NATIVE_MOUSE_EVENT_HANDLER(mouseEntered) 983NATIVE_MOUSE_EVENT_HANDLER(mouseExited) 984NATIVE_MOUSE_EVENT_HANDLER(mouseMovedInternal) 985NATIVE_MOUSE_EVENT_HANDLER(mouseDownInternal) 986NATIVE_MOUSE_EVENT_HANDLER(mouseUpInternal) 987NATIVE_MOUSE_EVENT_HANDLER(mouseDraggedInternal) 988NATIVE_MOUSE_EVENT_HANDLER(otherMouseDown) 989NATIVE_MOUSE_EVENT_HANDLER(otherMouseDragged) 990NATIVE_MOUSE_EVENT_HANDLER(otherMouseMoved) 991NATIVE_MOUSE_EVENT_HANDLER(otherMouseUp) 992NATIVE_MOUSE_EVENT_HANDLER(rightMouseDown) 993NATIVE_MOUSE_EVENT_HANDLER(rightMouseDragged) 994NATIVE_MOUSE_EVENT_HANDLER(rightMouseUp) 995 996#undef NATIVE_MOUSE_EVENT_HANDLER 997 998#define NATIVE_EVENT_HANDLER(Selector, Type) \ 999 - (void)Selector:(NSEvent *)theEvent \ 1000 { \ 1001 NativeWeb##Type##Event webEvent = NativeWeb##Type##Event(theEvent, self); \ 1002 _data->_page->handle##Type##Event(webEvent); \ 1003 } 1004 1005NATIVE_EVENT_HANDLER(scrollWheel, Wheel) 1006 1007#undef NATIVE_EVENT_HANDLER 1008 1009- (void)mouseMoved:(NSEvent *)event 1010{ 1011 // When a view is first responder, it gets mouse moved events even when the mouse is outside its visible rect. 1012 if (self == [[self window] firstResponder] && !NSPointInRect([self convertPoint:[event locationInWindow] fromView:nil], [self visibleRect])) 1013 return; 1014 1015 [self mouseMovedInternal:event]; 1016} 1017 1018- (void)mouseDown:(NSEvent *)event 1019{ 1020 [self _setMouseDownEvent:event]; 1021 _data->_ignoringMouseDraggedEvents = NO; 1022 _data->_dragHasStarted = NO; 1023 [self mouseDownInternal:event]; 1024} 1025 1026- (void)mouseUp:(NSEvent *)event 1027{ 1028 [self _setMouseDownEvent:nil]; 1029 [self mouseUpInternal:event]; 1030} 1031 1032- (void)mouseDragged:(NSEvent *)event 1033{ 1034 if (_data->_ignoringMouseDraggedEvents) 1035 return; 1036 [self mouseDraggedInternal:event]; 1037} 1038 1039- (BOOL)acceptsFirstMouse:(NSEvent *)event 1040{ 1041 // There's a chance that responding to this event will run a nested event loop, and 1042 // fetching a new event might release the old one. Retaining and then autoreleasing 1043 // the current event prevents that from causing a problem inside WebKit or AppKit code. 1044 [[event retain] autorelease]; 1045 1046 if (![self hitTest:[event locationInWindow]]) 1047 return NO; 1048 1049 [self _setMouseDownEvent:event]; 1050 bool result = _data->_page->acceptsFirstMouse([event eventNumber], WebEventFactory::createWebMouseEvent(event, self)); 1051 [self _setMouseDownEvent:nil]; 1052 return result; 1053} 1054 1055- (BOOL)shouldDelayWindowOrderingForEvent:(NSEvent *)event 1056{ 1057 // If this is the active window or we don't have a range selection, there is no need to perform additional checks 1058 // and we can avoid making a synchronous call to the WebProcess. 1059 if ([[self window] isKeyWindow] || _data->_page->editorState().selectionIsNone || !_data->_page->editorState().selectionIsRange) 1060 return NO; 1061 1062 // There's a chance that responding to this event will run a nested event loop, and 1063 // fetching a new event might release the old one. Retaining and then autoreleasing 1064 // the current event prevents that from causing a problem inside WebKit or AppKit code. 1065 [[event retain] autorelease]; 1066 1067 if (![self hitTest:[event locationInWindow]]) 1068 return NO; 1069 1070 [self _setMouseDownEvent:event]; 1071 bool result = _data->_page->shouldDelayWindowOrderingForEvent(WebEventFactory::createWebMouseEvent(event, self)); 1072 [self _setMouseDownEvent:nil]; 1073 return result; 1074} 1075 1076#if ENABLE(GESTURE_EVENTS) 1077 1078static const short kIOHIDEventTypeScroll = 6; 1079 1080- (void)shortCircuitedEndGestureWithEvent:(NSEvent *)event 1081{ 1082 if ([event subtype] != kIOHIDEventTypeScroll) 1083 return; 1084 1085 WebGestureEvent webEvent = WebEventFactory::createWebGestureEvent(event, self); 1086 _data->_page->handleGestureEvent(webEvent); 1087 1088 if (_data->_endGestureMonitor) { 1089 [NSEvent removeMonitor:_data->_endGestureMonitor]; 1090 _data->_endGestureMonitor = nil; 1091 } 1092} 1093 1094- (void)beginGestureWithEvent:(NSEvent *)event 1095{ 1096 if ([event subtype] != kIOHIDEventTypeScroll) 1097 return; 1098 1099 WebGestureEvent webEvent = WebEventFactory::createWebGestureEvent(event, self); 1100 _data->_page->handleGestureEvent(webEvent); 1101 1102 if (!_data->_endGestureMonitor) { 1103 _data->_endGestureMonitor = [NSEvent addLocalMonitorForEventsMatchingMask:NSEventMaskEndGesture handler:^(NSEvent *blockEvent) { 1104 [self shortCircuitedEndGestureWithEvent:blockEvent]; 1105 return blockEvent; 1106 }]; 1107 } 1108} 1109#endif 1110 1111- (void)doCommandBySelector:(SEL)selector 1112{ 1113 LOG(TextInput, "doCommandBySelector:\"%s\"", sel_getName(selector)); 1114 1115 WKViewInterpretKeyEventsParameters* parameters = _data->_interpretKeyEventsParameters; 1116 if (parameters) 1117 parameters->consumedByIM = false; 1118 1119 // As in insertText:replacementRange:, we assume that the call comes from an input method if there is marked text. 1120 bool isFromInputMethod = _data->_page->editorState().hasComposition; 1121 1122 if (parameters && !isFromInputMethod) 1123 parameters->commands->append(KeypressCommand(NSStringFromSelector(selector))); 1124 else { 1125 // FIXME: Send the command to Editor synchronously and only send it along the 1126 // responder chain if it's a selector that does not correspond to an editing command. 1127 [super doCommandBySelector:selector]; 1128 } 1129} 1130 1131- (void)insertText:(id)string 1132{ 1133 // Unlike and NSTextInputClient variant with replacementRange, this NSResponder method is called when there is no input context, 1134 // so text input processing isn't performed. We are not going to actually insert any text in that case, but saving an insertText 1135 // command ensures that a keypress event is dispatched as appropriate. 1136 [self insertText:string replacementRange:NSMakeRange(NSNotFound, 0)]; 1137} 1138 1139- (void)insertText:(id)string replacementRange:(NSRange)replacementRange 1140{ 1141 BOOL isAttributedString = [string isKindOfClass:[NSAttributedString class]]; 1142 ASSERT(isAttributedString || [string isKindOfClass:[NSString class]]); 1143 1144 if (replacementRange.location != NSNotFound) 1145 LOG(TextInput, "insertText:\"%@\" replacementRange:(%u, %u)", isAttributedString ? [string string] : string, replacementRange.location, replacementRange.length); 1146 else 1147 LOG(TextInput, "insertText:\"%@\"", isAttributedString ? [string string] : string); 1148 WKViewInterpretKeyEventsParameters* parameters = _data->_interpretKeyEventsParameters; 1149 if (parameters) 1150 parameters->consumedByIM = false; 1151 1152 NSString *text; 1153 bool isFromInputMethod = _data->_page->editorState().hasComposition; 1154 1155 if (isAttributedString) { 1156 // FIXME: We ignore most attributes from the string, so for example inserting from Character Palette loses font and glyph variation data. 1157 text = [string string]; 1158 } else 1159 text = string; 1160 1161 // insertText can be called for several reasons: 1162 // - If it's from normal key event processing (including key bindings), we may need to save the action to perform it later. 1163 // - 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. 1164 // 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. 1165 // - If it's sent outside of keyboard event processing (e.g. from Character Viewer, or when confirming an inline input area with a mouse), 1166 // then we also execute it immediately, as there will be no other chance. 1167 if (parameters && !isFromInputMethod) { 1168 ASSERT(replacementRange.location == NSNotFound); 1169 parameters->commands->append(KeypressCommand("insertText:", text)); 1170 return; 1171 } 1172 1173 String eventText = text; 1174 eventText.replace(NSBackTabCharacter, NSTabCharacter); // same thing is done in KeyEventMac.mm in WebCore 1175 bool eventHandled = _data->_page->insertText(eventText, replacementRange.location, NSMaxRange(replacementRange)); 1176 1177 if (parameters) 1178 parameters->eventInterpretationHadSideEffects |= eventHandled; 1179} 1180 1181- (BOOL)_handleStyleKeyEquivalent:(NSEvent *)event 1182{ 1183 if (!_data->_page->editorState().isContentEditable) 1184 return NO; 1185 1186 if (([event modifierFlags] & NSDeviceIndependentModifierFlagsMask) != NSCommandKeyMask) 1187 return NO; 1188 1189 // Here we special case cmd+b and cmd+i but not cmd+u, for historic reason. 1190 // This should not be changed, since it could break some Mac applications that 1191 // rely on this inherent behavior. 1192 // See https://bugs.webkit.org/show_bug.cgi?id=24943 1193 1194 NSString *string = [event characters]; 1195 if ([string caseInsensitiveCompare:@"b"] == NSOrderedSame) { 1196 _data->_page->executeEditCommand("ToggleBold"); 1197 return YES; 1198 } 1199 if ([string caseInsensitiveCompare:@"i"] == NSOrderedSame) { 1200 _data->_page->executeEditCommand("ToggleItalic"); 1201 return YES; 1202 } 1203 1204 return NO; 1205} 1206 1207- (BOOL)performKeyEquivalent:(NSEvent *)event 1208{ 1209 // There's a chance that responding to this event will run a nested event loop, and 1210 // fetching a new event might release the old one. Retaining and then autoreleasing 1211 // the current event prevents that from causing a problem inside WebKit or AppKit code. 1212 [[event retain] autorelease]; 1213 1214 BOOL eventWasSentToWebCore = (_data->_keyDownEventBeingResent == event); 1215 1216 // Pass key combos through WebCore if there is a key binding available for 1217 // this event. This lets web pages have a crack at intercepting key-modified keypresses. 1218 // But don't do it if we have already handled the event. 1219 // Pressing Esc results in a fake event being sent - don't pass it to WebCore. 1220 if (!eventWasSentToWebCore && event == [NSApp currentEvent] && self == [[self window] firstResponder]) { 1221 _data->_page->handleKeyboardEvent(NativeWebKeyboardEvent(event, self)); 1222 return YES; 1223 } 1224 1225 return [self _handleStyleKeyEquivalent:event] || [super performKeyEquivalent:event]; 1226} 1227 1228- (void)keyUp:(NSEvent *)theEvent 1229{ 1230 _data->_page->handleKeyboardEvent(NativeWebKeyboardEvent(theEvent, self)); 1231} 1232 1233- (void)keyDown:(NSEvent *)theEvent 1234{ 1235 // There's a chance that responding to this event will run a nested event loop, and 1236 // fetching a new event might release the old one. Retaining and then autoreleasing 1237 // the current event prevents that from causing a problem inside WebKit or AppKit code. 1238 [[theEvent retain] autorelease]; 1239 1240 if (_data->_pluginComplexTextInputIdentifier) { 1241 // Try feeding the keyboard event directly to the plug-in. 1242 NSString *string = nil; 1243 if ([[WKTextInputWindowController sharedTextInputWindowController] interpretKeyEvent:theEvent string:&string]) { 1244 if (string) 1245 _data->_page->sendComplexTextInputToPlugin(_data->_pluginComplexTextInputIdentifier, string); 1246 return; 1247 } 1248 } 1249 1250 // We could be receiving a key down from AppKit if we have re-sent an event 1251 // that maps to an action that is currently unavailable (for example a copy when 1252 // there is no range selection). 1253 // If this is the case we should ignore the key down. 1254 if (_data->_keyDownEventBeingResent == theEvent) { 1255 [super keyDown:theEvent]; 1256 return; 1257 } 1258 _data->_page->handleKeyboardEvent(NativeWebKeyboardEvent(theEvent, self)); 1259} 1260 1261- (void)flagsChanged:(NSEvent *)theEvent 1262{ 1263 // There's a chance that responding to this event will run a nested event loop, and 1264 // fetching a new event might release the old one. Retaining and then autoreleasing 1265 // the current event prevents that from causing a problem inside WebKit or AppKit code. 1266 [[theEvent retain] autorelease]; 1267 1268 unsigned short keyCode = [theEvent keyCode]; 1269 1270 // Don't make an event from the num lock and function keys 1271 if (!keyCode || keyCode == 10 || keyCode == 63) 1272 return; 1273 1274 _data->_page->handleKeyboardEvent(NativeWebKeyboardEvent(theEvent, self)); 1275} 1276 1277- (void)_executeSavedKeypressCommands 1278{ 1279 WKViewInterpretKeyEventsParameters* parameters = _data->_interpretKeyEventsParameters; 1280 if (!parameters || parameters->commands->isEmpty()) 1281 return; 1282 1283 // We could be called again if the execution of one command triggers a call to selectedRange. 1284 // In this case, the state is up to date, and we don't need to execute any more saved commands to return a result. 1285 if (parameters->executingSavedKeypressCommands) 1286 return; 1287 1288 parameters->executingSavedKeypressCommands = true; 1289 parameters->eventInterpretationHadSideEffects |= _data->_page->executeKeypressCommands(*parameters->commands); 1290 parameters->commands->clear(); 1291 parameters->executingSavedKeypressCommands = false; 1292} 1293 1294- (void)_notifyInputContextAboutDiscardedComposition 1295{ 1296 // <rdar://problem/9359055>: -discardMarkedText can only be called for active contexts. 1297 // FIXME: We fail to ever notify the input context if something (e.g. a navigation) happens while the window is not key. 1298 // This is not a problem when the window is key, because we discard marked text on resigning first responder. 1299 if (![[self window] isKeyWindow] || self != [[self window] firstResponder]) 1300 return; 1301 1302 [[super inputContext] discardMarkedText]; // Inform the input method that we won't have an inline input area despite having been asked to. 1303} 1304 1305- (NSTextInputContext *)inputContext 1306{ 1307 WKViewInterpretKeyEventsParameters* parameters = _data->_interpretKeyEventsParameters; 1308 1309 if (_data->_pluginComplexTextInputIdentifier && !parameters) 1310 return [[WKTextInputWindowController sharedTextInputWindowController] inputContext]; 1311 1312 // Disable text input machinery when in non-editable content. An invisible inline input area affects performance, and can prevent Expose from working. 1313 if (!_data->_page->editorState().isContentEditable) 1314 return nil; 1315 1316 return [super inputContext]; 1317} 1318 1319- (NSRange)selectedRange 1320{ 1321 [self _executeSavedKeypressCommands]; 1322 1323 uint64_t selectionStart; 1324 uint64_t selectionLength; 1325 _data->_page->getSelectedRange(selectionStart, selectionLength); 1326 1327 NSRange result = NSMakeRange(selectionStart, selectionLength); 1328 if (result.location == NSNotFound) 1329 LOG(TextInput, "selectedRange -> (NSNotFound, %u)", result.length); 1330 else 1331 LOG(TextInput, "selectedRange -> (%u, %u)", result.location, result.length); 1332 1333 return result; 1334} 1335 1336- (BOOL)hasMarkedText 1337{ 1338 WKViewInterpretKeyEventsParameters* parameters = _data->_interpretKeyEventsParameters; 1339 1340 BOOL result; 1341 if (parameters) { 1342 result = _data->_page->editorState().hasComposition; 1343 if (result) { 1344 // A saved command can confirm a composition, but it cannot start a new one. 1345 [self _executeSavedKeypressCommands]; 1346 result = _data->_page->editorState().hasComposition; 1347 } 1348 } else { 1349 uint64_t location; 1350 uint64_t length; 1351 _data->_page->getMarkedRange(location, length); 1352 result = location != NSNotFound; 1353 } 1354 1355 LOG(TextInput, "hasMarkedText -> %u", result); 1356 return result; 1357} 1358 1359- (void)unmarkText 1360{ 1361 [self _executeSavedKeypressCommands]; 1362 1363 LOG(TextInput, "unmarkText"); 1364 1365 // Use pointer to get parameters passed to us by the caller of interpretKeyEvents. 1366 WKViewInterpretKeyEventsParameters* parameters = _data->_interpretKeyEventsParameters; 1367 1368 if (parameters) { 1369 parameters->eventInterpretationHadSideEffects = true; 1370 parameters->consumedByIM = false; 1371 } 1372 1373 _data->_page->confirmComposition(); 1374} 1375 1376- (NSArray *)validAttributesForMarkedText 1377{ 1378 static NSArray *validAttributes; 1379 if (!validAttributes) { 1380 validAttributes = [[NSArray alloc] initWithObjects: 1381 NSUnderlineStyleAttributeName, NSUnderlineColorAttributeName, 1382 NSMarkedClauseSegmentAttributeName, nil]; 1383 // NSText also supports the following attributes, but it's 1384 // hard to tell which are really required for text input to 1385 // work well; I have not seen any input method make use of them yet. 1386 // NSFontAttributeName, NSForegroundColorAttributeName, 1387 // NSBackgroundColorAttributeName, NSLanguageAttributeName. 1388 CFRetain(validAttributes); 1389 } 1390 LOG(TextInput, "validAttributesForMarkedText -> (...)"); 1391 return validAttributes; 1392} 1393 1394static void extractUnderlines(NSAttributedString *string, Vector<CompositionUnderline>& result) 1395{ 1396 int length = [[string string] length]; 1397 1398 int i = 0; 1399 while (i < length) { 1400 NSRange range; 1401 NSDictionary *attrs = [string attributesAtIndex:i longestEffectiveRange:&range inRange:NSMakeRange(i, length - i)]; 1402 1403 if (NSNumber *style = [attrs objectForKey:NSUnderlineStyleAttributeName]) { 1404 Color color = Color::black; 1405 if (NSColor *colorAttr = [attrs objectForKey:NSUnderlineColorAttributeName]) 1406 color = colorFromNSColor([colorAttr colorUsingColorSpaceName:NSDeviceRGBColorSpace]); 1407 result.append(CompositionUnderline(range.location, NSMaxRange(range), color, [style intValue] > 1)); 1408 } 1409 1410 i = range.location + range.length; 1411 } 1412} 1413 1414- (void)setMarkedText:(id)string selectedRange:(NSRange)newSelRange replacementRange:(NSRange)replacementRange 1415{ 1416 [self _executeSavedKeypressCommands]; 1417 1418 BOOL isAttributedString = [string isKindOfClass:[NSAttributedString class]]; 1419 ASSERT(isAttributedString || [string isKindOfClass:[NSString class]]); 1420 1421 LOG(TextInput, "setMarkedText:\"%@\" selectedRange:(%u, %u)", isAttributedString ? [string string] : string, newSelRange.location, newSelRange.length); 1422 1423 // Use pointer to get parameters passed to us by the caller of interpretKeyEvents. 1424 WKViewInterpretKeyEventsParameters* parameters = _data->_interpretKeyEventsParameters; 1425 1426 if (parameters) { 1427 parameters->eventInterpretationHadSideEffects = true; 1428 parameters->consumedByIM = false; 1429 } 1430 1431 Vector<CompositionUnderline> underlines; 1432 NSString *text; 1433 1434 if (isAttributedString) { 1435 // FIXME: We ignore most attributes from the string, so an input method cannot specify e.g. a font or a glyph variation. 1436 text = [string string]; 1437 extractUnderlines(string, underlines); 1438 } else 1439 text = string; 1440 1441 if (_data->_page->editorState().isInPasswordField) { 1442 // In password fields, we only allow ASCII dead keys, and don't allow inline input, matching NSSecureTextInputField. 1443 // Allowing ASCII dead keys is necessary to enable full Roman input when using a Vietnamese keyboard. 1444 ASSERT(!_data->_page->editorState().hasComposition); 1445 [self _notifyInputContextAboutDiscardedComposition]; 1446 if ([text length] == 1 && [[text decomposedStringWithCanonicalMapping] characterAtIndex:0] < 0x80) { 1447 _data->_page->insertText(text, replacementRange.location, NSMaxRange(replacementRange)); 1448 } else 1449 NSBeep(); 1450 return; 1451 } 1452 1453 _data->_page->setComposition(text, underlines, newSelRange.location, NSMaxRange(newSelRange), replacementRange.location, NSMaxRange(replacementRange)); 1454} 1455 1456- (NSRange)markedRange 1457{ 1458 [self _executeSavedKeypressCommands]; 1459 1460 uint64_t location; 1461 uint64_t length; 1462 _data->_page->getMarkedRange(location, length); 1463 1464 LOG(TextInput, "markedRange -> (%u, %u)", location, length); 1465 return NSMakeRange(location, length); 1466} 1467 1468- (NSAttributedString *)attributedSubstringForProposedRange:(NSRange)nsRange actualRange:(NSRangePointer)actualRange 1469{ 1470 [self _executeSavedKeypressCommands]; 1471 1472 if (!_data->_page->editorState().isContentEditable) { 1473 LOG(TextInput, "attributedSubstringFromRange:(%u, %u) -> nil", nsRange.location, nsRange.length); 1474 return nil; 1475 } 1476 1477 if (_data->_page->editorState().isInPasswordField) 1478 return nil; 1479 1480 AttributedString result; 1481 _data->_page->getAttributedSubstringFromRange(nsRange.location, NSMaxRange(nsRange), result); 1482 1483 if (actualRange) 1484 *actualRange = nsRange; 1485 1486 LOG(TextInput, "attributedSubstringFromRange:(%u, %u) -> \"%@\"", nsRange.location, nsRange.length, [result.string.get() string]); 1487 return [[result.string.get() retain] autorelease]; 1488} 1489 1490- (NSUInteger)characterIndexForPoint:(NSPoint)thePoint 1491{ 1492 [self _executeSavedKeypressCommands]; 1493 1494 NSWindow *window = [self window]; 1495 1496 if (window) 1497 thePoint = [window convertScreenToBase:thePoint]; 1498 thePoint = [self convertPoint:thePoint fromView:nil]; // the point is relative to the main frame 1499 1500 uint64_t result = _data->_page->characterIndexForPoint(IntPoint(thePoint)); 1501 LOG(TextInput, "characterIndexForPoint:(%f, %f) -> %u", thePoint.x, thePoint.y, result); 1502 return result; 1503} 1504 1505- (NSRect)firstRectForCharacterRange:(NSRange)theRange actualRange:(NSRangePointer)actualRange 1506{ 1507 [self _executeSavedKeypressCommands]; 1508 1509 // Just to match NSTextView's behavior. Regression tests cannot detect this; 1510 // to reproduce, use a test application from http://bugs.webkit.org/show_bug.cgi?id=4682 1511 // (type something; try ranges (1, -1) and (2, -1). 1512 if ((theRange.location + theRange.length < theRange.location) && (theRange.location + theRange.length != 0)) 1513 theRange.length = 0; 1514 1515 NSRect resultRect = _data->_page->firstRectForCharacterRange(theRange.location, theRange.length); 1516 resultRect = [self convertRect:resultRect toView:nil]; 1517 1518 NSWindow *window = [self window]; 1519 if (window) 1520 resultRect.origin = [window convertBaseToScreen:resultRect.origin]; 1521 1522 if (actualRange) 1523 *actualRange = theRange; 1524 1525 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); 1526 return resultRect; 1527} 1528 1529- (void)draggedImage:(NSImage *)anImage endedAt:(NSPoint)aPoint operation:(NSDragOperation)operation 1530{ 1531 NSPoint windowImageLoc = [[self window] convertScreenToBase:aPoint]; 1532 NSPoint windowMouseLoc = windowImageLoc; 1533 1534 // Prevent queued mouseDragged events from coming after the drag and fake mouseUp event. 1535 _data->_ignoringMouseDraggedEvents = YES; 1536 1537 _data->_page->dragEnded(IntPoint(windowMouseLoc), globalPoint(windowMouseLoc, [self window]), operation); 1538} 1539 1540- (DragApplicationFlags)applicationFlags:(id <NSDraggingInfo>)draggingInfo 1541{ 1542 uint32_t flags = 0; 1543 if ([NSApp modalWindow]) 1544 flags = DragApplicationIsModal; 1545 if ([[self window] attachedSheet]) 1546 flags |= DragApplicationHasAttachedSheet; 1547 if ([draggingInfo draggingSource] == self) 1548 flags |= DragApplicationIsSource; 1549 if ([[NSApp currentEvent] modifierFlags] & NSAlternateKeyMask) 1550 flags |= DragApplicationIsCopyKeyDown; 1551 return static_cast<DragApplicationFlags>(flags); 1552} 1553 1554- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)draggingInfo 1555{ 1556 IntPoint client([self convertPoint:[draggingInfo draggingLocation] fromView:nil]); 1557 IntPoint global(globalPoint([draggingInfo draggingLocation], [self window])); 1558 DragData dragData(draggingInfo, client, global, static_cast<DragOperation>([draggingInfo draggingSourceOperationMask]), [self applicationFlags:draggingInfo]); 1559 1560 _data->_page->resetDragOperation(); 1561 _data->_page->dragEntered(&dragData, [[draggingInfo draggingPasteboard] name]); 1562 return NSDragOperationCopy; 1563} 1564 1565- (NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)draggingInfo 1566{ 1567 IntPoint client([self convertPoint:[draggingInfo draggingLocation] fromView:nil]); 1568 IntPoint global(globalPoint([draggingInfo draggingLocation], [self window])); 1569 DragData dragData(draggingInfo, client, global, static_cast<DragOperation>([draggingInfo draggingSourceOperationMask]), [self applicationFlags:draggingInfo]); 1570 _data->_page->dragUpdated(&dragData, [[draggingInfo draggingPasteboard] name]); 1571 return _data->_page->dragOperation(); 1572} 1573 1574- (void)draggingExited:(id <NSDraggingInfo>)draggingInfo 1575{ 1576 IntPoint client([self convertPoint:[draggingInfo draggingLocation] fromView:nil]); 1577 IntPoint global(globalPoint([draggingInfo draggingLocation], [self window])); 1578 DragData dragData(draggingInfo, client, global, static_cast<DragOperation>([draggingInfo draggingSourceOperationMask]), [self applicationFlags:draggingInfo]); 1579 _data->_page->dragExited(&dragData, [[draggingInfo draggingPasteboard] name]); 1580 _data->_page->resetDragOperation(); 1581} 1582 1583- (BOOL)prepareForDragOperation:(id <NSDraggingInfo>)draggingInfo 1584{ 1585 return YES; 1586} 1587 1588// FIXME: This code is more or less copied from Pasteboard::getBestURL. 1589// It would be nice to be able to share the code somehow. 1590static void maybeCreateSandboxExtensionFromPasteboard(NSPasteboard *pasteboard, SandboxExtension::Handle& sandboxExtensionHandle) 1591{ 1592 NSArray *types = [pasteboard types]; 1593 if (![types containsObject:NSFilenamesPboardType]) 1594 return; 1595 1596 NSArray *files = [pasteboard propertyListForType:NSFilenamesPboardType]; 1597 if ([files count] != 1) 1598 return; 1599 1600 NSString *file = [files objectAtIndex:0]; 1601 BOOL isDirectory; 1602 if (![[NSFileManager defaultManager] fileExistsAtPath:file isDirectory:&isDirectory]) 1603 return; 1604 1605 if (isDirectory) 1606 return; 1607 1608 SandboxExtension::createHandle("/", SandboxExtension::ReadOnly, sandboxExtensionHandle); 1609} 1610 1611- (BOOL)performDragOperation:(id <NSDraggingInfo>)draggingInfo 1612{ 1613 IntPoint client([self convertPoint:[draggingInfo draggingLocation] fromView:nil]); 1614 IntPoint global(globalPoint([draggingInfo draggingLocation], [self window])); 1615 DragData dragData(draggingInfo, client, global, static_cast<DragOperation>([draggingInfo draggingSourceOperationMask]), [self applicationFlags:draggingInfo]); 1616 1617 SandboxExtension::Handle sandboxExtensionHandle; 1618 maybeCreateSandboxExtensionFromPasteboard([draggingInfo draggingPasteboard], sandboxExtensionHandle); 1619 1620 _data->_page->performDrag(&dragData, [[draggingInfo draggingPasteboard] name], sandboxExtensionHandle); 1621 1622 return YES; 1623} 1624 1625// This code is needed to support drag and drop when the drag types cannot be matched. 1626// This is the case for elements that do not place content 1627// in the drag pasteboard automatically when the drag start (i.e. dragging a DIV element). 1628- (NSView *)_hitTest:(NSPoint *)point dragTypes:(NSSet *)types 1629{ 1630 if ([[self superview] mouse:*point inRect:[self frame]]) 1631 return self; 1632 return nil; 1633} 1634 1635- (BOOL)_windowResizeMouseLocationIsInVisibleScrollerThumb:(NSPoint)loc 1636{ 1637 NSPoint localPoint = [self convertPoint:loc fromView:nil]; 1638 NSRect visibleThumbRect = NSRect(_data->_page->visibleScrollerThumbRect()); 1639 return NSMouseInRect(localPoint, visibleThumbRect, [self isFlipped]); 1640} 1641 1642- (void)_updateWindowVisibility 1643{ 1644 _data->_page->updateWindowIsVisible(![[self window] isMiniaturized]); 1645} 1646 1647- (BOOL)_ownsWindowGrowBox 1648{ 1649 NSWindow* window = [self window]; 1650 if (!window) 1651 return NO; 1652 1653 NSView *superview = [self superview]; 1654 if (!superview) 1655 return NO; 1656 1657 NSRect growBoxRect = [window _growBoxRect]; 1658 if (NSIsEmptyRect(growBoxRect)) 1659 return NO; 1660 1661 NSRect visibleRect = [self visibleRect]; 1662 if (NSIsEmptyRect(visibleRect)) 1663 return NO; 1664 1665 NSRect visibleRectInWindowCoords = [self convertRect:visibleRect toView:nil]; 1666 if (!NSIntersectsRect(growBoxRect, visibleRectInWindowCoords)) 1667 return NO; 1668 1669 return YES; 1670} 1671 1672- (BOOL)_updateGrowBoxForWindowFrameChange 1673{ 1674 // Temporarily enable the resize indicator to make a the _ownsWindowGrowBox calculation work. 1675 BOOL wasShowingIndicator = [[self window] showsResizeIndicator]; 1676 if (!wasShowingIndicator) 1677 [[self window] setShowsResizeIndicator:YES]; 1678 1679 BOOL ownsGrowBox = [self _ownsWindowGrowBox]; 1680 _data->_page->setWindowResizerSize(ownsGrowBox ? enclosingIntRect([[self window] _growBoxRect]).size() : IntSize()); 1681 1682 if (ownsGrowBox) 1683 [[self window] _setShowOpaqueGrowBoxForOwner:(_data->_page->hasHorizontalScrollbar() || _data->_page->hasVerticalScrollbar() ? self : nil)]; 1684 else 1685 [[self window] _setShowOpaqueGrowBoxForOwner:nil]; 1686 1687 // Once WebCore can draw the window resizer, this should read: 1688 // if (wasShowingIndicator) 1689 // [[self window] setShowsResizeIndicator:!ownsGrowBox]; 1690 if (!wasShowingIndicator) 1691 [[self window] setShowsResizeIndicator:NO]; 1692 1693 return ownsGrowBox; 1694} 1695 1696- (void)addWindowObserversForWindow:(NSWindow *)window 1697{ 1698 if (window) { 1699 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_windowDidBecomeKey:) 1700 name:NSWindowDidBecomeKeyNotification object:nil]; 1701 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_windowDidResignKey:) 1702 name:NSWindowDidResignKeyNotification object:nil]; 1703 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_windowDidMiniaturize:) 1704 name:NSWindowDidMiniaturizeNotification object:window]; 1705 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_windowDidDeminiaturize:) 1706 name:NSWindowDidDeminiaturizeNotification object:window]; 1707 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_windowFrameDidChange:) 1708 name:NSWindowDidMoveNotification object:window]; 1709 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_windowFrameDidChange:) 1710 name:NSWindowDidResizeNotification object:window]; 1711 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_windowDidOrderOffScreen:) 1712 name:@"NSWindowDidOrderOffScreenNotification" object:window]; 1713 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_windowDidOrderOnScreen:) 1714 name:@"_NSWindowDidBecomeVisible" object:window]; 1715 } 1716} 1717 1718- (void)removeWindowObservers 1719{ 1720 NSWindow *window = [self window]; 1721 if (!window) 1722 return; 1723 1724 [[NSNotificationCenter defaultCenter] removeObserver:self name:NSWindowDidBecomeKeyNotification object:nil]; 1725 [[NSNotificationCenter defaultCenter] removeObserver:self name:NSWindowDidResignKeyNotification object:nil]; 1726 [[NSNotificationCenter defaultCenter] removeObserver:self name:NSWindowDidMiniaturizeNotification object:window]; 1727 [[NSNotificationCenter defaultCenter] removeObserver:self name:NSWindowDidDeminiaturizeNotification object:window]; 1728 [[NSNotificationCenter defaultCenter] removeObserver:self name:NSWindowDidMoveNotification object:window]; 1729 [[NSNotificationCenter defaultCenter] removeObserver:self name:NSWindowDidResizeNotification object:window]; 1730 [[NSNotificationCenter defaultCenter] removeObserver:self name:@"NSWindowDidOrderOffScreenNotification" object:window]; 1731 [[NSNotificationCenter defaultCenter] removeObserver:self name:@"_NSWindowDidBecomeVisible" object:window]; 1732} 1733 1734- (void)viewWillMoveToWindow:(NSWindow *)window 1735{ 1736 NSWindow *currentWindow = [self window]; 1737 if (window == currentWindow) 1738 return; 1739 1740 [self removeWindowObservers]; 1741 [self addWindowObserversForWindow:window]; 1742 1743 if ([currentWindow _growBoxOwner] == self) 1744 [currentWindow _setShowOpaqueGrowBoxForOwner:nil]; 1745} 1746 1747- (void)viewDidMoveToWindow 1748{ 1749 // We want to make sure to update the active state while hidden, so if the view is about to become visible, we 1750 // update the active state first and then make it visible. If the view is about to be hidden, we hide it first and then 1751 // update the active state. 1752 if ([self window]) { 1753 _data->_page->viewStateDidChange(WebPageProxy::ViewWindowIsActive); 1754 _data->_page->viewStateDidChange(WebPageProxy::ViewIsVisible | WebPageProxy::ViewIsInWindow); 1755 [self _updateWindowVisibility]; 1756 [self _updateWindowAndViewFrames]; 1757 1758 // Initialize remote accessibility when the window connection has been established. 1759 NSData *remoteElementToken = WKAXRemoteTokenForElement(self); 1760 NSData *remoteWindowToken = WKAXRemoteTokenForElement([self accessibilityAttributeValue:NSAccessibilityWindowAttribute]); 1761 CoreIPC::DataReference elementToken = CoreIPC::DataReference(reinterpret_cast<const uint8_t*>([remoteElementToken bytes]), [remoteElementToken length]); 1762 CoreIPC::DataReference windowToken = CoreIPC::DataReference(reinterpret_cast<const uint8_t*>([remoteWindowToken bytes]), [remoteWindowToken length]); 1763 _data->_page->registerUIProcessAccessibilityTokens(elementToken, windowToken); 1764 1765 } else { 1766 _data->_page->viewStateDidChange(WebPageProxy::ViewIsVisible); 1767 _data->_page->viewStateDidChange(WebPageProxy::ViewWindowIsActive | WebPageProxy::ViewIsInWindow); 1768 1769#if ENABLE(GESTURE_EVENTS) 1770 if (_data->_endGestureMonitor) { 1771 [NSEvent removeMonitor:_data->_endGestureMonitor]; 1772 _data->_endGestureMonitor = nil; 1773 } 1774#endif 1775#if !defined(BUILDING_ON_SNOW_LEOPARD) 1776 WKHideWordDefinitionWindow(); 1777#endif 1778 } 1779} 1780 1781- (void)_windowDidBecomeKey:(NSNotification *)notification 1782{ 1783 NSWindow *keyWindow = [notification object]; 1784 if (keyWindow == [self window] || keyWindow == [[self window] attachedSheet]) { 1785 [self _updateSecureInputState]; 1786 _data->_page->viewStateDidChange(WebPageProxy::ViewWindowIsActive); 1787 } 1788} 1789 1790- (void)_windowDidResignKey:(NSNotification *)notification 1791{ 1792 NSWindow *formerKeyWindow = [notification object]; 1793 if (formerKeyWindow == [self window] || formerKeyWindow == [[self window] attachedSheet]) { 1794 [self _updateSecureInputState]; 1795 _data->_page->viewStateDidChange(WebPageProxy::ViewWindowIsActive); 1796 } 1797} 1798 1799- (void)_windowDidMiniaturize:(NSNotification *)notification 1800{ 1801 [self _updateWindowVisibility]; 1802} 1803 1804- (void)_windowDidDeminiaturize:(NSNotification *)notification 1805{ 1806 [self _updateWindowVisibility]; 1807} 1808 1809- (void)_windowFrameDidChange:(NSNotification *)notification 1810{ 1811 [self _updateWindowAndViewFrames]; 1812} 1813 1814- (void)_windowDidOrderOffScreen:(NSNotification *)notification 1815{ 1816 _data->_page->viewStateDidChange(WebPageProxy::ViewIsVisible); 1817} 1818 1819- (void)_windowDidOrderOnScreen:(NSNotification *)notification 1820{ 1821 _data->_page->viewStateDidChange(WebPageProxy::ViewIsVisible); 1822} 1823 1824static void drawPageBackground(CGContextRef context, WebPageProxy* page, const IntRect& rect) 1825{ 1826 if (!page->drawsBackground()) 1827 return; 1828 1829 CGContextSaveGState(context); 1830 CGContextSetBlendMode(context, kCGBlendModeCopy); 1831 1832 CGColorRef backgroundColor; 1833 if (page->drawsTransparentBackground()) 1834 backgroundColor = CGColorGetConstantColor(kCGColorClear); 1835 else 1836 backgroundColor = CGColorGetConstantColor(kCGColorWhite); 1837 1838 CGContextSetFillColorWithColor(context, backgroundColor); 1839 CGContextFillRect(context, rect); 1840 1841 CGContextRestoreGState(context); 1842} 1843 1844- (void)drawRect:(NSRect)rect 1845{ 1846 LOG(View, "drawRect: x:%g, y:%g, width:%g, height:%g", rect.origin.x, rect.origin.y, rect.size.width, rect.size.height); 1847 _data->_page->endPrinting(); 1848 CGContextRef context = static_cast<CGContextRef>([[NSGraphicsContext currentContext] graphicsPort]); 1849 1850 if (DrawingAreaProxyImpl* drawingArea = static_cast<DrawingAreaProxyImpl*>(_data->_page->drawingArea())) { 1851 const NSRect *rectsBeingDrawn; 1852 NSInteger numRectsBeingDrawn; 1853 [self getRectsBeingDrawn:&rectsBeingDrawn count:&numRectsBeingDrawn]; 1854 for (NSInteger i = 0; i < numRectsBeingDrawn; ++i) { 1855 Region unpaintedRegion; 1856 IntRect rect = enclosingIntRect(rectsBeingDrawn[i]); 1857 drawingArea->paint(context, rect, unpaintedRegion); 1858 1859 Vector<IntRect> unpaintedRects = unpaintedRegion.rects(); 1860 for (size_t i = 0; i < unpaintedRects.size(); ++i) 1861 drawPageBackground(context, _data->_page.get(), unpaintedRects[i]); 1862 } 1863 } else 1864 drawPageBackground(context, _data->_page.get(), enclosingIntRect(rect)); 1865 1866 _data->_page->didDraw(); 1867} 1868 1869- (BOOL)isOpaque 1870{ 1871 return _data->_page->drawsBackground(); 1872} 1873 1874- (BOOL)mouseDownCanMoveWindow 1875{ 1876 // -[NSView mouseDownCanMoveWindow] returns YES when the NSView is transparent, 1877 // but we don't want a drag in the NSView to move the window, even if it's transparent. 1878 return NO; 1879} 1880 1881- (void)viewDidHide 1882{ 1883 _data->_page->viewStateDidChange(WebPageProxy::ViewIsVisible); 1884} 1885 1886- (void)viewDidUnhide 1887{ 1888 _data->_page->viewStateDidChange(WebPageProxy::ViewIsVisible); 1889} 1890 1891- (id)accessibilityFocusedUIElement 1892{ 1893 if (_data->_pdfViewController) 1894 return NSAccessibilityUnignoredDescendant(_data->_pdfViewController->pdfView()); 1895 1896 return _data->_remoteAccessibilityChild.get(); 1897} 1898 1899- (BOOL)accessibilityIsIgnored 1900{ 1901 return NO; 1902} 1903 1904- (id)accessibilityHitTest:(NSPoint)point 1905{ 1906 if (_data->_pdfViewController) 1907 return [_data->_pdfViewController->pdfView() accessibilityHitTest:point]; 1908 1909 return _data->_remoteAccessibilityChild.get(); 1910} 1911 1912- (id)accessibilityAttributeValue:(NSString*)attribute 1913{ 1914 if ([attribute isEqualToString:NSAccessibilityChildrenAttribute]) { 1915 1916 id child = nil; 1917 if (_data->_pdfViewController) 1918 child = NSAccessibilityUnignoredDescendant(_data->_pdfViewController->pdfView()); 1919 else if (_data->_remoteAccessibilityChild) 1920 child = _data->_remoteAccessibilityChild.get(); 1921 1922 if (!child) 1923 return nil; 1924 return [NSArray arrayWithObject:child]; 1925 } 1926 if ([attribute isEqualToString:NSAccessibilityRoleAttribute]) 1927 return NSAccessibilityGroupRole; 1928 if ([attribute isEqualToString:NSAccessibilityRoleDescriptionAttribute]) 1929 return NSAccessibilityRoleDescription(NSAccessibilityGroupRole, nil); 1930 if ([attribute isEqualToString:NSAccessibilityParentAttribute]) 1931 return NSAccessibilityUnignoredAncestor([self superview]); 1932 if ([attribute isEqualToString:NSAccessibilityEnabledAttribute]) 1933 return [NSNumber numberWithBool:YES]; 1934 1935 return [super accessibilityAttributeValue:attribute]; 1936} 1937 1938- (NSView *)hitTest:(NSPoint)point 1939{ 1940 NSView *hitView = [super hitTest:point]; 1941 if (hitView && _data && hitView == _data->_layerHostingView) 1942 hitView = self; 1943 1944 return hitView; 1945} 1946 1947- (NSInteger)conversationIdentifier 1948{ 1949 return (NSInteger)self; 1950} 1951 1952 1953- (BOOL)canChangeFrameLayout:(WKFrameRef)frameRef 1954{ 1955 // PDF documents are already paginated, so we can't change them to add headers and footers. 1956 return !toImpl(frameRef)->isMainFrame() || !_data->_pdfViewController; 1957} 1958 1959- (NSPrintOperation *)printOperationWithPrintInfo:(NSPrintInfo *)printInfo forFrame:(WKFrameRef)frameRef 1960{ 1961 LOG(View, "Creating an NSPrintOperation for frame '%s'", toImpl(frameRef)->url().utf8().data()); 1962 1963 // Only the top frame can currently contain a PDF view. 1964 if (_data->_pdfViewController) { 1965 if (!toImpl(frameRef)->isMainFrame()) 1966 return 0; 1967 return _data->_pdfViewController->makePrintOperation(printInfo); 1968 } else { 1969 RetainPtr<WKPrintingView> printingView(AdoptNS, [[WKPrintingView alloc] initWithFrameProxy:toImpl(frameRef) view:self]); 1970 // NSPrintOperation takes ownership of the view. 1971 NSPrintOperation *printOperation = [NSPrintOperation printOperationWithView:printingView.get()]; 1972 [printOperation setCanSpawnSeparateThread:YES]; 1973 [printOperation setJobTitle:toImpl(frameRef)->title()]; 1974 printingView->_printOperation = printOperation; 1975 return printOperation; 1976 } 1977} 1978 1979@end 1980 1981@implementation WKView (Internal) 1982 1983- (PassOwnPtr<WebKit::DrawingAreaProxy>)_createDrawingAreaProxy 1984{ 1985 return DrawingAreaProxyImpl::create(_data->_page.get()); 1986} 1987 1988- (BOOL)_isFocused 1989{ 1990 if (_data->_inBecomeFirstResponder) 1991 return YES; 1992 if (_data->_inResignFirstResponder) 1993 return NO; 1994 return [[self window] firstResponder] == self; 1995} 1996 1997- (void)_processDidCrash 1998{ 1999 [self setNeedsDisplay:YES]; 2000 [self _updateRemoteAccessibilityRegistration:NO]; 2001} 2002 2003- (void)_pageClosed 2004{ 2005 [self _updateRemoteAccessibilityRegistration:NO]; 2006} 2007 2008- (void)_didRelaunchProcess 2009{ 2010 [self setNeedsDisplay:YES]; 2011} 2012 2013- (void)_setCursor:(NSCursor *)cursor 2014{ 2015 if ([NSCursor currentCursor] == cursor) 2016 return; 2017 [cursor set]; 2018} 2019 2020- (void)_setUserInterfaceItemState:(NSString *)commandName enabled:(BOOL)isEnabled state:(int)newState 2021{ 2022 ValidationVector items = _data->_validationMap.take(commandName); 2023 size_t size = items.size(); 2024 for (size_t i = 0; i < size; ++i) { 2025 ValidationItem item = items[i].get(); 2026 [menuItem(item) setState:newState]; 2027 [menuItem(item) setEnabled:isEnabled]; 2028 [toolbarItem(item) setEnabled:isEnabled]; 2029 // FIXME <rdar://problem/8803392>: If the item is neither a menu nor toolbar item, it will be left enabled. 2030 } 2031} 2032 2033- (void)_resendKeyDownEvent:(NSEvent *)event 2034{ 2035 // resending the event may destroy this WKView 2036 RetainPtr<WKView> protect(self); 2037 2038 ASSERT(!_data->_keyDownEventBeingResent); 2039 _data->_keyDownEventBeingResent = event; 2040 [NSApp _setCurrentEvent:event]; 2041 [NSApp sendEvent:event]; 2042 2043 _data->_keyDownEventBeingResent = nullptr; 2044} 2045 2046- (BOOL)_interpretKeyEvent:(NSEvent *)event savingCommandsTo:(Vector<WebCore::KeypressCommand>&)commands 2047{ 2048 ASSERT(!_data->_interpretKeyEventsParameters); 2049 ASSERT(commands.isEmpty()); 2050 2051 if ([event type] == NSFlagsChanged) 2052 return NO; 2053 2054 WKViewInterpretKeyEventsParameters parameters; 2055 parameters.eventInterpretationHadSideEffects = false; 2056 parameters.executingSavedKeypressCommands = false; 2057 // We assume that an input method has consumed the event, and only change this assumption if one of the NSTextInput methods is called. 2058 // We assume the IM will *not* consume hotkey sequences. 2059 parameters.consumedByIM = !([event modifierFlags] & NSCommandKeyMask); 2060 parameters.commands = &commands; 2061 _data->_interpretKeyEventsParameters = ¶meters; 2062 2063 [self interpretKeyEvents:[NSArray arrayWithObject:event]]; 2064 2065 _data->_interpretKeyEventsParameters = 0; 2066 2067 // An input method may consume an event and not tell us (e.g. when displaying a candidate window), 2068 // in which case we should not bubble the event up the DOM. 2069 if (parameters.consumedByIM) 2070 return YES; 2071 2072 // 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. 2073 return parameters.eventInterpretationHadSideEffects; 2074} 2075 2076- (NSRect)_convertToDeviceSpace:(NSRect)rect 2077{ 2078 return toDeviceSpace(rect, [self window]); 2079} 2080 2081- (NSRect)_convertToUserSpace:(NSRect)rect 2082{ 2083 return toUserSpace(rect, [self window]); 2084} 2085 2086// Any non-zero value will do, but using something recognizable might help us debug some day. 2087#define TRACKING_RECT_TAG 0xBADFACE 2088 2089- (NSTrackingRectTag)addTrackingRect:(NSRect)rect owner:(id)owner userData:(void *)data assumeInside:(BOOL)assumeInside 2090{ 2091 ASSERT(_data->_trackingRectOwner == nil); 2092 _data->_trackingRectOwner = owner; 2093 _data->_trackingRectUserData = data; 2094 return TRACKING_RECT_TAG; 2095} 2096 2097- (NSTrackingRectTag)_addTrackingRect:(NSRect)rect owner:(id)owner userData:(void *)data assumeInside:(BOOL)assumeInside useTrackingNum:(int)tag 2098{ 2099 ASSERT(tag == 0 || tag == TRACKING_RECT_TAG); 2100 ASSERT(_data->_trackingRectOwner == nil); 2101 _data->_trackingRectOwner = owner; 2102 _data->_trackingRectUserData = data; 2103 return TRACKING_RECT_TAG; 2104} 2105 2106- (void)_addTrackingRects:(NSRect *)rects owner:(id)owner userDataList:(void **)userDataList assumeInsideList:(BOOL *)assumeInsideList trackingNums:(NSTrackingRectTag *)trackingNums count:(int)count 2107{ 2108 ASSERT(count == 1); 2109 ASSERT(trackingNums[0] == 0 || trackingNums[0] == TRACKING_RECT_TAG); 2110 ASSERT(_data->_trackingRectOwner == nil); 2111 _data->_trackingRectOwner = owner; 2112 _data->_trackingRectUserData = userDataList[0]; 2113 trackingNums[0] = TRACKING_RECT_TAG; 2114} 2115 2116- (void)removeTrackingRect:(NSTrackingRectTag)tag 2117{ 2118 if (!_data) 2119 return; 2120 2121 if (tag == 0) 2122 return; 2123 2124 if (tag == TRACKING_RECT_TAG) { 2125 _data->_trackingRectOwner = nil; 2126 return; 2127 } 2128 2129 if (tag == _data->_lastToolTipTag) { 2130 [super removeTrackingRect:tag]; 2131 _data->_lastToolTipTag = 0; 2132 return; 2133 } 2134 2135 // If any other tracking rect is being removed, we don't know how it was created 2136 // and it's possible there's a leak involved (see 3500217) 2137 ASSERT_NOT_REACHED(); 2138} 2139 2140- (void)_removeTrackingRects:(NSTrackingRectTag *)tags count:(int)count 2141{ 2142 int i; 2143 for (i = 0; i < count; ++i) { 2144 int tag = tags[i]; 2145 if (tag == 0) 2146 continue; 2147 ASSERT(tag == TRACKING_RECT_TAG); 2148 if (_data != nil) { 2149 _data->_trackingRectOwner = nil; 2150 } 2151 } 2152} 2153 2154- (void)_sendToolTipMouseExited 2155{ 2156 // Nothing matters except window, trackingNumber, and userData. 2157 NSEvent *fakeEvent = [NSEvent enterExitEventWithType:NSMouseExited 2158 location:NSMakePoint(0, 0) 2159 modifierFlags:0 2160 timestamp:0 2161 windowNumber:[[self window] windowNumber] 2162 context:NULL 2163 eventNumber:0 2164 trackingNumber:TRACKING_RECT_TAG 2165 userData:_data->_trackingRectUserData]; 2166 [_data->_trackingRectOwner mouseExited:fakeEvent]; 2167} 2168 2169- (void)_sendToolTipMouseEntered 2170{ 2171 // Nothing matters except window, trackingNumber, and userData. 2172 NSEvent *fakeEvent = [NSEvent enterExitEventWithType:NSMouseEntered 2173 location:NSMakePoint(0, 0) 2174 modifierFlags:0 2175 timestamp:0 2176 windowNumber:[[self window] windowNumber] 2177 context:NULL 2178 eventNumber:0 2179 trackingNumber:TRACKING_RECT_TAG 2180 userData:_data->_trackingRectUserData]; 2181 [_data->_trackingRectOwner mouseEntered:fakeEvent]; 2182} 2183 2184- (NSString *)view:(NSView *)view stringForToolTip:(NSToolTipTag)tag point:(NSPoint)point userData:(void *)data 2185{ 2186 return nsStringFromWebCoreString(_data->_page->toolTip()); 2187} 2188 2189- (void)_toolTipChangedFrom:(NSString *)oldToolTip to:(NSString *)newToolTip 2190{ 2191 if (oldToolTip) 2192 [self _sendToolTipMouseExited]; 2193 2194 if (newToolTip && [newToolTip length] > 0) { 2195 // See radar 3500217 for why we remove all tooltips rather than just the single one we created. 2196 [self removeAllToolTips]; 2197 NSRect wideOpenRect = NSMakeRect(-100000, -100000, 200000, 200000); 2198 _data->_lastToolTipTag = [self addToolTipRect:wideOpenRect owner:self userData:NULL]; 2199 [self _sendToolTipMouseEntered]; 2200 } 2201} 2202 2203- (void)_setFindIndicator:(PassRefPtr<FindIndicator>)findIndicator fadeOut:(BOOL)fadeOut 2204{ 2205 if (!findIndicator) { 2206 _data->_findIndicatorWindow = nullptr; 2207 return; 2208 } 2209 2210 if (!_data->_findIndicatorWindow) 2211 _data->_findIndicatorWindow = FindIndicatorWindow::create(self); 2212 2213 _data->_findIndicatorWindow->setFindIndicator(findIndicator, fadeOut); 2214} 2215 2216- (void)_enterAcceleratedCompositingMode:(const LayerTreeContext&)layerTreeContext 2217{ 2218 ASSERT(!_data->_layerHostingView); 2219 ASSERT(!layerTreeContext.isEmpty()); 2220 2221 // Create an NSView that will host our layer tree. 2222 _data->_layerHostingView.adoptNS([[NSView alloc] initWithFrame:[self bounds]]); 2223 [_data->_layerHostingView.get() setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; 2224 2225 [CATransaction begin]; 2226 [CATransaction setDisableActions:YES]; 2227 [self addSubview:_data->_layerHostingView.get()]; 2228 2229 // Create a root layer that will back the NSView. 2230 RetainPtr<CALayer> rootLayer(AdoptNS, [[CALayer alloc] init]); 2231#ifndef NDEBUG 2232 [rootLayer.get() setName:@"Hosting root layer"]; 2233#endif 2234 2235 CALayer *renderLayer = WKMakeRenderLayer(layerTreeContext.contextID); 2236 [rootLayer.get() addSublayer:renderLayer]; 2237 2238 [_data->_layerHostingView.get() setLayer:rootLayer.get()]; 2239 [_data->_layerHostingView.get() setWantsLayer:YES]; 2240 2241 [CATransaction commit]; 2242} 2243 2244- (void)_exitAcceleratedCompositingMode 2245{ 2246 ASSERT(_data->_layerHostingView); 2247 2248 [_data->_layerHostingView.get() removeFromSuperview]; 2249 [_data->_layerHostingView.get() setLayer:nil]; 2250 [_data->_layerHostingView.get() setWantsLayer:NO]; 2251 2252 _data->_layerHostingView = nullptr; 2253} 2254 2255- (void)_setAccessibilityWebProcessToken:(NSData *)data 2256{ 2257 _data->_remoteAccessibilityChild = WKAXRemoteElementForToken(data); 2258 [self _updateRemoteAccessibilityRegistration:YES]; 2259} 2260 2261- (void)_setComplexTextInputEnabled:(BOOL)complexTextInputEnabled pluginComplexTextInputIdentifier:(uint64_t)pluginComplexTextInputIdentifier 2262{ 2263 BOOL inputSourceChanged = _data->_pluginComplexTextInputIdentifier; 2264 2265 if (complexTextInputEnabled) { 2266 // Check if we're already allowing text input for this plug-in. 2267 if (pluginComplexTextInputIdentifier == _data->_pluginComplexTextInputIdentifier) 2268 return; 2269 2270 _data->_pluginComplexTextInputIdentifier = pluginComplexTextInputIdentifier; 2271 2272 } else { 2273 // Check if we got a request to disable complex text input for a plug-in that is not the current plug-in. 2274 if (pluginComplexTextInputIdentifier != _data->_pluginComplexTextInputIdentifier) 2275 return; 2276 2277 _data->_pluginComplexTextInputIdentifier = 0; 2278 } 2279 2280 if (inputSourceChanged) { 2281 // Inform the out of line window that the input source changed. 2282 [[WKTextInputWindowController sharedTextInputWindowController] keyboardInputSourceChanged]; 2283 } 2284} 2285 2286- (void)_setPageHasCustomRepresentation:(BOOL)pageHasCustomRepresentation 2287{ 2288 _data->_pdfViewController = nullptr; 2289 2290 if (pageHasCustomRepresentation) 2291 _data->_pdfViewController = PDFViewController::create(self); 2292} 2293 2294- (void)_didFinishLoadingDataForCustomRepresentationWithSuggestedFilename:(const String&)suggestedFilename dataReference:(const CoreIPC::DataReference&)dataReference 2295{ 2296 ASSERT(_data->_pdfViewController); 2297 2298 _data->_pdfViewController->setPDFDocumentData(_data->_page->mainFrame()->mimeType(), suggestedFilename, dataReference); 2299} 2300 2301- (double)_customRepresentationZoomFactor 2302{ 2303 if (!_data->_pdfViewController) 2304 return 1; 2305 2306 return _data->_pdfViewController->zoomFactor(); 2307} 2308 2309- (void)_setCustomRepresentationZoomFactor:(double)zoomFactor 2310{ 2311 if (!_data->_pdfViewController) 2312 return; 2313 2314 _data->_pdfViewController->setZoomFactor(zoomFactor); 2315} 2316 2317- (void)_findStringInCustomRepresentation:(NSString *)string withFindOptions:(WebKit::FindOptions)options maxMatchCount:(NSUInteger)count 2318{ 2319 if (!_data->_pdfViewController) 2320 return; 2321 2322 _data->_pdfViewController->findString(string, options, count); 2323} 2324 2325- (void)_countStringMatchesInCustomRepresentation:(NSString *)string withFindOptions:(WebKit::FindOptions)options maxMatchCount:(NSUInteger)count 2326{ 2327 if (!_data->_pdfViewController) 2328 return; 2329 2330 _data->_pdfViewController->countStringMatches(string, options, count); 2331} 2332 2333- (void)_setDragImage:(NSImage *)image at:(NSPoint)clientPoint linkDrag:(BOOL)linkDrag 2334{ 2335 // We need to prevent re-entering this call to avoid crashing in AppKit. 2336 // Given the asynchronous nature of WebKit2 this can now happen. 2337 if (_data->_dragHasStarted) 2338 return; 2339 2340 _data->_dragHasStarted = YES; 2341 [super dragImage:image 2342 at:clientPoint 2343 offset:NSZeroSize 2344 event:(linkDrag) ? [NSApp currentEvent] :_data->_mouseDownEvent 2345 pasteboard:[NSPasteboard pasteboardWithName:NSDragPboard] 2346 source:self 2347 slideBack:YES]; 2348 _data->_dragHasStarted = NO; 2349} 2350 2351- (void)_updateSecureInputState 2352{ 2353 if (![[self window] isKeyWindow] || ![self _isFocused]) { 2354 if (_data->_inSecureInputState) { 2355 DisableSecureEventInput(); 2356 _data->_inSecureInputState = NO; 2357 } 2358 return; 2359 } 2360 // WKView has a single input context for all editable areas (except for plug-ins). 2361 NSTextInputContext *context = [super inputContext]; 2362 bool isInPasswordField = _data->_page->editorState().isInPasswordField; 2363 2364 if (isInPasswordField) { 2365 if (!_data->_inSecureInputState) 2366 EnableSecureEventInput(); 2367 static NSArray *romanInputSources = [[NSArray alloc] initWithObjects:&NSAllRomanInputSourcesLocaleIdentifier count:1]; 2368 [context setAllowedInputSourceLocales:romanInputSources]; 2369 } else { 2370 if (_data->_inSecureInputState) 2371 DisableSecureEventInput(); 2372 [context setAllowedInputSourceLocales:nil]; 2373 } 2374 _data->_inSecureInputState = isInPasswordField; 2375} 2376 2377- (void)_updateTextInputStateIncludingSecureInputState:(BOOL)updateSecureInputState 2378{ 2379 const EditorState& editorState = _data->_page->editorState(); 2380 if (updateSecureInputState) { 2381 // This is a temporary state when editing. Flipping secure input state too quickly can expose race conditions. 2382 if (!editorState.selectionIsNone) 2383 [self _updateSecureInputState]; 2384 } 2385 2386 if (!editorState.hasComposition || editorState.shouldIgnoreCompositionSelectionChange) 2387 return; 2388 2389 _data->_page->confirmCompositionWithoutDisturbingSelection(); 2390 2391 [self _notifyInputContextAboutDiscardedComposition]; 2392} 2393 2394- (void)_resetTextInputState 2395{ 2396 [self _notifyInputContextAboutDiscardedComposition]; 2397 2398 if (_data->_inSecureInputState) { 2399 DisableSecureEventInput(); 2400 _data->_inSecureInputState = NO; 2401 } 2402} 2403 2404- (void)_setDrawingAreaSize:(NSSize)size 2405{ 2406 if (!_data->_page->drawingArea()) 2407 return; 2408 2409 _data->_page->drawingArea()->setSize(IntSize(size), IntSize(_data->_resizeScrollOffset)); 2410 _data->_resizeScrollOffset = NSZeroSize; 2411} 2412 2413- (void)_didChangeScrollbarsForMainFrame 2414{ 2415 [self _updateGrowBoxForWindowFrameChange]; 2416} 2417 2418#if ENABLE(FULLSCREEN_API) 2419- (WKFullScreenWindowController*)fullScreenWindowController 2420{ 2421 if (!_data->_fullScreenWindowController) { 2422 _data->_fullScreenWindowController.adoptNS([[WKFullScreenWindowController alloc] init]); 2423 [_data->_fullScreenWindowController.get() setWebView:self]; 2424 } 2425 return _data->_fullScreenWindowController.get(); 2426} 2427#endif 2428 2429- (bool)_executeSavedCommandBySelector:(SEL)selector 2430{ 2431 // The sink does two things: 1) Tells us if the responder went unhandled, and 2432 // 2) prevents any NSBeep; we don't ever want to beep here. 2433 RetainPtr<WKResponderChainSink> sink(AdoptNS, [[WKResponderChainSink alloc] initWithResponderChain:self]); 2434 [super doCommandBySelector:selector]; 2435 [sink.get() detach]; 2436 return ![sink.get() didReceiveUnhandledCommand]; 2437} 2438 2439- (void)_cacheWindowBottomCornerRect 2440{ 2441#if !defined(BUILDING_ON_SNOW_LEOPARD) 2442 // FIXME: We should remove this code when <rdar://problem/9362085> is resolved. 2443 NSWindow *window = [self window]; 2444 if (!window) 2445 return; 2446 2447 _data->_windowBottomCornerIntersectionRect = [window _intersectBottomCornersWithRect:[self convertRect:[self visibleRect] toView:nil]]; 2448 if (!NSIsEmptyRect(_data->_windowBottomCornerIntersectionRect)) 2449 [self setNeedsDisplayInRect:[self convertRect:_data->_windowBottomCornerIntersectionRect fromView:nil]]; 2450#endif 2451} 2452 2453@end 2454 2455@implementation WKView (Private) 2456 2457- (void)disableFrameSizeUpdates 2458{ 2459 _frameSizeUpdatesDisabledCount++; 2460} 2461 2462- (void)enableFrameSizeUpdates 2463{ 2464 if (!_frameSizeUpdatesDisabledCount) 2465 return; 2466 2467 if (!(--_frameSizeUpdatesDisabledCount)) 2468 [self _setDrawingAreaSize:[self frame].size]; 2469} 2470 2471- (BOOL)frameSizeUpdatesDisabled 2472{ 2473 return _frameSizeUpdatesDisabledCount > 0; 2474} 2475 2476- (void)performDictionaryLookupAtCurrentMouseLocation 2477{ 2478 NSPoint thePoint = [NSEvent mouseLocation]; 2479 thePoint = [[self window] convertScreenToBase:thePoint]; 2480 thePoint = [self convertPoint:thePoint fromView:nil]; 2481 2482 _data->_page->performDictionaryLookupAtLocation(FloatPoint(thePoint.x, thePoint.y)); 2483} 2484 2485- (NSInteger)spellCheckerDocumentTag 2486{ 2487 if (!_data->_hasSpellCheckerDocumentTag) { 2488 _data->_spellCheckerDocumentTag = [NSSpellChecker uniqueSpellDocumentTag]; 2489 _data->_hasSpellCheckerDocumentTag = YES; 2490 } 2491 return _data->_spellCheckerDocumentTag; 2492} 2493 2494- (void)handleCorrectionPanelResult:(NSString*)result 2495{ 2496 _data->_page->handleCorrectionPanelResult(result); 2497} 2498 2499@end 2500 2501@implementation WKResponderChainSink 2502 2503- (id)initWithResponderChain:(NSResponder *)chain 2504{ 2505 self = [super init]; 2506 if (!self) 2507 return nil; 2508 _lastResponderInChain = chain; 2509 while (NSResponder *next = [_lastResponderInChain nextResponder]) 2510 _lastResponderInChain = next; 2511 [_lastResponderInChain setNextResponder:self]; 2512 return self; 2513} 2514 2515- (void)detach 2516{ 2517 [_lastResponderInChain setNextResponder:nil]; 2518 _lastResponderInChain = nil; 2519} 2520 2521- (bool)didReceiveUnhandledCommand 2522{ 2523 return _didReceiveUnhandledCommand; 2524} 2525 2526- (void)noResponderFor:(SEL)selector 2527{ 2528 _didReceiveUnhandledCommand = true; 2529} 2530 2531- (void)doCommandBySelector:(SEL)selector 2532{ 2533 _didReceiveUnhandledCommand = true; 2534} 2535 2536- (BOOL)tryToPerform:(SEL)action with:(id)object 2537{ 2538 _didReceiveUnhandledCommand = true; 2539 return YES; 2540} 2541 2542@end 2543