1/* 2 * Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved. 3 * (C) 2006, 2007 Graham Dennis (graham.dennis@gmail.com) 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of 15 * its contributors may be used to endorse or promote products derived 16 * from this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY 19 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY 22 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 */ 29 30#import "WebHTMLView.h" 31 32#import "DOMCSSStyleDeclarationInternal.h" 33#import "DOMDocumentFragmentInternal.h" 34#import "DOMDocumentInternal.h" 35#import "DOMNodeInternal.h" 36#import "DOMRangeInternal.h" 37#import "WebArchive.h" 38#import "WebClipView.h" 39#import "WebDOMOperationsInternal.h" 40#import "WebDataSourceInternal.h" 41#import "WebDefaultUIDelegate.h" 42#import "WebDelegateImplementationCaching.h" 43#import "WebDocumentInternal.h" 44#import "WebDynamicScrollBarsViewInternal.h" 45#import "WebEditingDelegate.h" 46#import "WebElementDictionary.h" 47#import "WebFrameInternal.h" 48#import "WebFramePrivate.h" 49#import "WebFrameViewInternal.h" 50#import "WebHTMLRepresentationPrivate.h" 51#import "WebHTMLViewInternal.h" 52#import "WebKitLogging.h" 53#import "WebKitNSStringExtras.h" 54#import "WebKitVersionChecks.h" 55#import "WebLocalizableStringsInternal.h" 56#import "WebNSEventExtras.h" 57#import "WebNSFileManagerExtras.h" 58#import "WebNSImageExtras.h" 59#import "WebNSObjectExtras.h" 60#import "WebNSPasteboardExtras.h" 61#import "WebNSPrintOperationExtras.h" 62#import "WebNSURLExtras.h" 63#import "WebNSViewExtras.h" 64#import "WebNetscapePluginView.h" 65#import "WebNodeHighlight.h" 66#import "WebPluginController.h" 67#import "WebPreferences.h" 68#import "WebPreferencesPrivate.h" 69#import "WebResourcePrivate.h" 70#import "WebTextCompletionController.h" 71#import "WebTypesInternal.h" 72#import "WebUIDelegatePrivate.h" 73#import "WebViewInternal.h" 74#import <AppKit/NSAccessibility.h> 75#import <ApplicationServices/ApplicationServices.h> 76#import <WebCore/CSSMutableStyleDeclaration.h> 77#import <WebCore/CachedImage.h> 78#import <WebCore/CachedResourceClient.h> 79#import <WebCore/ColorMac.h> 80#import <WebCore/ContextMenu.h> 81#import <WebCore/ContextMenuController.h> 82#import <WebCore/Document.h> 83#import <WebCore/DocumentFragment.h> 84#import <WebCore/DocumentMarkerController.h> 85#import <WebCore/DragController.h> 86#import <WebCore/Editor.h> 87#import <WebCore/EditorDeleteAction.h> 88#import <WebCore/Element.h> 89#import <WebCore/EventHandler.h> 90#import <WebCore/ExceptionHandlers.h> 91#import <WebCore/FloatRect.h> 92#import <WebCore/FocusController.h> 93#import <WebCore/Frame.h> 94#import <WebCore/FrameLoader.h> 95#import <WebCore/FrameView.h> 96#import <WebCore/HTMLConverter.h> 97#import <WebCore/HTMLNames.h> 98#import <WebCore/HitTestResult.h> 99#import <WebCore/Image.h> 100#import <WebCore/KeyboardEvent.h> 101#import <WebCore/LegacyWebArchive.h> 102#import <WebCore/MIMETypeRegistry.h> 103#import <WebCore/Page.h> 104#import <WebCore/PlatformKeyboardEvent.h> 105#import <WebCore/Range.h> 106#import <WebCore/RenderWidget.h> 107#import <WebCore/RenderView.h> 108#import <WebCore/RuntimeApplicationChecks.h> 109#import <WebCore/SelectionController.h> 110#import <WebCore/SharedBuffer.h> 111#import <WebCore/SimpleFontData.h> 112#import <WebCore/Text.h> 113#import <WebCore/WebCoreObjCExtras.h> 114#import <WebCore/WebFontCache.h> 115#import <WebCore/WebNSAttributedStringExtras.h> 116#import <WebCore/markup.h> 117#import <WebKit/DOM.h> 118#import <WebKit/DOMExtensions.h> 119#import <WebKit/DOMPrivate.h> 120#import <WebKitSystemInterface.h> 121#import <dlfcn.h> 122#import <limits> 123#import <runtime/InitializeThreading.h> 124#import <wtf/Threading.h> 125 126#if USE(ACCELERATED_COMPOSITING) 127#import <QuartzCore/QuartzCore.h> 128#endif 129 130using namespace WebCore; 131using namespace HTMLNames; 132using namespace WTF; 133using namespace std; 134 135@interface WebMenuTarget : NSObject { 136 WebCore::ContextMenuController* _menuController; 137} 138+ (WebMenuTarget*)sharedMenuTarget; 139- (WebCore::ContextMenuController*)menuController; 140- (void)setMenuController:(WebCore::ContextMenuController*)menuController; 141- (void)forwardContextMenuAction:(id)sender; 142- (BOOL)validateMenuItem:(NSMenuItem *)item; 143@end 144 145static WebMenuTarget* target; 146 147@implementation WebMenuTarget 148 149+ (WebMenuTarget*)sharedMenuTarget 150{ 151 if (!target) 152 target = [[WebMenuTarget alloc] init]; 153 return target; 154} 155 156- (WebCore::ContextMenuController*)menuController 157{ 158 return _menuController; 159} 160 161- (void)setMenuController:(WebCore::ContextMenuController*)menuController 162{ 163 _menuController = menuController; 164} 165 166- (void)forwardContextMenuAction:(id)sender 167{ 168 WebCore::ContextMenuItem item(WebCore::ActionType, static_cast<WebCore::ContextMenuAction>([sender tag]), [sender title]); 169 _menuController->contextMenuItemSelected(&item); 170} 171 172- (BOOL)validateMenuItem:(NSMenuItem *)item 173{ 174 WebCore::ContextMenuItem coreItem(item); 175 ASSERT(_menuController->contextMenu()); 176 _menuController->checkOrEnableIfNeeded(coreItem); 177 return coreItem.enabled(); 178} 179 180@end 181 182@interface NSWindow (BorderViewAccess) 183- (NSView*)_web_borderView; 184@end 185 186@implementation NSWindow (BorderViewAccess) 187- (NSView*)_web_borderView 188{ 189 return _borderView; 190} 191@end 192 193@interface WebResponderChainSink : NSResponder { 194 NSResponder* _lastResponderInChain; 195 BOOL _receivedUnhandledCommand; 196} 197- (id)initWithResponderChain:(NSResponder *)chain; 198- (void)detach; 199- (BOOL)receivedUnhandledCommand; 200@end 201 202// if YES, do the standard NSView hit test (which can't give the right result when HTML overlaps a view) 203static BOOL forceNSViewHitTest; 204 205// if YES, do the "top WebHTMLView" hit test (which we'd like to do all the time but can't because of Java requirements [see bug 4349721]) 206static BOOL forceWebHTMLViewHitTest; 207 208static WebHTMLView *lastHitView; 209 210static bool needsCursorRectsSupportAtPoint(NSWindow* window, NSPoint point) 211{ 212 forceNSViewHitTest = YES; 213 NSView* view = [[window _web_borderView] hitTest:point]; 214 forceNSViewHitTest = NO; 215 216 // WebHTMLView doesn't use cursor rects. 217 if ([view isKindOfClass:[WebHTMLView class]]) 218 return false; 219 220#if ENABLE(NETSCAPE_PLUGIN_API) 221 // Neither do NPAPI plug-ins. 222 if ([view isKindOfClass:[WebBaseNetscapePluginView class]]) 223 return false; 224#endif 225 226 // Non-Web content, WebPDFView, and WebKit plug-ins use normal cursor handling. 227 return true; 228} 229 230#ifndef BUILDING_ON_TIGER 231 232static IMP oldSetCursorForMouseLocationIMP; 233 234// Overriding an internal method is a hack; <rdar://problem/7662987> tracks finding a better solution. 235static void setCursor(NSWindow *self, SEL cmd, NSPoint point) 236{ 237 if (needsCursorRectsSupportAtPoint(self, point)) 238 oldSetCursorForMouseLocationIMP(self, cmd, point); 239} 240 241#else 242 243static IMP oldResetCursorRectsIMP; 244static IMP oldSetCursorIMP; 245static BOOL canSetCursor = YES; 246 247static void resetCursorRects(NSWindow* self, SEL cmd) 248{ 249 canSetCursor = needsCursorRectsSupportAtPoint(self, [self mouseLocationOutsideOfEventStream]); 250 oldResetCursorRectsIMP(self, cmd); 251 canSetCursor = YES; 252} 253 254static void setCursor(NSCursor* self, SEL cmd) 255{ 256 if (canSetCursor) 257 oldSetCursorIMP(self, cmd); 258} 259 260#endif 261 262extern "C" { 263 264// Need to declare these attribute names because AppKit exports them but does not make them available in API or SPI headers. 265 266extern NSString *NSMarkedClauseSegmentAttributeName; 267extern NSString *NSTextInputReplacementRangeAttributeName; 268 269} 270 271@interface NSView (WebNSViewDetails) 272- (void)_recursiveDisplayRectIfNeededIgnoringOpacity:(NSRect)rect isVisibleRect:(BOOL)isVisibleRect rectIsVisibleRectForView:(NSView *)visibleView topView:(BOOL)topView; 273- (void)_recursiveDisplayAllDirtyWithLockFocus:(BOOL)needsLockFocus visRect:(NSRect)visRect; 274- (void)_recursive:(BOOL)recurse displayRectIgnoringOpacity:(NSRect)displayRect inContext:(NSGraphicsContext *)context topView:(BOOL)topView; 275- (NSRect)_dirtyRect; 276- (void)_setDrawsOwnDescendants:(BOOL)drawsOwnDescendants; 277- (BOOL)_drawnByAncestor; 278- (void)_invalidateGStatesForTree; 279- (void)_propagateDirtyRectsToOpaqueAncestors; 280- (void)_windowChangedKeyState; 281#if USE(ACCELERATED_COMPOSITING) && defined(BUILDING_ON_LEOPARD) 282- (void)_updateLayerGeometryFromView; 283#endif 284@end 285 286#if USE(ACCELERATED_COMPOSITING) 287static IMP oldSetNeedsDisplayInRectIMP; 288 289static void setNeedsDisplayInRect(NSView *self, SEL cmd, NSRect invalidRect) 290{ 291 if (![self _drawnByAncestor]) { 292 oldSetNeedsDisplayInRectIMP(self, cmd, invalidRect); 293 return; 294 } 295 296 static Class webFrameViewClass = [WebFrameView class]; 297 WebFrameView *enclosingWebFrameView = (WebFrameView *)self; 298 while (enclosingWebFrameView && ![enclosingWebFrameView isKindOfClass:webFrameViewClass]) 299 enclosingWebFrameView = (WebFrameView *)[enclosingWebFrameView superview]; 300 301 if (!enclosingWebFrameView) { 302 oldSetNeedsDisplayInRectIMP(self, cmd, invalidRect); 303 return; 304 } 305 306 Frame* coreFrame = core([enclosingWebFrameView webFrame]); 307 FrameView* frameView = coreFrame ? coreFrame->view() : 0; 308 if (!frameView || !frameView->isEnclosedInCompositingLayer()) { 309 oldSetNeedsDisplayInRectIMP(self, cmd, invalidRect); 310 return; 311 } 312 313 NSRect invalidRectInWebFrameViewCoordinates = [enclosingWebFrameView convertRect:invalidRect fromView:self]; 314 IntRect invalidRectInFrameViewCoordinates(invalidRectInWebFrameViewCoordinates); 315 if (![enclosingWebFrameView isFlipped]) 316 invalidRectInFrameViewCoordinates.setY(frameView->frameRect().size().height() - invalidRectInFrameViewCoordinates.maxY()); 317 318 frameView->invalidateRect(invalidRectInFrameViewCoordinates); 319} 320#endif // USE(ACCELERATED_COMPOSITING) 321 322@interface NSApplication (WebNSApplicationDetails) 323- (void)speakString:(NSString *)string; 324@end 325 326@interface NSWindow (WebNSWindowDetails) 327- (id)_newFirstResponderAfterResigning; 328@end 329 330@interface NSAttributedString (WebNSAttributedStringDetails) 331- (id)_initWithDOMRange:(DOMRange *)range; 332- (DOMDocumentFragment *)_documentFromRange:(NSRange)range document:(DOMDocument *)document documentAttributes:(NSDictionary *)dict subresources:(NSArray **)subresources; 333@end 334 335@interface NSSpellChecker (WebNSSpellCheckerDetails) 336- (void)learnWord:(NSString *)word; 337@end 338 339// By imaging to a width a little wider than the available pixels, 340// thin pages will be scaled down a little, matching the way they 341// print in IE and Camino. This lets them use fewer sheets than they 342// would otherwise, which is presumably why other browsers do this. 343// Wide pages will be scaled down more than this. 344const float _WebHTMLViewPrintingMinimumShrinkFactor = 1.25; 345 346// This number determines how small we are willing to reduce the page content 347// in order to accommodate the widest line. If the page would have to be 348// reduced smaller to make the widest line fit, we just clip instead (this 349// behavior matches MacIE and Mozilla, at least) 350const float _WebHTMLViewPrintingMaximumShrinkFactor = 2; 351 352#define AUTOSCROLL_INTERVAL 0.1f 353 354// Any non-zero value will do, but using something recognizable might help us debug some day. 355#define TRACKING_RECT_TAG 0xBADFACE 356 357// FIXME: This constant is copied from AppKit's _NXSmartPaste constant. 358#define WebSmartPastePboardType @"NeXT smart paste pasteboard type" 359 360#define STANDARD_WEIGHT 5 361#define MIN_BOLD_WEIGHT 7 362#define STANDARD_BOLD_WEIGHT 9 363 364// Fake URL scheme. 365#define WebDataProtocolScheme @"webkit-fake-url" 366 367// <rdar://problem/4985524> References to WebCoreScrollView as a subview of a WebHTMLView may be present 368// in some NIB files, so NSUnarchiver must be still able to look up this now-unused class. 369@interface WebCoreScrollView : NSScrollView 370@end 371 372@implementation WebCoreScrollView 373@end 374 375// We need this to be able to safely reference the CachedImage for the promised drag data 376static CachedResourceClient* promisedDataClient() 377{ 378 static CachedResourceClient* staticCachedResourceClient = new CachedResourceClient; 379 return staticCachedResourceClient; 380} 381 382@interface WebHTMLView (WebHTMLViewFileInternal) 383- (BOOL)_imageExistsAtPaths:(NSArray *)paths; 384- (DOMDocumentFragment *)_documentFragmentFromPasteboard:(NSPasteboard *)pasteboard inContext:(DOMRange *)context allowPlainText:(BOOL)allowPlainText; 385- (NSString *)_plainTextFromPasteboard:(NSPasteboard *)pasteboard; 386- (void)_pasteWithPasteboard:(NSPasteboard *)pasteboard allowPlainText:(BOOL)allowPlainText; 387- (void)_pasteAsPlainTextWithPasteboard:(NSPasteboard *)pasteboard; 388- (void)_removeMouseMovedObserverUnconditionally; 389- (void)_removeSuperviewObservers; 390- (void)_removeWindowObservers; 391- (BOOL)_shouldInsertFragment:(DOMDocumentFragment *)fragment replacingDOMRange:(DOMRange *)range givenAction:(WebViewInsertAction)action; 392- (BOOL)_shouldInsertText:(NSString *)text replacingDOMRange:(DOMRange *)range givenAction:(WebViewInsertAction)action; 393- (BOOL)_shouldReplaceSelectionWithText:(NSString *)text givenAction:(WebViewInsertAction)action; 394- (DOMRange *)_selectedRange; 395- (BOOL)_shouldDeleteRange:(DOMRange *)range; 396- (NSView *)_hitViewForEvent:(NSEvent *)event; 397- (void)_writeSelectionWithPasteboardTypes:(NSArray *)types toPasteboard:(NSPasteboard *)pasteboard cachedAttributedString:(NSAttributedString *)attributedString; 398- (DOMRange *)_documentRange; 399- (void)_setMouseDownEvent:(NSEvent *)event; 400- (WebHTMLView *)_topHTMLView; 401- (BOOL)_isTopHTMLView; 402- (void)_web_setPrintingModeRecursive; 403- (void)_web_setPrintingModeRecursiveAndAdjustViewSize; 404- (void)_web_clearPrintingModeRecursive; 405@end 406 407#if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) 408 409@interface WebHTMLView (WebHTMLViewTextCheckingInternal) 410- (void)orderFrontSubstitutionsPanel:(id)sender; 411- (BOOL)smartInsertDeleteEnabled; 412- (void)setSmartInsertDeleteEnabled:(BOOL)flag; 413- (void)toggleSmartInsertDelete:(id)sender; 414- (BOOL)isAutomaticQuoteSubstitutionEnabled; 415- (void)setAutomaticQuoteSubstitutionEnabled:(BOOL)flag; 416- (void)toggleAutomaticQuoteSubstitution:(id)sender; 417- (BOOL)isAutomaticLinkDetectionEnabled; 418- (void)setAutomaticLinkDetectionEnabled:(BOOL)flag; 419- (void)toggleAutomaticLinkDetection:(id)sender; 420- (BOOL)isAutomaticDashSubstitutionEnabled; 421- (void)setAutomaticDashSubstitutionEnabled:(BOOL)flag; 422- (void)toggleAutomaticDashSubstitution:(id)sender; 423- (BOOL)isAutomaticTextReplacementEnabled; 424- (void)setAutomaticTextReplacementEnabled:(BOOL)flag; 425- (void)toggleAutomaticTextReplacement:(id)sender; 426- (BOOL)isAutomaticSpellingCorrectionEnabled; 427- (void)setAutomaticSpellingCorrectionEnabled:(BOOL)flag; 428- (void)toggleAutomaticSpellingCorrection:(id)sender; 429@end 430 431#endif 432 433@interface WebHTMLView (WebForwardDeclaration) // FIXME: Put this in a normal category and stop doing the forward declaration trick. 434- (void)_setPrinting:(BOOL)printing minimumPageLogicalWidth:(float)minPageWidth logicalHeight:(float)minPageHeight maximumPageLogicalWidth:(float)maxPageWidth adjustViewSize:(BOOL)adjustViewSize paginateScreenContent:(BOOL)paginateScreenContent; 435- (void)_updateSecureInputState; 436@end 437 438@class NSTextInputContext; 439@interface NSResponder (AppKitDetails) 440- (NSTextInputContext *)inputContext; 441@end 442 443@interface NSObject (NSTextInputContextDetails) 444- (BOOL)wantsToHandleMouseEvents; 445- (BOOL)handleMouseEvent:(NSEvent *)event; 446@end 447 448@interface WebHTMLView (WebNSTextInputSupport) <NSTextInput> 449- (void)_updateSelectionForInputManager; 450@end 451 452@interface WebHTMLView (WebEditingStyleSupport) 453- (DOMCSSStyleDeclaration *)_emptyStyle; 454- (NSString *)_colorAsString:(NSColor *)color; 455@end 456 457@interface NSView (WebHTMLViewFileInternal) 458- (void)_web_addDescendantWebHTMLViewsToArray:(NSMutableArray *) array; 459@end 460 461@interface NSMutableDictionary (WebHTMLViewFileInternal) 462- (void)_web_setObjectIfNotNil:(id)object forKey:(id)key; 463@end 464 465struct WebHTMLViewInterpretKeyEventsParameters { 466 KeyboardEvent* event; 467 bool eventInterpretationHadSideEffects; 468 bool shouldSaveCommands; 469 bool consumedByIM; 470 bool executingSavedKeypressCommands; 471}; 472 473@interface WebHTMLViewPrivate : NSObject { 474@public 475 BOOL closed; 476 BOOL ignoringMouseDraggedEvents; 477 BOOL printing; 478 BOOL paginateScreenContent; 479 BOOL observingMouseMovedNotifications; 480 BOOL observingSuperviewNotifications; 481 BOOL observingWindowNotifications; 482 483 id savedSubviews; 484 BOOL subviewsSetAside; 485 486#if USE(ACCELERATED_COMPOSITING) 487 NSView *layerHostingView; 488 BOOL drawingIntoLayer; 489#endif 490 491 NSEvent *mouseDownEvent; // Kept after handling the event. 492 BOOL handlingMouseDownEvent; 493 NSEvent *keyDownEvent; // Kept after handling the event. 494 495 // A WebHTMLView has a single input context, but we return nil when in non-editable content to avoid making input methods do their work. 496 // This state is saved each time selection changes, because computing it causes style recalc, which is not always safe to do. 497 BOOL exposeInputContext; 498 499 // Track whether the view has set a secure input state. 500 BOOL isInSecureInputState; 501 502 BOOL _forceUpdateSecureInputState; 503 504 NSPoint lastScrollPosition; 505#ifndef BUILDING_ON_TIGER 506 BOOL inScrollPositionChanged; 507#endif 508 509 WebPluginController *pluginController; 510 511 NSString *toolTip; 512 NSToolTipTag lastToolTipTag; 513 id trackingRectOwner; 514 void *trackingRectUserData; 515 516 NSTimer *autoscrollTimer; 517 NSEvent *autoscrollTriggerEvent; 518 519 NSArray *pageRects; 520 521 NSMutableDictionary *highlighters; 522 523#ifdef BUILDING_ON_TIGER 524 BOOL nextResponderDisabledOnce; 525#endif 526 527 WebTextCompletionController *completionController; 528 529 BOOL transparentBackground; 530 531 WebHTMLViewInterpretKeyEventsParameters* interpretKeyEventsParameters; 532 533 WebDataSource *dataSource; 534 WebCore::CachedImage* promisedDragTIFFDataSource; 535 536 CFRunLoopTimerRef updateMouseoverTimer; 537 538 SEL selectorForDoCommandBySelector; 539 540#ifndef NDEBUG 541 BOOL enumeratingSubviews; 542#endif 543} 544- (void)clear; 545@end 546 547static NSCellStateValue kit(TriState state) 548{ 549 switch (state) { 550 case FalseTriState: 551 return NSOffState; 552 case TrueTriState: 553 return NSOnState; 554 case MixedTriState: 555 return NSMixedState; 556 } 557 ASSERT_NOT_REACHED(); 558 return NSOffState; 559} 560 561static FindOptions coreOptions(WebFindOptions options) 562{ 563 return (options & WebFindOptionsCaseInsensitive ? CaseInsensitive : 0) 564 | (options & WebFindOptionsAtWordStarts ? AtWordStarts : 0) 565 | (options & WebFindOptionsTreatMedialCapitalAsWordStart ? TreatMedialCapitalAsWordStart : 0) 566 | (options & WebFindOptionsBackwards ? Backwards : 0) 567 | (options & WebFindOptionsWrapAround ? WrapAround : 0) 568 | (options & WebFindOptionsStartInSelection ? StartInSelection : 0); 569} 570 571@implementation WebHTMLViewPrivate 572 573+ (void)initialize 574{ 575 JSC::initializeThreading(); 576 WTF::initializeMainThreadToProcessMainThread(); 577#ifndef BUILDING_ON_TIGER 578 WebCoreObjCFinalizeOnMainThread(self); 579#endif 580 581#ifndef BUILDING_ON_TIGER 582 if (!oldSetCursorForMouseLocationIMP) { 583 Method setCursorMethod = class_getInstanceMethod([NSWindow class], @selector(_setCursorForMouseLocation:)); 584 ASSERT(setCursorMethod); 585 oldSetCursorForMouseLocationIMP = method_setImplementation(setCursorMethod, (IMP)setCursor); 586 ASSERT(oldSetCursorForMouseLocationIMP); 587 } 588 589#if USE(ACCELERATED_COMPOSITING) 590 if (!oldSetNeedsDisplayInRectIMP) { 591 Method setNeedsDisplayInRectMethod = class_getInstanceMethod([NSView class], @selector(setNeedsDisplayInRect:)); 592 ASSERT(setNeedsDisplayInRectMethod); 593 oldSetNeedsDisplayInRectIMP = method_setImplementation(setNeedsDisplayInRectMethod, (IMP)setNeedsDisplayInRect); 594 ASSERT(oldSetNeedsDisplayInRectIMP); 595 } 596#endif // USE(ACCELERATED_COMPOSITING) 597 598#else // defined(BUILDING_ON_TIGER) 599 if (!oldSetCursorIMP) { 600 Method setCursorMethod = class_getInstanceMethod([NSCursor class], @selector(set)); 601 ASSERT(setCursorMethod); 602 oldSetCursorIMP = method_setImplementation(setCursorMethod, (IMP)setCursor); 603 ASSERT(oldSetCursorIMP); 604 } 605 if (!oldResetCursorRectsIMP) { 606 Method resetCursorRectsMethod = class_getInstanceMethod([NSWindow class], @selector(resetCursorRects)); 607 ASSERT(resetCursorRectsMethod); 608 oldResetCursorRectsIMP = method_setImplementation(resetCursorRectsMethod, (IMP)resetCursorRects); 609 ASSERT(oldResetCursorRectsIMP); 610 } 611#endif 612 613} 614 615- (void)dealloc 616{ 617 if (WebCoreObjCScheduleDeallocateOnMainThread([WebHTMLViewPrivate class], self)) 618 return; 619 620 ASSERT(!autoscrollTimer); 621 ASSERT(!autoscrollTriggerEvent); 622 ASSERT(!updateMouseoverTimer); 623 624 [mouseDownEvent release]; 625 [keyDownEvent release]; 626 [pluginController release]; 627 [toolTip release]; 628 [completionController release]; 629 [dataSource release]; 630 [highlighters release]; 631 if (promisedDragTIFFDataSource) 632 promisedDragTIFFDataSource->removeClient(promisedDataClient()); 633 634 [super dealloc]; 635} 636 637- (void)finalize 638{ 639 ASSERT_MAIN_THREAD(); 640 641 if (promisedDragTIFFDataSource) 642 promisedDragTIFFDataSource->removeClient(promisedDataClient()); 643 644 [super finalize]; 645} 646 647- (void)clear 648{ 649 [mouseDownEvent release]; 650 [keyDownEvent release]; 651 [pluginController release]; 652 [toolTip release]; 653 [completionController release]; 654 [dataSource release]; 655 [highlighters release]; 656 if (promisedDragTIFFDataSource) 657 promisedDragTIFFDataSource->removeClient(promisedDataClient()); 658 659 mouseDownEvent = nil; 660 keyDownEvent = nil; 661 pluginController = nil; 662 toolTip = nil; 663 completionController = nil; 664 dataSource = nil; 665 highlighters = nil; 666 promisedDragTIFFDataSource = 0; 667 668#if USE(ACCELERATED_COMPOSITING) 669 layerHostingView = nil; 670#endif 671} 672 673@end 674 675@implementation WebHTMLView (WebHTMLViewFileInternal) 676 677- (DOMRange *)_documentRange 678{ 679 return [[[self _frame] DOMDocument] _documentRange]; 680} 681 682- (BOOL)_imageExistsAtPaths:(NSArray *)paths 683{ 684 NSEnumerator *enumerator = [paths objectEnumerator]; 685 NSString *path; 686 687 while ((path = [enumerator nextObject]) != nil) { 688 NSString *MIMEType = WKGetMIMETypeForExtension([path pathExtension]); 689 if (MIMETypeRegistry::isSupportedImageResourceMIMEType(MIMEType)) 690 return YES; 691 } 692 693 return NO; 694} 695 696- (WebDataSource *)_dataSource 697{ 698 return _private->dataSource; 699} 700 701- (WebView *)_webView 702{ 703 return [_private->dataSource _webView]; 704} 705 706- (WebFrameView *)_frameView 707{ 708 return [[_private->dataSource webFrame] frameView]; 709} 710 711- (DOMDocumentFragment *)_documentFragmentWithPaths:(NSArray *)paths 712{ 713 DOMDocumentFragment *fragment; 714 NSEnumerator *enumerator = [paths objectEnumerator]; 715 NSMutableArray *domNodes = [[NSMutableArray alloc] init]; 716 NSString *path; 717 718 while ((path = [enumerator nextObject]) != nil) { 719 // Non-image file types; _web_userVisibleString is appropriate here because this will 720 // be pasted as visible text. 721 NSString *url = [[[NSURL fileURLWithPath:path] _webkit_canonicalize] _web_userVisibleString]; 722 [domNodes addObject:[[[self _frame] DOMDocument] createTextNode: url]]; 723 } 724 725 fragment = [[self _frame] _documentFragmentWithNodesAsParagraphs:domNodes]; 726 727 [domNodes release]; 728 729 return [fragment firstChild] != nil ? fragment : nil; 730} 731 732+ (NSArray *)_excludedElementsForAttributedStringConversion 733{ 734 static NSArray *elements = nil; 735 if (elements == nil) { 736 elements = [[NSArray alloc] initWithObjects: 737 // Omit style since we want style to be inline so the fragment can be easily inserted. 738 @"style", 739 // Omit xml so the result is not XHTML. 740 @"xml", 741 // Omit tags that will get stripped when converted to a fragment anyway. 742 @"doctype", @"html", @"head", @"body", 743 // Omit deprecated tags. 744 @"applet", @"basefont", @"center", @"dir", @"font", @"isindex", @"menu", @"s", @"strike", @"u", 745 // Omit object so no file attachments are part of the fragment. 746 @"object", nil]; 747 CFRetain(elements); 748 } 749 return elements; 750} 751 752static NSURL* uniqueURLWithRelativePart(NSString *relativePart) 753{ 754 CFUUIDRef UUIDRef = CFUUIDCreate(kCFAllocatorDefault); 755 NSString *UUIDString = (NSString *)CFUUIDCreateString(kCFAllocatorDefault, UUIDRef); 756 CFRelease(UUIDRef); 757 NSURL *URL = [NSURL URLWithString:[NSString stringWithFormat:@"%@://%@/%@", WebDataProtocolScheme, UUIDString, relativePart]]; 758 CFRelease(UUIDString); 759 760 return URL; 761} 762 763- (DOMDocumentFragment *)_documentFragmentFromPasteboard:(NSPasteboard *)pasteboard 764 inContext:(DOMRange *)context 765 allowPlainText:(BOOL)allowPlainText 766{ 767 NSArray *types = [pasteboard types]; 768 DOMDocumentFragment *fragment = nil; 769 770 if ([types containsObject:WebArchivePboardType] && 771 (fragment = [self _documentFragmentFromPasteboard:pasteboard 772 forType:WebArchivePboardType 773 inContext:context 774 subresources:0])) 775 return fragment; 776 777 if ([types containsObject:NSFilenamesPboardType] && 778 (fragment = [self _documentFragmentFromPasteboard:pasteboard 779 forType:NSFilenamesPboardType 780 inContext:context 781 subresources:0])) 782 return fragment; 783 784 if ([types containsObject:NSHTMLPboardType] && 785 (fragment = [self _documentFragmentFromPasteboard:pasteboard 786 forType:NSHTMLPboardType 787 inContext:context 788 subresources:0])) 789 return fragment; 790 791 if ([types containsObject:NSRTFDPboardType] && 792 (fragment = [self _documentFragmentFromPasteboard:pasteboard 793 forType:NSRTFDPboardType 794 inContext:context 795 subresources:0])) 796 return fragment; 797 798 if ([types containsObject:NSRTFPboardType] && 799 (fragment = [self _documentFragmentFromPasteboard:pasteboard 800 forType:NSRTFPboardType 801 inContext:context 802 subresources:0])) 803 return fragment; 804 805 if ([types containsObject:NSTIFFPboardType] && 806 (fragment = [self _documentFragmentFromPasteboard:pasteboard 807 forType:NSTIFFPboardType 808 inContext:context 809 subresources:0])) 810 return fragment; 811 812 if ([types containsObject:NSPDFPboardType] && 813 (fragment = [self _documentFragmentFromPasteboard:pasteboard 814 forType:NSPDFPboardType 815 inContext:context 816 subresources:0])) 817 return fragment; 818 819#if defined(BUILDING_ON_TIGER) || defined(BUILDING_ON_LEOPARD) 820 if ([types containsObject:NSPICTPboardType] && 821 (fragment = [self _documentFragmentFromPasteboard:pasteboard 822 forType:NSPICTPboardType 823 inContext:context 824 subresources:0])) 825 return fragment; 826#endif 827 828 // Only 10.5 and higher support setting and retrieving pasteboard types with UTIs, but we don't believe 829 // that any applications on Tiger put types for which we only have a UTI, like PNG, on the pasteboard. 830 if ([types containsObject:(NSString*)kUTTypePNG] && 831 (fragment = [self _documentFragmentFromPasteboard:pasteboard 832 forType:(NSString*)kUTTypePNG 833 inContext:context 834 subresources:0])) 835 return fragment; 836 837 if ([types containsObject:NSURLPboardType] && 838 (fragment = [self _documentFragmentFromPasteboard:pasteboard 839 forType:NSURLPboardType 840 inContext:context 841 subresources:0])) 842 return fragment; 843 844 if (allowPlainText && [types containsObject:NSStringPboardType] && 845 (fragment = [self _documentFragmentFromPasteboard:pasteboard 846 forType:NSStringPboardType 847 inContext:context 848 subresources:0])) { 849 return fragment; 850 } 851 852 return nil; 853} 854 855- (NSString *)_plainTextFromPasteboard:(NSPasteboard *)pasteboard 856{ 857 NSArray *types = [pasteboard types]; 858 859 if ([types containsObject:NSStringPboardType]) 860 return [[pasteboard stringForType:NSStringPboardType] precomposedStringWithCanonicalMapping]; 861 862 NSAttributedString *attributedString = nil; 863 NSString *string; 864 865 if ([types containsObject:NSRTFDPboardType]) 866 attributedString = [[NSAttributedString alloc] initWithRTFD:[pasteboard dataForType:NSRTFDPboardType] documentAttributes:NULL]; 867 if (attributedString == nil && [types containsObject:NSRTFPboardType]) 868 attributedString = [[NSAttributedString alloc] initWithRTF:[pasteboard dataForType:NSRTFPboardType] documentAttributes:NULL]; 869 if (attributedString != nil) { 870 string = [[attributedString string] copy]; 871 [attributedString release]; 872 return [string autorelease]; 873 } 874 875 if ([types containsObject:NSFilenamesPboardType]) { 876 string = [[pasteboard propertyListForType:NSFilenamesPboardType] componentsJoinedByString:@"\n"]; 877 if (string != nil) 878 return string; 879 } 880 881 NSURL *URL; 882 883 if ((URL = [NSURL URLFromPasteboard:pasteboard])) { 884 string = [URL _web_userVisibleString]; 885 if ([string length] > 0) 886 return string; 887 } 888 889 return nil; 890} 891 892- (void)_pasteWithPasteboard:(NSPasteboard *)pasteboard allowPlainText:(BOOL)allowPlainText 893{ 894 WebView *webView = [[self _webView] retain]; 895 [webView _setInsertionPasteboard:pasteboard]; 896 897 DOMRange *range = [self _selectedRange]; 898 Frame* coreFrame = core([self _frame]); 899 900#if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) && !defined(BUILDING_ON_SNOW_LEOPARD) 901 DOMDocumentFragment *fragment = [self _documentFragmentFromPasteboard:pasteboard inContext:range allowPlainText:allowPlainText]; 902 if (fragment && [self _shouldInsertFragment:fragment replacingDOMRange:range givenAction:WebViewInsertActionPasted]) 903 coreFrame->editor()->pasteAsFragment(core(fragment), [self _canSmartReplaceWithPasteboard:pasteboard], false); 904#else 905 // Mail is ignoring the frament passed to the delegate and creates a new one. 906 // We want to avoid creating the fragment twice. 907 if (applicationIsAppleMail()) { 908 if ([self _shouldInsertFragment:nil replacingDOMRange:range givenAction:WebViewInsertActionPasted]) { 909 DOMDocumentFragment *fragment = [self _documentFragmentFromPasteboard:pasteboard inContext:range allowPlainText:allowPlainText]; 910 if (fragment) 911 coreFrame->editor()->pasteAsFragment(core(fragment), [self _canSmartReplaceWithPasteboard:pasteboard], false); 912 } 913 } else { 914 DOMDocumentFragment *fragment = [self _documentFragmentFromPasteboard:pasteboard inContext:range allowPlainText:allowPlainText]; 915 if (fragment && [self _shouldInsertFragment:fragment replacingDOMRange:range givenAction:WebViewInsertActionPasted]) 916 coreFrame->editor()->pasteAsFragment(core(fragment), [self _canSmartReplaceWithPasteboard:pasteboard], false); 917 } 918#endif 919 [webView _setInsertionPasteboard:nil]; 920 [webView release]; 921} 922 923- (void)_pasteAsPlainTextWithPasteboard:(NSPasteboard *)pasteboard 924{ 925 WebView *webView = [[self _webView] retain]; 926 [webView _setInsertionPasteboard:pasteboard]; 927 928 NSString *text = [self _plainTextFromPasteboard:pasteboard]; 929 if ([self _shouldReplaceSelectionWithText:text givenAction:WebViewInsertActionPasted]) 930 [[self _frame] _replaceSelectionWithText:text selectReplacement:NO smartReplace:[self _canSmartReplaceWithPasteboard:pasteboard]]; 931 932 [webView _setInsertionPasteboard:nil]; 933 [webView release]; 934} 935 936// This method is needed to support Mac OS X services. 937- (BOOL)readSelectionFromPasteboard:(NSPasteboard *)pasteboard 938{ 939 Frame* coreFrame = core([self _frame]); 940 if (!coreFrame) 941 return NO; 942 if (coreFrame->selection()->isContentRichlyEditable()) 943 [self _pasteWithPasteboard:pasteboard allowPlainText:YES]; 944 else 945 [self _pasteAsPlainTextWithPasteboard:pasteboard]; 946 return YES; 947} 948 949- (void)_removeMouseMovedObserverUnconditionally 950{ 951 if (!_private || !_private->observingMouseMovedNotifications) 952 return; 953 954 [[NSNotificationCenter defaultCenter] removeObserver:self name:WKMouseMovedNotification() object:nil]; 955 _private->observingMouseMovedNotifications = false; 956} 957 958- (void)_removeSuperviewObservers 959{ 960 if (!_private || !_private->observingSuperviewNotifications) 961 return; 962 963 NSView *superview = [self superview]; 964 if (!superview || ![self window]) 965 return; 966 967 NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; 968 [notificationCenter removeObserver:self name:NSViewFrameDidChangeNotification object:superview]; 969 [notificationCenter removeObserver:self name:NSViewBoundsDidChangeNotification object:superview]; 970 971 _private->observingSuperviewNotifications = false; 972} 973 974- (void)_removeWindowObservers 975{ 976 if (!_private->observingWindowNotifications) 977 return; 978 979 NSWindow *window = [self window]; 980 if (!window) 981 return; 982 983 NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; 984 [notificationCenter removeObserver:self name:NSWindowDidBecomeKeyNotification object:nil]; 985 [notificationCenter removeObserver:self name:NSWindowDidResignKeyNotification object:nil]; 986 [notificationCenter removeObserver:self name:NSWindowWillCloseNotification object:window]; 987 988 _private->observingWindowNotifications = false; 989} 990 991- (BOOL)_shouldInsertFragment:(DOMDocumentFragment *)fragment replacingDOMRange:(DOMRange *)range givenAction:(WebViewInsertAction)action 992{ 993 WebView *webView = [self _webView]; 994 DOMNode *child = [fragment firstChild]; 995 if ([fragment lastChild] == child && [child isKindOfClass:[DOMCharacterData class]]) 996 return [[webView _editingDelegateForwarder] webView:webView shouldInsertText:[(DOMCharacterData *)child data] replacingDOMRange:range givenAction:action]; 997 return [[webView _editingDelegateForwarder] webView:webView shouldInsertNode:fragment replacingDOMRange:range givenAction:action]; 998} 999 1000- (BOOL)_shouldInsertText:(NSString *)text replacingDOMRange:(DOMRange *)range givenAction:(WebViewInsertAction)action 1001{ 1002 WebView *webView = [self _webView]; 1003 return [[webView _editingDelegateForwarder] webView:webView shouldInsertText:text replacingDOMRange:range givenAction:action]; 1004} 1005 1006- (BOOL)_shouldReplaceSelectionWithText:(NSString *)text givenAction:(WebViewInsertAction)action 1007{ 1008 return [self _shouldInsertText:text replacingDOMRange:[self _selectedRange] givenAction:action]; 1009} 1010 1011- (DOMRange *)_selectedRange 1012{ 1013 Frame* coreFrame = core([self _frame]); 1014 return coreFrame ? kit(coreFrame->selection()->toNormalizedRange().get()) : nil; 1015} 1016 1017- (BOOL)_shouldDeleteRange:(DOMRange *)range 1018{ 1019 Frame* coreFrame = core([self _frame]); 1020 return coreFrame && coreFrame->editor()->shouldDeleteRange(core(range)); 1021} 1022 1023- (NSView *)_hitViewForEvent:(NSEvent *)event 1024{ 1025 // Usually, we hack AK's hitTest method to catch all events at the topmost WebHTMLView. 1026 // Callers of this method, however, want to query the deepest view instead. 1027 forceNSViewHitTest = YES; 1028 NSView *hitView = [[[self window] contentView] hitTest:[event locationInWindow]]; 1029 forceNSViewHitTest = NO; 1030 return hitView; 1031} 1032 1033- (void)_writeSelectionWithPasteboardTypes:(NSArray *)types toPasteboard:(NSPasteboard *)pasteboard cachedAttributedString:(NSAttributedString *)attributedString 1034{ 1035 // Put HTML on the pasteboard. 1036 if ([types containsObject:WebArchivePboardType]) { 1037 if (RefPtr<LegacyWebArchive> coreArchive = LegacyWebArchive::createFromSelection(core([self _frame]))) { 1038 if (RetainPtr<CFDataRef> data = coreArchive ? coreArchive->rawDataRepresentation() : 0) 1039 [pasteboard setData:(NSData *)data.get() forType:WebArchivePboardType]; 1040 } 1041 } 1042 1043 // Put the attributed string on the pasteboard (RTF/RTFD format). 1044 if ([types containsObject:NSRTFDPboardType]) { 1045 if (attributedString == nil) { 1046 attributedString = [self selectedAttributedString]; 1047 } 1048 NSData *RTFDData = [attributedString RTFDFromRange:NSMakeRange(0, [attributedString length]) documentAttributes:nil]; 1049 [pasteboard setData:RTFDData forType:NSRTFDPboardType]; 1050 } 1051 if ([types containsObject:NSRTFPboardType]) { 1052 if (!attributedString) 1053 attributedString = [self selectedAttributedString]; 1054 if ([attributedString containsAttachments]) 1055 attributedString = attributedStringByStrippingAttachmentCharacters(attributedString); 1056 NSData *RTFData = [attributedString RTFFromRange:NSMakeRange(0, [attributedString length]) documentAttributes:nil]; 1057 [pasteboard setData:RTFData forType:NSRTFPboardType]; 1058 } 1059 1060 // Put plain string on the pasteboard. 1061 if ([types containsObject:NSStringPboardType]) { 1062 // Map to a plain old space because this is better for source code, other browsers do it, 1063 // and because HTML forces you to do this any time you want two spaces in a row. 1064 NSMutableString *s = [[self selectedString] mutableCopy]; 1065 const unichar NonBreakingSpaceCharacter = 0xA0; 1066 NSString *NonBreakingSpaceString = [NSString stringWithCharacters:&NonBreakingSpaceCharacter length:1]; 1067 [s replaceOccurrencesOfString:NonBreakingSpaceString withString:@" " options:0 range:NSMakeRange(0, [s length])]; 1068 [pasteboard setString:s forType:NSStringPboardType]; 1069 [s release]; 1070 } 1071 1072 if ([self _canSmartCopyOrDelete] && [types containsObject:WebSmartPastePboardType]) { 1073 [pasteboard setData:nil forType:WebSmartPastePboardType]; 1074 } 1075} 1076 1077- (void)_setMouseDownEvent:(NSEvent *)event 1078{ 1079 ASSERT(!event || [event type] == NSLeftMouseDown || [event type] == NSRightMouseDown || [event type] == NSOtherMouseDown); 1080 1081 if (event == _private->mouseDownEvent) 1082 return; 1083 1084 [event retain]; 1085 [_private->mouseDownEvent release]; 1086 _private->mouseDownEvent = event; 1087} 1088 1089- (void)_cancelUpdateMouseoverTimer 1090{ 1091 if (_private->updateMouseoverTimer) { 1092 CFRunLoopTimerInvalidate(_private->updateMouseoverTimer); 1093 CFRelease(_private->updateMouseoverTimer); 1094 _private->updateMouseoverTimer = NULL; 1095 } 1096} 1097 1098- (WebHTMLView *)_topHTMLView 1099{ 1100 // FIXME: this can fail if the dataSource is nil, which happens when the WebView is tearing down from the window closing. 1101 WebHTMLView *view = (WebHTMLView *)[[[[_private->dataSource _webView] mainFrame] frameView] documentView]; 1102 ASSERT(!view || [view isKindOfClass:[WebHTMLView class]]); 1103 return view; 1104} 1105 1106- (BOOL)_isTopHTMLView 1107{ 1108 // FIXME: this should be a cached boolean that doesn't rely on _topHTMLView since that can fail (see _topHTMLView). 1109 return self == [self _topHTMLView]; 1110} 1111 1112- (void)_web_setPrintingModeRecursive 1113{ 1114 [self _setPrinting:YES minimumPageLogicalWidth:0 logicalHeight:0 maximumPageLogicalWidth:0 adjustViewSize:NO paginateScreenContent:[self _isInScreenPaginationMode]]; 1115 1116#ifndef NDEBUG 1117 _private->enumeratingSubviews = YES; 1118#endif 1119 1120 NSMutableArray *descendantWebHTMLViews = [[NSMutableArray alloc] init]; 1121 1122 [self _web_addDescendantWebHTMLViewsToArray:descendantWebHTMLViews]; 1123 1124 unsigned count = [descendantWebHTMLViews count]; 1125 for (unsigned i = 0; i < count; ++i) 1126 [[descendantWebHTMLViews objectAtIndex:i] _setPrinting:YES minimumPageLogicalWidth:0 logicalHeight:0 maximumPageLogicalWidth:0 adjustViewSize:NO paginateScreenContent:[self _isInScreenPaginationMode]]; 1127 1128 [descendantWebHTMLViews release]; 1129 1130#ifndef NDEBUG 1131 _private->enumeratingSubviews = NO; 1132#endif 1133} 1134 1135- (void)_web_clearPrintingModeRecursive 1136{ 1137 [self _setPrinting:NO minimumPageLogicalWidth:0 logicalHeight:0 maximumPageLogicalWidth:0 adjustViewSize:NO paginateScreenContent:[self _isInScreenPaginationMode]]; 1138 1139#ifndef NDEBUG 1140 _private->enumeratingSubviews = YES; 1141#endif 1142 1143 NSMutableArray *descendantWebHTMLViews = [[NSMutableArray alloc] init]; 1144 1145 [self _web_addDescendantWebHTMLViewsToArray:descendantWebHTMLViews]; 1146 1147 unsigned count = [descendantWebHTMLViews count]; 1148 for (unsigned i = 0; i < count; ++i) 1149 [[descendantWebHTMLViews objectAtIndex:i] _setPrinting:NO minimumPageLogicalWidth:0 logicalHeight:0 maximumPageLogicalWidth:0 adjustViewSize:NO paginateScreenContent:[self _isInScreenPaginationMode]]; 1150 1151 [descendantWebHTMLViews release]; 1152 1153#ifndef NDEBUG 1154 _private->enumeratingSubviews = NO; 1155#endif 1156} 1157 1158- (void)_web_setPrintingModeRecursiveAndAdjustViewSize 1159{ 1160 [self _setPrinting:YES minimumPageLogicalWidth:0 logicalHeight:0 maximumPageLogicalWidth:0 adjustViewSize:YES paginateScreenContent:[self _isInScreenPaginationMode]]; 1161 1162#ifndef NDEBUG 1163 _private->enumeratingSubviews = YES; 1164#endif 1165 1166 NSMutableArray *descendantWebHTMLViews = [[NSMutableArray alloc] init]; 1167 1168 [self _web_addDescendantWebHTMLViewsToArray:descendantWebHTMLViews]; 1169 1170 unsigned count = [descendantWebHTMLViews count]; 1171 for (unsigned i = 0; i < count; ++i) 1172 [[descendantWebHTMLViews objectAtIndex:i] _setPrinting:YES minimumPageLogicalWidth:0 logicalHeight:0 maximumPageLogicalWidth:0 adjustViewSize:YES paginateScreenContent:[self _isInScreenPaginationMode]]; 1173 1174 [descendantWebHTMLViews release]; 1175 1176#ifndef NDEBUG 1177 _private->enumeratingSubviews = NO; 1178#endif 1179} 1180 1181@end 1182 1183@implementation WebHTMLView (WebPrivate) 1184 1185+ (NSArray *)supportedMIMETypes 1186{ 1187 return [WebHTMLRepresentation supportedMIMETypes]; 1188} 1189 1190+ (NSArray *)supportedImageMIMETypes 1191{ 1192 return [WebHTMLRepresentation supportedImageMIMETypes]; 1193} 1194 1195+ (NSArray *)supportedNonImageMIMETypes 1196{ 1197 return [WebHTMLRepresentation supportedNonImageMIMETypes]; 1198} 1199 1200+ (NSArray *)unsupportedTextMIMETypes 1201{ 1202 return [WebHTMLRepresentation unsupportedTextMIMETypes]; 1203} 1204 1205+ (void)_postFlagsChangedEvent:(NSEvent *)flagsChangedEvent 1206{ 1207 // This is a workaround for: <rdar://problem/2981619> NSResponder_Private should include notification for FlagsChanged 1208 NSEvent *fakeEvent = [NSEvent mouseEventWithType:NSMouseMoved 1209 location:[[flagsChangedEvent window] convertScreenToBase:[NSEvent mouseLocation]] 1210 modifierFlags:[flagsChangedEvent modifierFlags] 1211 timestamp:[flagsChangedEvent timestamp] 1212 windowNumber:[flagsChangedEvent windowNumber] 1213 context:[flagsChangedEvent context] 1214 eventNumber:0 clickCount:0 pressure:0]; 1215 1216 // Pretend it's a mouse move. 1217 [[NSNotificationCenter defaultCenter] 1218 postNotificationName:WKMouseMovedNotification() object:self 1219 userInfo:[NSDictionary dictionaryWithObject:fakeEvent forKey:@"NSEvent"]]; 1220} 1221 1222- (id)_bridge 1223{ 1224 // This method exists to maintain compatibility with Leopard's Dictionary.app, since it 1225 // calls _bridge to get access to convertNSRangeToDOMRange: and convertDOMRangeToNSRange:. 1226 // Return the WebFrame, which implements the compatibility methods. <rdar://problem/6002160> 1227 return [self _frame]; 1228} 1229 1230- (void)_updateMouseoverWithFakeEvent 1231{ 1232 [self _cancelUpdateMouseoverTimer]; 1233 1234 NSEvent *fakeEvent = [NSEvent mouseEventWithType:NSMouseMoved 1235 location:[[self window] convertScreenToBase:[NSEvent mouseLocation]] 1236 modifierFlags:[[NSApp currentEvent] modifierFlags] 1237 timestamp:[NSDate timeIntervalSinceReferenceDate] 1238 windowNumber:[[self window] windowNumber] 1239 context:[[NSApp currentEvent] context] 1240 eventNumber:0 clickCount:0 pressure:0]; 1241 1242 [self _updateMouseoverWithEvent:fakeEvent]; 1243} 1244 1245static void _updateMouseoverTimerCallback(CFRunLoopTimerRef timer, void *info) 1246{ 1247 WebHTMLView *view = (WebHTMLView *)info; 1248 1249 [view _updateMouseoverWithFakeEvent]; 1250} 1251 1252- (void)_frameOrBoundsChanged 1253{ 1254 WebView *webView = [self _webView]; 1255 WebDynamicScrollBarsView *scrollView = [[[webView mainFrame] frameView] _scrollView]; 1256 1257 NSPoint origin = [[self superview] bounds].origin; 1258 if (!NSEqualPoints(_private->lastScrollPosition, origin) && ![scrollView inProgrammaticScroll]) { 1259 if (Frame* coreFrame = core([self _frame])) { 1260 if (FrameView* coreView = coreFrame->view()) { 1261#ifndef BUILDING_ON_TIGER 1262 _private->inScrollPositionChanged = YES; 1263#endif 1264 coreView->scrollPositionChangedViaPlatformWidget(); 1265#ifndef BUILDING_ON_TIGER 1266 _private->inScrollPositionChanged = NO; 1267#endif 1268 } 1269 } 1270 1271 [_private->completionController endRevertingChange:NO moveLeft:NO]; 1272 1273 [[webView _UIDelegateForwarder] webView:webView didScrollDocumentInFrameView:[self _frameView]]; 1274 } 1275 _private->lastScrollPosition = origin; 1276 1277 if ([self window] && !_private->closed && !_private->updateMouseoverTimer) { 1278 CFRunLoopTimerContext context = { 0, self, NULL, NULL, NULL }; 1279 1280 // Use a 100ms delay so that the synthetic mouse over update doesn't cause cursor thrashing when pages are loading 1281 // and scrolling rapidly back to back. 1282 _private->updateMouseoverTimer = CFRunLoopTimerCreate(NULL, CFAbsoluteTimeGetCurrent() + 0.1, 0, 0, 0, 1283 _updateMouseoverTimerCallback, &context); 1284 CFRunLoopAddTimer(CFRunLoopGetCurrent(), _private->updateMouseoverTimer, kCFRunLoopDefaultMode); 1285 } 1286 1287#if USE(ACCELERATED_COMPOSITING) && defined(BUILDING_ON_LEOPARD) 1288 [self _updateLayerHostingViewPosition]; 1289#endif 1290} 1291 1292- (void)_setAsideSubviews 1293{ 1294 ASSERT(!_private->subviewsSetAside); 1295 ASSERT(_private->savedSubviews == nil); 1296 _private->savedSubviews = _subviews; 1297#if USE(ACCELERATED_COMPOSITING) 1298 // We need to keep the layer-hosting view in the subviews, otherwise the layers flash. 1299 if (_private->layerHostingView) { 1300 NSArray* newSubviews = [[NSArray alloc] initWithObjects:_private->layerHostingView, nil]; 1301 _subviews = newSubviews; 1302 } else 1303 _subviews = nil; 1304#else 1305 _subviews = nil; 1306#endif 1307 _private->subviewsSetAside = YES; 1308 } 1309 1310 - (void)_restoreSubviews 1311 { 1312 ASSERT(_private->subviewsSetAside); 1313#if USE(ACCELERATED_COMPOSITING) 1314 if (_private->layerHostingView) { 1315 [_subviews release]; 1316 _subviews = _private->savedSubviews; 1317 } else { 1318 ASSERT(_subviews == nil); 1319 _subviews = _private->savedSubviews; 1320 } 1321#else 1322 ASSERT(_subviews == nil); 1323 _subviews = _private->savedSubviews; 1324#endif 1325 _private->savedSubviews = nil; 1326 _private->subviewsSetAside = NO; 1327} 1328 1329#ifndef NDEBUG 1330 1331- (void)didAddSubview:(NSView *)subview 1332{ 1333 if (_private->enumeratingSubviews) 1334 LOG(View, "A view of class %s was added during subview enumeration for layout or printing mode change. This view might paint without first receiving layout.", object_getClassName([subview class])); 1335} 1336#endif 1337 1338#ifdef BUILDING_ON_TIGER 1339 1340// This is called when we are about to draw, but before our dirty rect is propagated to our ancestors. 1341// That's the perfect time to do a layout, except that ideally we'd want to be sure that we're dirty 1342// before doing it. As a compromise, when we're opaque we do the layout only when actually asked to 1343// draw, but when we're transparent we do the layout at this stage so views behind us know that they 1344// need to be redrawn (in case the layout causes some things to get dirtied). 1345- (void)_propagateDirtyRectsToOpaqueAncestors 1346{ 1347 if (![[self _webView] drawsBackground]) 1348 [self _web_updateLayoutAndStyleIfNeededRecursive]; 1349 [super _propagateDirtyRectsToOpaqueAncestors]; 1350} 1351 1352#else 1353 1354- (void)viewWillDraw 1355{ 1356 // On window close we will be called when the datasource is nil, then hit an assert in _topHTMLView 1357 // So check if the dataSource is nil before calling [self _isTopHTMLView], this can be removed 1358 // once the FIXME in _isTopHTMLView is fixed. 1359 if (_private->dataSource && [self _isTopHTMLView]) 1360 [self _web_updateLayoutAndStyleIfNeededRecursive]; 1361 [super viewWillDraw]; 1362} 1363 1364#endif 1365 1366// Don't let AppKit even draw subviews. We take care of that. 1367- (void)_recursiveDisplayRectIfNeededIgnoringOpacity:(NSRect)rect isVisibleRect:(BOOL)isVisibleRect rectIsVisibleRectForView:(NSView *)visibleView topView:(BOOL)topView 1368{ 1369 // This helps when we print as part of a larger print process. 1370 // If the WebHTMLView itself is what we're printing, then we will never have to do this. 1371 BOOL wasInPrintingMode = _private->printing; 1372 BOOL isPrinting = ![NSGraphicsContext currentContextDrawingToScreen]; 1373 if (isPrinting) { 1374 if (!wasInPrintingMode) 1375 [self _web_setPrintingModeRecursive]; 1376#ifndef BUILDING_ON_TIGER 1377 else 1378 [self _web_updateLayoutAndStyleIfNeededRecursive]; 1379#endif 1380 } else if (wasInPrintingMode) 1381 [self _web_clearPrintingModeRecursive]; 1382 1383#ifndef BUILDING_ON_TIGER 1384 // There are known cases where -viewWillDraw is not called on all views being drawn. 1385 // See <rdar://problem/6964278> for example. Performing layout at this point prevents us from 1386 // trying to paint without layout (which WebCore now refuses to do, instead bailing out without 1387 // drawing at all), but we may still fail to update any regions dirtied by the layout which are 1388 // not already dirty. 1389 if ([self _needsLayout]) { 1390 NSInteger rectCount; 1391 [self getRectsBeingDrawn:0 count:&rectCount]; 1392 if (rectCount) { 1393 LOG_ERROR("View needs layout. Either -viewWillDraw wasn't called or layout was invalidated during the display operation. Performing layout now."); 1394 [self _web_updateLayoutAndStyleIfNeededRecursive]; 1395 } 1396 } 1397#else 1398 // Because Tiger does not have viewWillDraw we need to do layout here. 1399 [self _web_updateLayoutAndStyleIfNeededRecursive]; 1400 [_subviews makeObjectsPerformSelector:@selector(_propagateDirtyRectsToOpaqueAncestors)]; 1401#endif 1402 1403 [self _setAsideSubviews]; 1404 [super _recursiveDisplayRectIfNeededIgnoringOpacity:rect isVisibleRect:isVisibleRect rectIsVisibleRectForView:visibleView topView:topView]; 1405 [self _restoreSubviews]; 1406 1407 if (wasInPrintingMode != isPrinting) { 1408 if (wasInPrintingMode) 1409 [self _web_setPrintingModeRecursive]; 1410 else 1411 [self _web_clearPrintingModeRecursive]; 1412 } 1413} 1414 1415// Don't let AppKit even draw subviews. We take care of that. 1416- (void)_recursiveDisplayAllDirtyWithLockFocus:(BOOL)needsLockFocus visRect:(NSRect)visRect 1417{ 1418 BOOL needToSetAsideSubviews = !_private->subviewsSetAside; 1419 1420 BOOL wasInPrintingMode = _private->printing; 1421 BOOL isPrinting = ![NSGraphicsContext currentContextDrawingToScreen]; 1422 1423 if (needToSetAsideSubviews) { 1424 // This helps when we print as part of a larger print process. 1425 // If the WebHTMLView itself is what we're printing, then we will never have to do this. 1426 if (isPrinting) { 1427 if (!wasInPrintingMode) 1428 [self _web_setPrintingModeRecursive]; 1429#ifndef BUILDING_ON_TIGER 1430 else 1431 [self _web_updateLayoutAndStyleIfNeededRecursive]; 1432#endif 1433 } else if (wasInPrintingMode) 1434 [self _web_clearPrintingModeRecursive]; 1435 1436#ifdef BUILDING_ON_TIGER 1437 1438 // Because Tiger does not have viewWillDraw we need to do layout here. 1439 NSRect boundsBeforeLayout = [self bounds]; 1440 if (!NSIsEmptyRect(visRect)) 1441 [self _web_updateLayoutAndStyleIfNeededRecursive]; 1442 1443 // If layout changes the view's bounds, then we need to recompute the visRect. 1444 // That's because the visRect passed to us was based on the bounds at the time 1445 // we were called. This method is only displayed to draw "all", so it's safe 1446 // to just call visibleRect to compute the entire rectangle. 1447 if (!NSEqualRects(boundsBeforeLayout, [self bounds])) 1448 visRect = [self visibleRect]; 1449 1450#endif 1451 1452 [self _setAsideSubviews]; 1453 } 1454 1455 [super _recursiveDisplayAllDirtyWithLockFocus:needsLockFocus visRect:visRect]; 1456 1457 if (needToSetAsideSubviews) { 1458 if (wasInPrintingMode != isPrinting) { 1459 if (wasInPrintingMode) 1460 [self _web_setPrintingModeRecursive]; 1461 else 1462 [self _web_clearPrintingModeRecursive]; 1463 } 1464 1465 [self _restoreSubviews]; 1466 } 1467} 1468 1469// Don't let AppKit even draw subviews. We take care of that. 1470- (void)_recursive:(BOOL)recurse displayRectIgnoringOpacity:(NSRect)displayRect inContext:(NSGraphicsContext *)context topView:(BOOL)topView 1471{ 1472#ifdef BUILDING_ON_TIGER 1473 // Because Tiger does not have viewWillDraw we need to do layout here. 1474 [self _web_updateLayoutAndStyleIfNeededRecursive]; 1475#endif 1476 1477 [self _setAsideSubviews]; 1478 [super _recursive:recurse displayRectIgnoringOpacity:displayRect inContext:context topView:topView]; 1479 [self _restoreSubviews]; 1480} 1481 1482- (BOOL)_insideAnotherHTMLView 1483{ 1484 return self != [self _topHTMLView]; 1485} 1486 1487- (NSView *)hitTest:(NSPoint)point 1488{ 1489 // WebHTMLView objects handle all events for objects inside them. 1490 // To get those events, we prevent hit testing from AppKit. 1491 1492 // But there are three exceptions to this: 1493 // 1) For right mouse clicks and control clicks we don't yet have an implementation 1494 // that works for nested views, so we let the hit testing go through the 1495 // standard NSView code path (needs to be fixed, see bug 4361618). 1496 // 2) Java depends on doing a hit test inside it's mouse moved handling, 1497 // so we let the hit testing go through the standard NSView code path 1498 // when the current event is a mouse move (except when we are calling 1499 // from _updateMouseoverWithEvent, so we have to use a global, 1500 // forceWebHTMLViewHitTest, for that) 1501 // 3) The acceptsFirstMouse: and shouldDelayWindowOrderingForEvent: methods 1502 // both need to figure out which view to check with inside the WebHTMLView. 1503 // They use a global to change the behavior of hitTest: so they can get the 1504 // right view. The global is forceNSViewHitTest and the method they use to 1505 // do the hit testing is _hitViewForEvent:. (But this does not work correctly 1506 // when there is HTML overlapping the view, see bug 4361626) 1507 // 4) NSAccessibilityHitTest relies on this for checking the cursor position. 1508 // Our check for that is whether the event is NSFlagsChanged. This works 1509 // for VoiceOver's Control-Option-F5 command (move focus to item under cursor) 1510 // and Dictionary's Command-Control-D (open dictionary popup for item under cursor). 1511 // This is of course a hack. 1512 1513 if (_private->closed) 1514 return nil; 1515 1516 BOOL captureHitsOnSubviews; 1517 if (forceNSViewHitTest) 1518 captureHitsOnSubviews = NO; 1519 else if (forceWebHTMLViewHitTest) 1520 captureHitsOnSubviews = YES; 1521 else { 1522 // FIXME: Why doesn't this include mouse entered/exited events, or other mouse button events? 1523 NSEvent *event = [[self window] currentEvent]; 1524 captureHitsOnSubviews = !([event type] == NSMouseMoved 1525 || [event type] == NSRightMouseDown 1526 || ([event type] == NSLeftMouseDown && ([event modifierFlags] & NSControlKeyMask) != 0) 1527 || [event type] == NSFlagsChanged); 1528 } 1529 1530 if (!captureHitsOnSubviews) { 1531 NSView* hitView = [super hitTest:point]; 1532#if USE(ACCELERATED_COMPOSITING) 1533 if (_private && hitView == _private->layerHostingView) 1534 hitView = self; 1535#endif 1536 return hitView; 1537 } 1538 if ([[self superview] mouse:point inRect:[self frame]]) 1539 return self; 1540 return nil; 1541} 1542 1543- (void)_clearLastHitViewIfSelf 1544{ 1545 if (lastHitView == self) 1546 lastHitView = nil; 1547} 1548 1549- (NSTrackingRectTag)addTrackingRect:(NSRect)rect owner:(id)owner userData:(void *)data assumeInside:(BOOL)assumeInside 1550{ 1551 ASSERT(_private->trackingRectOwner == nil); 1552 _private->trackingRectOwner = owner; 1553 _private->trackingRectUserData = data; 1554 return TRACKING_RECT_TAG; 1555} 1556 1557- (NSTrackingRectTag)_addTrackingRect:(NSRect)rect owner:(id)owner userData:(void *)data assumeInside:(BOOL)assumeInside useTrackingNum:(int)tag 1558{ 1559 ASSERT(tag == 0 || tag == TRACKING_RECT_TAG); 1560 ASSERT(_private->trackingRectOwner == nil); 1561 _private->trackingRectOwner = owner; 1562 _private->trackingRectUserData = data; 1563 return TRACKING_RECT_TAG; 1564} 1565 1566- (void)_addTrackingRects:(NSRect *)rects owner:(id)owner userDataList:(void **)userDataList assumeInsideList:(BOOL *)assumeInsideList trackingNums:(NSTrackingRectTag *)trackingNums count:(int)count 1567{ 1568 ASSERT(count == 1); 1569 ASSERT(trackingNums[0] == 0 || trackingNums[0] == TRACKING_RECT_TAG); 1570 ASSERT(_private->trackingRectOwner == nil); 1571 _private->trackingRectOwner = owner; 1572 _private->trackingRectUserData = userDataList[0]; 1573 trackingNums[0] = TRACKING_RECT_TAG; 1574} 1575 1576- (void)removeTrackingRect:(NSTrackingRectTag)tag 1577{ 1578 if (tag == 0) 1579 return; 1580 1581 if (_private && (tag == TRACKING_RECT_TAG)) { 1582 _private->trackingRectOwner = nil; 1583 return; 1584 } 1585 1586 if (_private && (tag == _private->lastToolTipTag)) { 1587 [super removeTrackingRect:tag]; 1588 _private->lastToolTipTag = 0; 1589 return; 1590 } 1591 1592 // If any other tracking rect is being removed, we don't know how it was created 1593 // and it's possible there's a leak involved (see 3500217) 1594 ASSERT_NOT_REACHED(); 1595} 1596 1597- (void)_removeTrackingRects:(NSTrackingRectTag *)tags count:(int)count 1598{ 1599 int i; 1600 for (i = 0; i < count; ++i) { 1601 int tag = tags[i]; 1602 if (tag == 0) 1603 continue; 1604 ASSERT(tag == TRACKING_RECT_TAG); 1605 if (_private != nil) { 1606 _private->trackingRectOwner = nil; 1607 } 1608 } 1609} 1610 1611- (void)_sendToolTipMouseExited 1612{ 1613 // Nothing matters except window, trackingNumber, and userData. 1614 NSEvent *fakeEvent = [NSEvent enterExitEventWithType:NSMouseExited 1615 location:NSMakePoint(0, 0) 1616 modifierFlags:0 1617 timestamp:0 1618 windowNumber:[[self window] windowNumber] 1619 context:NULL 1620 eventNumber:0 1621 trackingNumber:TRACKING_RECT_TAG 1622 userData:_private->trackingRectUserData]; 1623 [_private->trackingRectOwner mouseExited:fakeEvent]; 1624} 1625 1626- (void)_sendToolTipMouseEntered 1627{ 1628 // Nothing matters except window, trackingNumber, and userData. 1629 NSEvent *fakeEvent = [NSEvent enterExitEventWithType:NSMouseEntered 1630 location:NSMakePoint(0, 0) 1631 modifierFlags:0 1632 timestamp:0 1633 windowNumber:[[self window] windowNumber] 1634 context:NULL 1635 eventNumber:0 1636 trackingNumber:TRACKING_RECT_TAG 1637 userData:_private->trackingRectUserData]; 1638 [_private->trackingRectOwner mouseEntered:fakeEvent]; 1639} 1640 1641- (void)_setToolTip:(NSString *)string 1642{ 1643 NSString *toolTip = [string length] == 0 ? nil : string; 1644 NSString *oldToolTip = _private->toolTip; 1645 if ((toolTip == nil || oldToolTip == nil) ? toolTip == oldToolTip : [toolTip isEqualToString:oldToolTip]) { 1646 return; 1647 } 1648 if (oldToolTip) { 1649 [self _sendToolTipMouseExited]; 1650 [oldToolTip release]; 1651 } 1652 _private->toolTip = [toolTip copy]; 1653 if (toolTip) { 1654 // See radar 3500217 for why we remove all tooltips rather than just the single one we created. 1655 [self removeAllToolTips]; 1656 NSRect wideOpenRect = NSMakeRect(-100000, -100000, 200000, 200000); 1657 _private->lastToolTipTag = [self addToolTipRect:wideOpenRect owner:self userData:NULL]; 1658 [self _sendToolTipMouseEntered]; 1659 } 1660} 1661 1662- (NSString *)view:(NSView *)view stringForToolTip:(NSToolTipTag)tag point:(NSPoint)point userData:(void *)data 1663{ 1664 return [[_private->toolTip copy] autorelease]; 1665} 1666 1667- (void)_updateMouseoverWithEvent:(NSEvent *)event 1668{ 1669 if (_private->closed) 1670 return; 1671 1672 NSView *contentView = [[event window] contentView]; 1673 NSPoint locationForHitTest = [[contentView superview] convertPoint:[event locationInWindow] fromView:nil]; 1674 1675 forceWebHTMLViewHitTest = YES; 1676 NSView *hitView = [contentView hitTest:locationForHitTest]; 1677 forceWebHTMLViewHitTest = NO; 1678 1679 WebHTMLView *view = nil; 1680 if ([hitView isKindOfClass:[WebHTMLView class]] && ![[(WebHTMLView *)hitView _webView] isHoverFeedbackSuspended]) 1681 view = (WebHTMLView *)hitView; 1682 1683 if (view) 1684 [view retain]; 1685 1686 if (lastHitView != view && lastHitView && [lastHitView _frame]) { 1687 // If we are moving out of a view (or frame), let's pretend the mouse moved 1688 // all the way out of that view. But we have to account for scrolling, because 1689 // WebCore doesn't understand our clipping. 1690 NSRect visibleRect = [[[[lastHitView _frame] frameView] _scrollView] documentVisibleRect]; 1691 float yScroll = visibleRect.origin.y; 1692 float xScroll = visibleRect.origin.x; 1693 1694 NSEvent *event = [NSEvent mouseEventWithType:NSMouseMoved 1695 location:NSMakePoint(-1 - xScroll, -1 - yScroll) 1696 modifierFlags:[[NSApp currentEvent] modifierFlags] 1697 timestamp:[NSDate timeIntervalSinceReferenceDate] 1698 windowNumber:[[view window] windowNumber] 1699 context:[[NSApp currentEvent] context] 1700 eventNumber:0 clickCount:0 pressure:0]; 1701 if (Frame* lastHitCoreFrame = core([lastHitView _frame])) 1702 lastHitCoreFrame->eventHandler()->mouseMoved(event); 1703 } 1704 1705 lastHitView = view; 1706 1707 if (view) { 1708 if (Frame* coreFrame = core([view _frame])) 1709 coreFrame->eventHandler()->mouseMoved(event); 1710 1711 [view release]; 1712 } 1713} 1714 1715+ (NSArray *)_insertablePasteboardTypes 1716{ 1717 static NSArray *types = nil; 1718 if (!types) { 1719 types = [[NSArray alloc] initWithObjects:WebArchivePboardType, NSHTMLPboardType, NSFilenamesPboardType, NSTIFFPboardType, NSPDFPboardType, 1720#if defined(BUILDING_ON_TIGER) || defined(BUILDING_ON_LEOPARD) 1721 NSPICTPboardType, 1722#endif 1723 NSURLPboardType, NSRTFDPboardType, NSRTFPboardType, NSStringPboardType, NSColorPboardType, kUTTypePNG, nil]; 1724 CFRetain(types); 1725 } 1726 return types; 1727} 1728 1729+ (NSArray *)_selectionPasteboardTypes 1730{ 1731 // FIXME: We should put data for NSHTMLPboardType on the pasteboard but Microsoft Excel doesn't like our format of HTML (3640423). 1732 return [NSArray arrayWithObjects:WebArchivePboardType, NSRTFDPboardType, NSRTFPboardType, NSStringPboardType, nil]; 1733} 1734 1735- (void)pasteboardChangedOwner:(NSPasteboard *)pasteboard 1736{ 1737 [self setPromisedDragTIFFDataSource:0]; 1738} 1739 1740- (void)pasteboard:(NSPasteboard *)pasteboard provideDataForType:(NSString *)type 1741{ 1742 if ([type isEqual:NSRTFDPboardType] && [[pasteboard types] containsObject:WebArchivePboardType]) { 1743 WebArchive *archive = [[WebArchive alloc] initWithData:[pasteboard dataForType:WebArchivePboardType]]; 1744 [pasteboard _web_writePromisedRTFDFromArchive:archive containsImage:[[pasteboard types] containsObject:NSTIFFPboardType]]; 1745 [archive release]; 1746 } else if ([type isEqual:NSTIFFPboardType] && [self promisedDragTIFFDataSource]) { 1747 if (Image* image = [self promisedDragTIFFDataSource]->image()) 1748 [pasteboard setData:(NSData *)image->getTIFFRepresentation() forType:NSTIFFPboardType]; 1749 [self setPromisedDragTIFFDataSource:0]; 1750 } 1751} 1752 1753- (void)_handleAutoscrollForMouseDragged:(NSEvent *)event 1754{ 1755 [self autoscroll:event]; 1756 [self _startAutoscrollTimer:event]; 1757} 1758 1759- (WebPluginController *)_pluginController 1760{ 1761 return _private->pluginController; 1762} 1763 1764- (void)_layoutForPrinting 1765{ 1766 // Set printing mode temporarily so we can adjust the size of the view. This will allow 1767 // AppKit's pagination code to use the correct height for the page content. Leaving printing 1768 // mode on indefinitely would interfere with Mail's printing mechanism (at least), so we just 1769 // turn it off again after adjusting the size. 1770 [self _web_setPrintingModeRecursiveAndAdjustViewSize]; 1771 [self _web_clearPrintingModeRecursive]; 1772} 1773 1774- (void)_smartInsertForString:(NSString *)pasteString replacingRange:(DOMRange *)rangeToReplace beforeString:(NSString **)beforeString afterString:(NSString **)afterString 1775{ 1776 if (!pasteString || !rangeToReplace || ![[self _webView] smartInsertDeleteEnabled]) { 1777 if (beforeString) 1778 *beforeString = nil; 1779 if (afterString) 1780 *afterString = nil; 1781 return; 1782 } 1783 1784 [[self _frame] _smartInsertForString:pasteString replacingRange:rangeToReplace beforeString:beforeString afterString:afterString]; 1785} 1786 1787- (BOOL)_canSmartReplaceWithPasteboard:(NSPasteboard *)pasteboard 1788{ 1789 return [[self _webView] smartInsertDeleteEnabled] && [[pasteboard types] containsObject:WebSmartPastePboardType]; 1790} 1791 1792- (void)_startAutoscrollTimer:(NSEvent *)triggerEvent 1793{ 1794 if (_private->autoscrollTimer == nil) { 1795 _private->autoscrollTimer = [[NSTimer scheduledTimerWithTimeInterval:AUTOSCROLL_INTERVAL 1796 target:self selector:@selector(_autoscroll) userInfo:nil repeats:YES] retain]; 1797 _private->autoscrollTriggerEvent = [triggerEvent retain]; 1798 } 1799} 1800 1801// FIXME: _selectionRect is deprecated in favor of selectionRect, which is in protocol WebDocumentSelection. 1802// We can't remove this yet because it's still in use by Mail. 1803- (NSRect)_selectionRect 1804{ 1805 return [self selectionRect]; 1806} 1807 1808- (void)_stopAutoscrollTimer 1809{ 1810 NSTimer *timer = _private->autoscrollTimer; 1811 _private->autoscrollTimer = nil; 1812 [_private->autoscrollTriggerEvent release]; 1813 _private->autoscrollTriggerEvent = nil; 1814 [timer invalidate]; 1815 [timer release]; 1816} 1817 1818- (void)_autoscroll 1819{ 1820 // Guarantee that the autoscroll timer is invalidated, even if we don't receive 1821 // a mouse up event. 1822 BOOL isStillDown = CGEventSourceButtonState(kCGEventSourceStateCombinedSessionState, kCGMouseButtonLeft); 1823 if (!isStillDown){ 1824 [self _stopAutoscrollTimer]; 1825 return; 1826 } 1827 1828 NSEvent *fakeEvent = [NSEvent mouseEventWithType:NSLeftMouseDragged 1829 location:[[self window] convertScreenToBase:[NSEvent mouseLocation]] 1830 modifierFlags:[[NSApp currentEvent] modifierFlags] 1831 timestamp:[NSDate timeIntervalSinceReferenceDate] 1832 windowNumber:[[self window] windowNumber] 1833 context:[[NSApp currentEvent] context] 1834 eventNumber:0 clickCount:0 pressure:0]; 1835 [self mouseDragged:fakeEvent]; 1836} 1837 1838- (BOOL)_canEdit 1839{ 1840 Frame* coreFrame = core([self _frame]); 1841 return coreFrame && coreFrame->editor()->canEdit(); 1842} 1843 1844- (BOOL)_canEditRichly 1845{ 1846 Frame* coreFrame = core([self _frame]); 1847 return coreFrame && coreFrame->editor()->canEditRichly(); 1848} 1849 1850- (BOOL)_canAlterCurrentSelection 1851{ 1852 return [self _hasSelectionOrInsertionPoint] && [self _isEditable]; 1853} 1854 1855- (BOOL)_hasSelection 1856{ 1857 Frame* coreFrame = core([self _frame]); 1858 return coreFrame && coreFrame->selection()->isRange(); 1859} 1860 1861- (BOOL)_hasSelectionOrInsertionPoint 1862{ 1863 Frame* coreFrame = core([self _frame]); 1864 return coreFrame && coreFrame->selection()->isCaretOrRange(); 1865} 1866 1867- (BOOL)_hasInsertionPoint 1868{ 1869 Frame* coreFrame = core([self _frame]); 1870 return coreFrame && coreFrame->selection()->isCaret(); 1871} 1872 1873- (BOOL)_isEditable 1874{ 1875 Frame* coreFrame = core([self _frame]); 1876 return coreFrame && coreFrame->selection()->isContentEditable(); 1877} 1878 1879- (BOOL)_transparentBackground 1880{ 1881 return _private->transparentBackground; 1882} 1883 1884- (void)_setTransparentBackground:(BOOL)f 1885{ 1886 _private->transparentBackground = f; 1887} 1888 1889- (NSImage *)_selectionDraggingImage 1890{ 1891 if (![self _hasSelection]) 1892 return nil; 1893 NSImage *dragImage = core([self _frame])->selectionImage(); 1894 [dragImage _web_dissolveToFraction:WebDragImageAlpha]; 1895 return dragImage; 1896} 1897 1898- (NSRect)_selectionDraggingRect 1899{ 1900 // Mail currently calls this method. We can eliminate it when Mail no longer calls it. 1901 return [self selectionRect]; 1902} 1903 1904- (DOMNode *)_insertOrderedList 1905{ 1906 Frame* coreFrame = core([self _frame]); 1907 return coreFrame ? kit(coreFrame->editor()->insertOrderedList().get()) : nil; 1908} 1909 1910- (DOMNode *)_insertUnorderedList 1911{ 1912 Frame* coreFrame = core([self _frame]); 1913 return coreFrame ? kit(coreFrame->editor()->insertUnorderedList().get()) : nil; 1914} 1915 1916- (BOOL)_canIncreaseSelectionListLevel 1917{ 1918 Frame* coreFrame = core([self _frame]); 1919 return coreFrame && coreFrame->editor()->canIncreaseSelectionListLevel(); 1920} 1921 1922- (BOOL)_canDecreaseSelectionListLevel 1923{ 1924 Frame* coreFrame = core([self _frame]); 1925 return coreFrame && coreFrame->editor()->canDecreaseSelectionListLevel(); 1926} 1927 1928- (DOMNode *)_increaseSelectionListLevel 1929{ 1930 Frame* coreFrame = core([self _frame]); 1931 return coreFrame ? kit(coreFrame->editor()->increaseSelectionListLevel().get()) : nil; 1932} 1933 1934- (DOMNode *)_increaseSelectionListLevelOrdered 1935{ 1936 Frame* coreFrame = core([self _frame]); 1937 return coreFrame ? kit(coreFrame->editor()->increaseSelectionListLevelOrdered().get()) : nil; 1938} 1939 1940- (DOMNode *)_increaseSelectionListLevelUnordered 1941{ 1942 Frame* coreFrame = core([self _frame]); 1943 return coreFrame ? kit(coreFrame->editor()->increaseSelectionListLevelUnordered().get()) : nil; 1944} 1945 1946- (void)_decreaseSelectionListLevel 1947{ 1948 Frame* coreFrame = core([self _frame]); 1949 if (coreFrame) 1950 coreFrame->editor()->decreaseSelectionListLevel(); 1951} 1952 1953- (void)_setHighlighter:(id<WebHTMLHighlighter>)highlighter ofType:(NSString*)type 1954{ 1955 if (!_private->highlighters) 1956 _private->highlighters = [[NSMutableDictionary alloc] init]; 1957 [_private->highlighters setObject:highlighter forKey:type]; 1958} 1959 1960- (void)_removeHighlighterOfType:(NSString*)type 1961{ 1962 [_private->highlighters removeObjectForKey:type]; 1963} 1964 1965- (void)_writeSelectionToPasteboard:(NSPasteboard *)pasteboard 1966{ 1967 ASSERT([self _hasSelection]); 1968 NSArray *types = [self pasteboardTypesForSelection]; 1969 1970 // Don't write RTFD to the pasteboard when the copied attributed string has no attachments. 1971 NSAttributedString *attributedString = [self selectedAttributedString]; 1972 NSMutableArray *mutableTypes = nil; 1973 if (![attributedString containsAttachments]) { 1974 mutableTypes = [types mutableCopy]; 1975 [mutableTypes removeObject:NSRTFDPboardType]; 1976 types = mutableTypes; 1977 } 1978 1979 [pasteboard declareTypes:types owner:[self _topHTMLView]]; 1980 [self _writeSelectionWithPasteboardTypes:types toPasteboard:pasteboard cachedAttributedString:attributedString]; 1981 [mutableTypes release]; 1982} 1983 1984- (void)close 1985{ 1986 // Check for a nil _private here in case we were created with initWithCoder. In that case, the WebView is just throwing 1987 // out the archived WebHTMLView and recreating a new one if needed. So close doesn't need to do anything in that case. 1988 if (!_private || _private->closed) 1989 return; 1990 1991 _private->closed = YES; 1992 1993 [self _cancelUpdateMouseoverTimer]; 1994 [self _clearLastHitViewIfSelf]; 1995 [self _removeMouseMovedObserverUnconditionally]; 1996 [self _removeWindowObservers]; 1997 [self _removeSuperviewObservers]; 1998 [_private->pluginController destroyAllPlugins]; 1999 [_private->pluginController setDataSource:nil]; 2000 // remove tooltips before clearing _private so removeTrackingRect: will work correctly 2001 [self removeAllToolTips]; 2002 2003 if (_private->isInSecureInputState) { 2004 DisableSecureEventInput(); 2005 _private->isInSecureInputState = NO; 2006 } 2007 2008 [_private clear]; 2009} 2010 2011- (BOOL)_hasHTMLDocument 2012{ 2013 Frame* coreFrame = core([self _frame]); 2014 if (!coreFrame) 2015 return NO; 2016 Document* document = coreFrame->document(); 2017 return document && document->isHTMLDocument(); 2018} 2019 2020- (DOMDocumentFragment *)_documentFragmentFromPasteboard:(NSPasteboard *)pasteboard 2021 forType:(NSString *)pboardType 2022 inContext:(DOMRange *)context 2023 subresources:(NSArray **)subresources 2024{ 2025 if (pboardType == WebArchivePboardType) { 2026 WebArchive *archive = [[WebArchive alloc] initWithData:[pasteboard dataForType:WebArchivePboardType]]; 2027 if (subresources) 2028 *subresources = [archive subresources]; 2029 DOMDocumentFragment *fragment = [[self _dataSource] _documentFragmentWithArchive:archive]; 2030 [archive release]; 2031 return fragment; 2032 } 2033 if (pboardType == NSFilenamesPboardType) 2034 return [self _documentFragmentWithPaths:[pasteboard propertyListForType:NSFilenamesPboardType]]; 2035 2036 if (pboardType == NSHTMLPboardType) { 2037 NSString *HTMLString = [pasteboard stringForType:NSHTMLPboardType]; 2038 // This is a hack to make Microsoft's HTML pasteboard data work. See 3778785. 2039 if ([HTMLString hasPrefix:@"Version:"]) { 2040 NSRange range = [HTMLString rangeOfString:@"<html" options:NSCaseInsensitiveSearch]; 2041 if (range.location != NSNotFound) 2042 HTMLString = [HTMLString substringFromIndex:range.location]; 2043 } 2044 if ([HTMLString length] == 0) 2045 return nil; 2046 2047 return [[self _frame] _documentFragmentWithMarkupString:HTMLString baseURLString:nil]; 2048 } 2049 2050 // The _hasHTMLDocument clause here is a workaround for a bug in NSAttributedString: Radar 5052369. 2051 // If we call _documentFromRange on an XML document we'll get "setInnerHTML: method not found". 2052 // FIXME: Remove this once bug 5052369 is fixed. 2053 if ([self _hasHTMLDocument] && (pboardType == NSRTFPboardType || pboardType == NSRTFDPboardType)) { 2054 NSAttributedString *string = nil; 2055 if (pboardType == NSRTFDPboardType) 2056 string = [[NSAttributedString alloc] initWithRTFD:[pasteboard dataForType:NSRTFDPboardType] documentAttributes:NULL]; 2057 if (string == nil) 2058 string = [[NSAttributedString alloc] initWithRTF:[pasteboard dataForType:NSRTFPboardType] documentAttributes:NULL]; 2059 if (string == nil) 2060 return nil; 2061 2062 NSDictionary *documentAttributes = [[NSDictionary alloc] initWithObjectsAndKeys: 2063 [[self class] _excludedElementsForAttributedStringConversion], NSExcludedElementsDocumentAttribute, 2064 self, @"WebResourceHandler", nil]; 2065 NSArray *s; 2066 2067 BOOL wasDeferringCallbacks = [[self _webView] defersCallbacks]; 2068 if (!wasDeferringCallbacks) 2069 [[self _webView] setDefersCallbacks:YES]; 2070 2071 DOMDocumentFragment *fragment = [string _documentFromRange:NSMakeRange(0, [string length]) 2072 document:[[self _frame] DOMDocument] 2073 documentAttributes:documentAttributes 2074 subresources:&s]; 2075 if (subresources) 2076 *subresources = s; 2077 2078 NSEnumerator *e = [s objectEnumerator]; 2079 WebResource *r; 2080 while ((r = [e nextObject])) 2081 [[self _dataSource] addSubresource:r]; 2082 2083 if (!wasDeferringCallbacks) 2084 [[self _webView] setDefersCallbacks:NO]; 2085 2086 [documentAttributes release]; 2087 [string release]; 2088 return fragment; 2089 } 2090 if (pboardType == NSTIFFPboardType) { 2091 WebResource *resource = [[WebResource alloc] initWithData:[pasteboard dataForType:NSTIFFPboardType] 2092 URL:uniqueURLWithRelativePart(@"image.tiff") 2093 MIMEType:@"image/tiff" 2094 textEncodingName:nil 2095 frameName:nil]; 2096 DOMDocumentFragment *fragment = [[self _dataSource] _documentFragmentWithImageResource:resource]; 2097 [resource release]; 2098 return fragment; 2099 } 2100 if (pboardType == NSPDFPboardType) { 2101 WebResource *resource = [[WebResource alloc] initWithData:[pasteboard dataForType:NSPDFPboardType] 2102 URL:uniqueURLWithRelativePart(@"application.pdf") 2103 MIMEType:@"application/pdf" 2104 textEncodingName:nil 2105 frameName:nil]; 2106 DOMDocumentFragment *fragment = [[self _dataSource] _documentFragmentWithImageResource:resource]; 2107 [resource release]; 2108 return fragment; 2109 } 2110#if defined(BUILDING_ON_TIGER) || defined(BUILDING_ON_LEOPARD) 2111 if (pboardType == NSPICTPboardType) { 2112 WebResource *resource = [[WebResource alloc] initWithData:[pasteboard dataForType:NSPICTPboardType] 2113 URL:uniqueURLWithRelativePart(@"image.pict") 2114 MIMEType:@"image/pict" 2115 textEncodingName:nil 2116 frameName:nil]; 2117 DOMDocumentFragment *fragment = [[self _dataSource] _documentFragmentWithImageResource:resource]; 2118 [resource release]; 2119 return fragment; 2120 } 2121#endif 2122 // Only 10.5 and higher support setting and retrieving pasteboard types with UTIs, but we don't believe 2123 // that any applications on Tiger put types for which we only have a UTI, like PNG, on the pasteboard. 2124 if ([pboardType isEqualToString:(NSString*)kUTTypePNG]) { 2125 WebResource *resource = [[WebResource alloc] initWithData:[pasteboard dataForType:(NSString*)kUTTypePNG] 2126 URL:uniqueURLWithRelativePart(@"image.png") 2127 MIMEType:@"image/png" 2128 textEncodingName:nil 2129 frameName:nil]; 2130 DOMDocumentFragment *fragment = [[self _dataSource] _documentFragmentWithImageResource:resource]; 2131 [resource release]; 2132 return fragment; 2133 } 2134 if (pboardType == NSURLPboardType) { 2135 NSURL *URL = [NSURL URLFromPasteboard:pasteboard]; 2136 DOMDocument* document = [[self _frame] DOMDocument]; 2137 ASSERT(document); 2138 if (!document) 2139 return nil; 2140 DOMHTMLAnchorElement *anchor = (DOMHTMLAnchorElement *)[document createElement:@"a"]; 2141 NSString *URLString = [URL _web_originalDataAsString]; // Original data is ASCII-only, so there is no need to precompose. 2142 if ([URLString length] == 0) 2143 return nil; 2144 NSString *URLTitleString = [[pasteboard stringForType:WebURLNamePboardType] precomposedStringWithCanonicalMapping]; 2145 DOMText *text = [document createTextNode:URLTitleString]; 2146 [anchor setHref:URLString]; 2147 [anchor appendChild:text]; 2148 DOMDocumentFragment *fragment = [document createDocumentFragment]; 2149 [fragment appendChild:anchor]; 2150 return fragment; 2151 } 2152 if (pboardType == NSStringPboardType) 2153 return kit(createFragmentFromText(core(context), [[pasteboard stringForType:NSStringPboardType] precomposedStringWithCanonicalMapping]).get()); 2154 return nil; 2155} 2156 2157#if ENABLE(NETSCAPE_PLUGIN_API) 2158- (void)_pauseNullEventsForAllNetscapePlugins 2159{ 2160 NSArray *subviews = [self subviews]; 2161 unsigned int subviewCount = [subviews count]; 2162 unsigned int subviewIndex; 2163 2164 for (subviewIndex = 0; subviewIndex < subviewCount; subviewIndex++) { 2165 NSView *subview = [subviews objectAtIndex:subviewIndex]; 2166 if ([subview isKindOfClass:[WebBaseNetscapePluginView class]]) 2167 [(WebBaseNetscapePluginView *)subview stopTimers]; 2168 } 2169} 2170#endif 2171 2172#if ENABLE(NETSCAPE_PLUGIN_API) 2173- (void)_resumeNullEventsForAllNetscapePlugins 2174{ 2175 NSArray *subviews = [self subviews]; 2176 unsigned int subviewCount = [subviews count]; 2177 unsigned int subviewIndex; 2178 2179 for (subviewIndex = 0; subviewIndex < subviewCount; subviewIndex++) { 2180 NSView *subview = [subviews objectAtIndex:subviewIndex]; 2181 if ([subview isKindOfClass:[WebBaseNetscapePluginView class]]) 2182 [(WebBaseNetscapePluginView *)subview restartTimers]; 2183 } 2184} 2185#endif 2186 2187- (BOOL)_isUsingAcceleratedCompositing 2188{ 2189#if USE(ACCELERATED_COMPOSITING) 2190 return _private->layerHostingView != nil; 2191#else 2192 return NO; 2193#endif 2194} 2195 2196- (NSView *)_compositingLayersHostingView 2197{ 2198#if USE(ACCELERATED_COMPOSITING) 2199 return _private->layerHostingView; 2200#else 2201 return 0; 2202#endif 2203} 2204 2205- (BOOL)_isInPrintMode 2206{ 2207 return _private->printing; 2208} 2209 2210- (BOOL)_beginPrintModeWithMinimumPageWidth:(CGFloat)minimumPageWidth height:(CGFloat)minimumPageHeight maximumPageWidth:(CGFloat)maximumPageWidth 2211{ 2212 Frame* frame = core([self _frame]); 2213 if (!frame) 2214 return NO; 2215 2216 if (frame->document() && frame->document()->isFrameSet()) { 2217 minimumPageWidth = 0; 2218 minimumPageHeight = 0; 2219 maximumPageWidth = 0; 2220 } 2221 2222 [self _setPrinting:YES minimumPageLogicalWidth:minimumPageWidth logicalHeight:minimumPageHeight maximumPageLogicalWidth:maximumPageWidth adjustViewSize:YES paginateScreenContent:[self _isInScreenPaginationMode]]; 2223 return YES; 2224} 2225 2226- (BOOL)_beginPrintModeWithPageWidth:(float)pageWidth height:(float)pageHeight shrinkToFit:(BOOL)shrinkToFit 2227{ 2228 Frame* frame = core([self _frame]); 2229 if (!frame) 2230 return NO; 2231 2232 Document* document = frame->document(); 2233 bool isHorizontal = !document || !document->renderView() || document->renderView()->style()->isHorizontalWritingMode(); 2234 2235 float minLayoutLogicalWidth = isHorizontal ? pageWidth : pageHeight; 2236 float minLayoutLogicalHeight = isHorizontal ? pageHeight : pageWidth; 2237 float maxLayoutLogicalWidth = minLayoutLogicalWidth; 2238 2239 // If we are a frameset just print with the layout we have onscreen, otherwise relayout 2240 // according to the page width. 2241 if (shrinkToFit && (!frame->document() || !frame->document()->isFrameSet())) { 2242 minLayoutLogicalWidth *= _WebHTMLViewPrintingMinimumShrinkFactor; 2243 minLayoutLogicalHeight *= _WebHTMLViewPrintingMinimumShrinkFactor; 2244 maxLayoutLogicalWidth *= _WebHTMLViewPrintingMaximumShrinkFactor; 2245 } 2246 [self _setPrinting:YES minimumPageLogicalWidth:minLayoutLogicalWidth logicalHeight:minLayoutLogicalHeight maximumPageLogicalWidth:maxLayoutLogicalWidth adjustViewSize:YES paginateScreenContent:[self _isInScreenPaginationMode]]; 2247 2248 return YES; 2249} 2250 2251- (void)_endPrintMode 2252{ 2253 [self _setPrinting:NO minimumPageLogicalWidth:0 logicalHeight:0 maximumPageLogicalWidth:0 adjustViewSize:YES paginateScreenContent:[self _isInScreenPaginationMode]]; 2254} 2255 2256- (BOOL)_isInScreenPaginationMode 2257{ 2258 return _private->paginateScreenContent; 2259} 2260 2261- (BOOL)_beginScreenPaginationModeWithPageSize:(CGSize)pageSize shrinkToFit:(BOOL)shrinkToFit 2262{ 2263 Frame* frame = core([self _frame]); 2264 if (!frame) 2265 return NO; 2266 2267 Document* document = frame->document(); 2268 bool isHorizontal = !document || !document->renderView() || document->renderView()->style()->isHorizontalWritingMode(); 2269 2270 float minLayoutLogicalWidth = isHorizontal ? pageSize.width : pageSize.height; 2271 float minLayoutLogicalHeight = isHorizontal ? pageSize.height : pageSize.width; 2272 float maxLayoutLogicalWidth = minLayoutLogicalWidth; 2273 2274 // If we are a frameset just print with the layout we have onscreen, otherwise relayout 2275 // according to the page width. 2276 if (shrinkToFit && (!frame->document() || !frame->document()->isFrameSet())) { 2277 minLayoutLogicalWidth *= _WebHTMLViewPrintingMinimumShrinkFactor; 2278 minLayoutLogicalHeight *= _WebHTMLViewPrintingMinimumShrinkFactor; 2279 maxLayoutLogicalWidth *= _WebHTMLViewPrintingMaximumShrinkFactor; 2280 } 2281 [self _setPrinting:[self _isInPrintMode] minimumPageLogicalWidth:minLayoutLogicalWidth logicalHeight:minLayoutLogicalHeight maximumPageLogicalWidth:maxLayoutLogicalWidth adjustViewSize:YES paginateScreenContent:YES]; 2282 2283 return YES; 2284} 2285 2286- (void)_endScreenPaginationMode 2287{ 2288 [self _setPrinting:[self _isInPrintMode] minimumPageLogicalWidth:0 logicalHeight:0 maximumPageLogicalWidth:0 adjustViewSize:YES paginateScreenContent:NO]; 2289} 2290 2291- (CGFloat)_adjustedBottomOfPageWithTop:(CGFloat)top bottom:(CGFloat)bottom limit:(CGFloat)bottomLimit 2292{ 2293 Frame* frame = core([self _frame]); 2294 if (!frame) 2295 return bottom; 2296 2297 FrameView* view = frame->view(); 2298 if (!view) 2299 return bottom; 2300 2301 float newBottom; 2302 view->adjustPageHeightDeprecated(&newBottom, top, bottom, bottomLimit); 2303 2304#ifdef __LP64__ 2305 // If the new bottom is equal to the old bottom (when both are treated as floats), we just return the original 2306 // bottom. This prevents rounding errors that can occur when converting newBottom to a double. 2307 if (fabs(static_cast<float>(bottom) - newBottom) <= numeric_limits<float>::epsilon()) 2308 return bottom; 2309 else 2310#endif 2311 return newBottom; 2312} 2313 2314@end 2315 2316@implementation NSView (WebHTMLViewFileInternal) 2317 2318- (void)_web_addDescendantWebHTMLViewsToArray:(NSMutableArray *)array 2319{ 2320 unsigned count = [_subviews count]; 2321 for (unsigned i = 0; i < count; ++i) { 2322 NSView *child = [_subviews objectAtIndex:i]; 2323 if ([child isKindOfClass:[WebHTMLView class]]) 2324 [array addObject:child]; 2325 [child _web_addDescendantWebHTMLViewsToArray:array]; 2326 } 2327} 2328 2329@end 2330 2331@implementation NSMutableDictionary (WebHTMLViewFileInternal) 2332 2333- (void)_web_setObjectIfNotNil:(id)object forKey:(id)key 2334{ 2335 if (object == nil) { 2336 [self removeObjectForKey:key]; 2337 } else { 2338 [self setObject:object forKey:key]; 2339 } 2340} 2341 2342@end 2343 2344static bool matchesExtensionOrEquivalent(NSString *filename, NSString *extension) 2345{ 2346 NSString *extensionAsSuffix = [@"." stringByAppendingString:extension]; 2347 return [filename _webkit_hasCaseInsensitiveSuffix:extensionAsSuffix] 2348 || ([extension _webkit_isCaseInsensitiveEqualToString:@"jpeg"] 2349 && [filename _webkit_hasCaseInsensitiveSuffix:@".jpg"]); 2350} 2351 2352#ifdef BUILDING_ON_TIGER 2353 2354// The following is a workaround for 2355// <rdar://problem/3429631> window stops getting mouse moved events after first tooltip appears 2356// The trick is to define a category on NSToolTipPanel that implements setAcceptsMouseMovedEvents:. 2357// Since the category will be searched before the real class, we'll prevent the flag from being 2358// set on the tool tip panel. 2359 2360@interface NSToolTipPanel : NSPanel 2361@end 2362 2363@interface NSToolTipPanel (WebHTMLViewFileInternal) 2364@end 2365 2366@implementation NSToolTipPanel (WebHTMLViewFileInternal) 2367 2368- (void)setAcceptsMouseMovedEvents:(BOOL)flag 2369{ 2370 // Do nothing, preventing the tool tip panel from trying to accept mouse-moved events. 2371} 2372 2373@end 2374 2375#endif 2376 2377@implementation WebHTMLView 2378 2379+ (void)initialize 2380{ 2381 [NSApp registerServicesMenuSendTypes:[[self class] _selectionPasteboardTypes] 2382 returnTypes:[[self class] _insertablePasteboardTypes]]; 2383 JSC::initializeThreading(); 2384 WTF::initializeMainThreadToProcessMainThread(); 2385#ifndef BUILDING_ON_TIGER 2386 WebCoreObjCFinalizeOnMainThread(self); 2387#endif 2388} 2389 2390- (id)initWithFrame:(NSRect)frame 2391{ 2392 self = [super initWithFrame:frame]; 2393 if (!self) 2394 return nil; 2395 2396 [self setFocusRingType:NSFocusRingTypeNone]; 2397 2398 // Make all drawing go through us instead of subviews. 2399 [self _setDrawsOwnDescendants:YES]; 2400 2401 _private = [[WebHTMLViewPrivate alloc] init]; 2402 2403 _private->pluginController = [[WebPluginController alloc] initWithDocumentView:self]; 2404 2405 return self; 2406} 2407 2408- (void)dealloc 2409{ 2410 if (WebCoreObjCScheduleDeallocateOnMainThread([WebHTMLView class], self)) 2411 return; 2412 2413 // We can't assert that close has already been called because 2414 // this view can be removed from it's superview, even though 2415 // it could be needed later, so close if needed. 2416 [self close]; 2417 [_private release]; 2418 _private = nil; 2419 [super dealloc]; 2420} 2421 2422- (void)finalize 2423{ 2424 ASSERT_MAIN_THREAD(); 2425 // We can't assert that close has already been called because 2426 // this view can be removed from it's superview, even though 2427 // it could be needed later, so close if needed. 2428 [self close]; 2429 [super finalize]; 2430} 2431 2432// Returns YES if the delegate returns YES (so we should do no more work). 2433- (BOOL)callDelegateDoCommandBySelectorIfNeeded:(SEL)selector 2434{ 2435 BOOL callerAlreadyCalledDelegate = _private->selectorForDoCommandBySelector == selector; 2436 _private->selectorForDoCommandBySelector = 0; 2437 if (callerAlreadyCalledDelegate) 2438 return NO; 2439 WebView *webView = [self _webView]; 2440 return [[webView _editingDelegateForwarder] webView:webView doCommandBySelector:selector]; 2441} 2442 2443typedef HashMap<SEL, String> SelectorNameMap; 2444 2445// Map selectors into Editor command names. 2446// This is not needed for any selectors that have the same name as the Editor command. 2447static const SelectorNameMap* createSelectorExceptionMap() 2448{ 2449 SelectorNameMap* map = new HashMap<SEL, String>; 2450 2451 map->add(@selector(insertNewlineIgnoringFieldEditor:), "InsertNewline"); 2452 map->add(@selector(insertParagraphSeparator:), "InsertNewline"); 2453 map->add(@selector(insertTabIgnoringFieldEditor:), "InsertTab"); 2454 map->add(@selector(pageDown:), "MovePageDown"); 2455 map->add(@selector(pageDownAndModifySelection:), "MovePageDownAndModifySelection"); 2456 map->add(@selector(pageUp:), "MovePageUp"); 2457 map->add(@selector(pageUpAndModifySelection:), "MovePageUpAndModifySelection"); 2458 2459 return map; 2460} 2461 2462static String commandNameForSelector(SEL selector) 2463{ 2464 // Check the exception map first. 2465 static const SelectorNameMap* exceptionMap = createSelectorExceptionMap(); 2466 SelectorNameMap::const_iterator it = exceptionMap->find(selector); 2467 if (it != exceptionMap->end()) 2468 return it->second; 2469 2470 // Remove the trailing colon. 2471 // No need to capitalize the command name since Editor command names are 2472 // not case sensitive. 2473 const char* selectorName = sel_getName(selector); 2474 size_t selectorNameLength = strlen(selectorName); 2475 if (selectorNameLength < 2 || selectorName[selectorNameLength - 1] != ':') 2476 return String(); 2477 return String(selectorName, selectorNameLength - 1); 2478} 2479 2480- (Editor::Command)coreCommandBySelector:(SEL)selector 2481{ 2482 Frame* coreFrame = core([self _frame]); 2483 if (!coreFrame) 2484 return Editor::Command(); 2485 return coreFrame->editor()->command(commandNameForSelector(selector)); 2486} 2487 2488- (Editor::Command)coreCommandByName:(const char*)name 2489{ 2490 Frame* coreFrame = core([self _frame]); 2491 if (!coreFrame) 2492 return Editor::Command(); 2493 return coreFrame->editor()->command(name); 2494} 2495 2496- (void)executeCoreCommandBySelector:(SEL)selector 2497{ 2498 if ([self callDelegateDoCommandBySelectorIfNeeded:selector]) 2499 return; 2500 [self coreCommandBySelector:selector].execute(); 2501} 2502 2503- (void)executeCoreCommandByName:(const char*)name 2504{ 2505 [self coreCommandByName:name].execute(); 2506} 2507 2508// These commands are forwarded to the Editor object in WebCore. 2509// Ideally we'd do this for all editing commands; more of the code 2510// should be moved from here to there, and more commands should be 2511// added to this list. 2512 2513// FIXME: Maybe we should set things up so that all these share a single method implementation function. 2514// The functions are identical. 2515 2516#define WEBCORE_COMMAND(command) - (void)command:(id)sender { [self executeCoreCommandBySelector:_cmd]; } 2517 2518WEBCORE_COMMAND(alignCenter) 2519WEBCORE_COMMAND(alignJustified) 2520WEBCORE_COMMAND(alignLeft) 2521WEBCORE_COMMAND(alignRight) 2522WEBCORE_COMMAND(copy) 2523WEBCORE_COMMAND(cut) 2524WEBCORE_COMMAND(paste) 2525WEBCORE_COMMAND(delete) 2526WEBCORE_COMMAND(deleteBackward) 2527WEBCORE_COMMAND(deleteBackwardByDecomposingPreviousCharacter) 2528WEBCORE_COMMAND(deleteForward) 2529WEBCORE_COMMAND(deleteToBeginningOfLine) 2530WEBCORE_COMMAND(deleteToBeginningOfParagraph) 2531WEBCORE_COMMAND(deleteToEndOfLine) 2532WEBCORE_COMMAND(deleteToEndOfParagraph) 2533WEBCORE_COMMAND(deleteToMark) 2534WEBCORE_COMMAND(deleteWordBackward) 2535WEBCORE_COMMAND(deleteWordForward) 2536WEBCORE_COMMAND(ignoreSpelling) 2537WEBCORE_COMMAND(indent) 2538WEBCORE_COMMAND(insertBacktab) 2539WEBCORE_COMMAND(insertLineBreak) 2540WEBCORE_COMMAND(insertNewline) 2541WEBCORE_COMMAND(insertNewlineIgnoringFieldEditor) 2542WEBCORE_COMMAND(insertParagraphSeparator) 2543WEBCORE_COMMAND(insertTab) 2544WEBCORE_COMMAND(insertTabIgnoringFieldEditor) 2545WEBCORE_COMMAND(makeTextWritingDirectionLeftToRight) 2546WEBCORE_COMMAND(makeTextWritingDirectionNatural) 2547WEBCORE_COMMAND(makeTextWritingDirectionRightToLeft) 2548WEBCORE_COMMAND(moveBackward) 2549WEBCORE_COMMAND(moveBackwardAndModifySelection) 2550WEBCORE_COMMAND(moveDown) 2551WEBCORE_COMMAND(moveDownAndModifySelection) 2552WEBCORE_COMMAND(moveForward) 2553WEBCORE_COMMAND(moveForwardAndModifySelection) 2554WEBCORE_COMMAND(moveLeft) 2555WEBCORE_COMMAND(moveLeftAndModifySelection) 2556WEBCORE_COMMAND(moveParagraphBackwardAndModifySelection) 2557WEBCORE_COMMAND(moveParagraphForwardAndModifySelection) 2558WEBCORE_COMMAND(moveRight) 2559WEBCORE_COMMAND(moveRightAndModifySelection) 2560WEBCORE_COMMAND(moveToBeginningOfDocument) 2561WEBCORE_COMMAND(moveToBeginningOfDocumentAndModifySelection) 2562WEBCORE_COMMAND(moveToBeginningOfLine) 2563WEBCORE_COMMAND(moveToBeginningOfLineAndModifySelection) 2564WEBCORE_COMMAND(moveToBeginningOfParagraph) 2565WEBCORE_COMMAND(moveToBeginningOfParagraphAndModifySelection) 2566WEBCORE_COMMAND(moveToBeginningOfSentence) 2567WEBCORE_COMMAND(moveToBeginningOfSentenceAndModifySelection) 2568WEBCORE_COMMAND(moveToEndOfDocument) 2569WEBCORE_COMMAND(moveToEndOfDocumentAndModifySelection) 2570WEBCORE_COMMAND(moveToEndOfLine) 2571WEBCORE_COMMAND(moveToEndOfLineAndModifySelection) 2572WEBCORE_COMMAND(moveToEndOfParagraph) 2573WEBCORE_COMMAND(moveToEndOfParagraphAndModifySelection) 2574WEBCORE_COMMAND(moveToEndOfSentence) 2575WEBCORE_COMMAND(moveToEndOfSentenceAndModifySelection) 2576WEBCORE_COMMAND(moveToLeftEndOfLine) 2577WEBCORE_COMMAND(moveToLeftEndOfLineAndModifySelection) 2578WEBCORE_COMMAND(moveToRightEndOfLine) 2579WEBCORE_COMMAND(moveToRightEndOfLineAndModifySelection) 2580WEBCORE_COMMAND(moveUp) 2581WEBCORE_COMMAND(moveUpAndModifySelection) 2582WEBCORE_COMMAND(moveWordBackward) 2583WEBCORE_COMMAND(moveWordBackwardAndModifySelection) 2584WEBCORE_COMMAND(moveWordForward) 2585WEBCORE_COMMAND(moveWordForwardAndModifySelection) 2586WEBCORE_COMMAND(moveWordLeft) 2587WEBCORE_COMMAND(moveWordLeftAndModifySelection) 2588WEBCORE_COMMAND(moveWordRight) 2589WEBCORE_COMMAND(moveWordRightAndModifySelection) 2590WEBCORE_COMMAND(outdent) 2591WEBCORE_COMMAND(pageDown) 2592WEBCORE_COMMAND(pageDownAndModifySelection) 2593WEBCORE_COMMAND(pageUp) 2594WEBCORE_COMMAND(pageUpAndModifySelection) 2595WEBCORE_COMMAND(pasteAsPlainText) 2596WEBCORE_COMMAND(selectAll) 2597WEBCORE_COMMAND(selectLine) 2598WEBCORE_COMMAND(selectParagraph) 2599WEBCORE_COMMAND(selectSentence) 2600WEBCORE_COMMAND(selectToMark) 2601WEBCORE_COMMAND(selectWord) 2602WEBCORE_COMMAND(setMark) 2603WEBCORE_COMMAND(subscript) 2604WEBCORE_COMMAND(superscript) 2605WEBCORE_COMMAND(swapWithMark) 2606WEBCORE_COMMAND(transpose) 2607WEBCORE_COMMAND(underline) 2608WEBCORE_COMMAND(unscript) 2609WEBCORE_COMMAND(yank) 2610WEBCORE_COMMAND(yankAndSelect) 2611 2612#undef WEBCORE_COMMAND 2613 2614#define COMMAND_PROLOGUE if ([self callDelegateDoCommandBySelectorIfNeeded:_cmd]) return; 2615 2616- (IBAction)takeFindStringFromSelection:(id)sender 2617{ 2618 COMMAND_PROLOGUE 2619 2620 if (![self _hasSelection]) { 2621 NSBeep(); 2622 return; 2623 } 2624 2625 [NSPasteboard _web_setFindPasteboardString:[self selectedString] withOwner:self]; 2626} 2627 2628// This method is needed to support Mac OS X services. 2629- (BOOL)writeSelectionToPasteboard:(NSPasteboard *)pasteboard types:(NSArray *)types 2630{ 2631 [pasteboard declareTypes:types owner:[self _topHTMLView]]; 2632 [self writeSelectionWithPasteboardTypes:types toPasteboard:pasteboard]; 2633 return YES; 2634} 2635 2636- (id)validRequestorForSendType:(NSString *)sendType returnType:(NSString *)returnType 2637{ 2638 BOOL isSendTypeOK = !sendType || ([[self pasteboardTypesForSelection] containsObject:sendType] && [self _hasSelection]); 2639 BOOL isReturnTypeOK = NO; 2640 if (!returnType) 2641 isReturnTypeOK = YES; 2642 else if ([[[self class] _insertablePasteboardTypes] containsObject:returnType] && [self _isEditable]) { 2643 // We can insert strings in any editable context. We can insert other types, like images, only in rich edit contexts. 2644 isReturnTypeOK = [returnType isEqualToString:NSStringPboardType] || [self _canEditRichly]; 2645 } 2646 if (isSendTypeOK && isReturnTypeOK) 2647 return self; 2648 return [[self nextResponder] validRequestorForSendType:sendType returnType:returnType]; 2649} 2650 2651// jumpToSelection is the old name for what AppKit now calls centerSelectionInVisibleArea. Safari 2652// was using the old jumpToSelection selector in its menu. Newer versions of Safari will use the 2653// selector centerSelectionInVisibleArea. We'll leave the old selector in place for two reasons: 2654// (1) Compatibility between older Safari and newer WebKit; (2) other WebKit-based applications 2655// might be using the selector, and we don't want to break them. 2656- (void)jumpToSelection:(id)sender 2657{ 2658 COMMAND_PROLOGUE 2659 2660 if (Frame* coreFrame = core([self _frame])) 2661 coreFrame->selection()->revealSelection(ScrollAlignment::alignCenterAlways); 2662} 2663 2664- (BOOL)validateUserInterfaceItemWithoutDelegate:(id <NSValidatedUserInterfaceItem>)item 2665{ 2666 SEL action = [item action]; 2667 RefPtr<Frame> frame = core([self _frame]); 2668 2669 if (!frame) 2670 return NO; 2671 2672 if (Document* doc = frame->document()) { 2673 if (doc->isPluginDocument()) 2674 return NO; 2675 if (doc->isImageDocument()) { 2676 if (action == @selector(copy:)) 2677 return frame->loader()->isComplete(); 2678 return NO; 2679 } 2680 } 2681 2682 if (action == @selector(changeSpelling:) 2683 || action == @selector(_changeSpellingFromMenu:) 2684 || action == @selector(checkSpelling:) 2685 || action == @selector(complete:) 2686 || action == @selector(pasteFont:)) 2687 return [self _canEdit]; 2688 2689 if (action == @selector(showGuessPanel:)) { 2690#ifndef BUILDING_ON_TIGER 2691 // Match OS X AppKit behavior for post-Tiger. Don't change Tiger behavior. 2692 NSMenuItem *menuItem = (NSMenuItem *)item; 2693 if ([menuItem isKindOfClass:[NSMenuItem class]]) { 2694 BOOL panelShowing = [[[NSSpellChecker sharedSpellChecker] spellingPanel] isVisible]; 2695 [menuItem setTitle:panelShowing 2696 ? UI_STRING_INTERNAL("Hide Spelling and Grammar", "menu item title") 2697 : UI_STRING_INTERNAL("Show Spelling and Grammar", "menu item title")]; 2698 } 2699#endif 2700 return [self _canEdit]; 2701 } 2702 2703 if (action == @selector(changeBaseWritingDirection:) 2704 || action == @selector(makeBaseWritingDirectionLeftToRight:) 2705 || action == @selector(makeBaseWritingDirectionRightToLeft:)) { 2706 NSWritingDirection writingDirection; 2707 2708 if (action == @selector(changeBaseWritingDirection:)) { 2709 writingDirection = static_cast<NSWritingDirection>([item tag]); 2710 if (writingDirection == NSWritingDirectionNatural) 2711 return NO; 2712 } else if (action == @selector(makeBaseWritingDirectionLeftToRight:)) 2713 writingDirection = NSWritingDirectionLeftToRight; 2714 else 2715 writingDirection = NSWritingDirectionRightToLeft; 2716 2717 NSMenuItem *menuItem = (NSMenuItem *)item; 2718 if ([menuItem isKindOfClass:[NSMenuItem class]]) { 2719 String direction = writingDirection == NSWritingDirectionLeftToRight ? "ltr" : "rtl"; 2720 [menuItem setState:frame->editor()->selectionHasStyle(CSSPropertyDirection, direction)]; 2721 } 2722 return [self _canEdit]; 2723 } 2724 2725 if (action == @selector(makeBaseWritingDirectionNatural:)) { 2726 NSMenuItem *menuItem = (NSMenuItem *)item; 2727 if ([menuItem isKindOfClass:[NSMenuItem class]]) 2728 [menuItem setState:NSOffState]; 2729 return NO; 2730 } 2731 2732 if (action == @selector(toggleBaseWritingDirection:)) { 2733 NSMenuItem *menuItem = (NSMenuItem *)item; 2734 if ([menuItem isKindOfClass:[NSMenuItem class]]) { 2735 // Take control of the title of the menu item instead of just checking/unchecking it because 2736 // a check would be ambiguous. 2737 [menuItem setTitle:frame->editor()->selectionHasStyle(CSSPropertyDirection, "rtl") 2738 ? UI_STRING_INTERNAL("Left to Right", "Left to Right context menu item") 2739 : UI_STRING_INTERNAL("Right to Left", "Right to Left context menu item")]; 2740 } 2741 return [self _canEdit]; 2742 } 2743 2744 if (action == @selector(changeAttributes:) 2745 || action == @selector(changeColor:) 2746 || action == @selector(changeFont:)) 2747 return [self _canEditRichly]; 2748 2749 if (action == @selector(capitalizeWord:) 2750 || action == @selector(lowercaseWord:) 2751 || action == @selector(uppercaseWord:)) 2752 return [self _hasSelection] && [self _isEditable]; 2753 2754 if (action == @selector(centerSelectionInVisibleArea:) 2755 || action == @selector(jumpToSelection:) 2756 || action == @selector(copyFont:)) 2757 return [self _hasSelection] || ([self _isEditable] && [self _hasInsertionPoint]); 2758 2759 if (action == @selector(changeDocumentBackgroundColor:)) 2760 return [[self _webView] isEditable] && [self _canEditRichly]; 2761 2762 if (action == @selector(_ignoreSpellingFromMenu:) 2763 || action == @selector(_learnSpellingFromMenu:) 2764 || action == @selector(takeFindStringFromSelection:)) 2765 return [self _hasSelection]; 2766 2767 if (action == @selector(paste:) || action == @selector(pasteAsPlainText:)) 2768 return frame && (frame->editor()->canDHTMLPaste() || frame->editor()->canPaste()); 2769 2770 if (action == @selector(pasteAsRichText:)) 2771 return frame && (frame->editor()->canDHTMLPaste() 2772 || (frame->editor()->canPaste() && frame->selection()->isContentRichlyEditable())); 2773 2774 if (action == @selector(performFindPanelAction:)) 2775 return NO; 2776 2777 if (action == @selector(_lookUpInDictionaryFromMenu:)) 2778 return [self _hasSelection]; 2779 2780 if (action == @selector(stopSpeaking:)) 2781 return [NSApp isSpeaking]; 2782 2783#ifndef BUILDING_ON_TIGER 2784 if (action == @selector(toggleGrammarChecking:)) { 2785 // FIXME 4799134: WebView is the bottleneck for this grammar-checking logic, but we must validate 2786 // the selector here because we implement it here, and we must implement it here because the AppKit 2787 // code checks the first responder. 2788 NSMenuItem *menuItem = (NSMenuItem *)item; 2789 if ([menuItem isKindOfClass:[NSMenuItem class]]) 2790 [menuItem setState:[self isGrammarCheckingEnabled] ? NSOnState : NSOffState]; 2791 return YES; 2792 } 2793#endif 2794 2795#if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) 2796 if (action == @selector(orderFrontSubstitutionsPanel:)) { 2797 NSMenuItem *menuItem = (NSMenuItem *)item; 2798 if ([menuItem isKindOfClass:[NSMenuItem class]]) { 2799 BOOL panelShowing = [[[NSSpellChecker sharedSpellChecker] substitutionsPanel] isVisible]; 2800 [menuItem setTitle:panelShowing 2801 ? UI_STRING_INTERNAL("Hide Substitutions", "menu item title") 2802 : UI_STRING_INTERNAL("Show Substitutions", "menu item title")]; 2803 } 2804 return [self _canEdit]; 2805 } 2806 // FIXME 4799134: WebView is the bottleneck for this logic, but we must validate 2807 // the selector here because we implement it here, and we must implement it here because the AppKit 2808 // code checks the first responder. 2809 if (action == @selector(toggleSmartInsertDelete:)) { 2810 NSMenuItem *menuItem = (NSMenuItem *)item; 2811 if ([menuItem isKindOfClass:[NSMenuItem class]]) 2812 [menuItem setState:[self smartInsertDeleteEnabled] ? NSOnState : NSOffState]; 2813 return [self _canEdit]; 2814 } 2815 if (action == @selector(toggleAutomaticQuoteSubstitution:)) { 2816 NSMenuItem *menuItem = (NSMenuItem *)item; 2817 if ([menuItem isKindOfClass:[NSMenuItem class]]) 2818 [menuItem setState:[self isAutomaticQuoteSubstitutionEnabled] ? NSOnState : NSOffState]; 2819 return [self _canEdit]; 2820 } 2821 if (action == @selector(toggleAutomaticLinkDetection:)) { 2822 NSMenuItem *menuItem = (NSMenuItem *)item; 2823 if ([menuItem isKindOfClass:[NSMenuItem class]]) 2824 [menuItem setState:[self isAutomaticLinkDetectionEnabled] ? NSOnState : NSOffState]; 2825 return [self _canEdit]; 2826 } 2827 if (action == @selector(toggleAutomaticDashSubstitution:)) { 2828 NSMenuItem *menuItem = (NSMenuItem *)item; 2829 if ([menuItem isKindOfClass:[NSMenuItem class]]) 2830 [menuItem setState:[self isAutomaticDashSubstitutionEnabled] ? NSOnState : NSOffState]; 2831 return [self _canEdit]; 2832 } 2833 if (action == @selector(toggleAutomaticTextReplacement:)) { 2834 NSMenuItem *menuItem = (NSMenuItem *)item; 2835 if ([menuItem isKindOfClass:[NSMenuItem class]]) 2836 [menuItem setState:[self isAutomaticTextReplacementEnabled] ? NSOnState : NSOffState]; 2837 return [self _canEdit]; 2838 } 2839 if (action == @selector(toggleAutomaticSpellingCorrection:)) { 2840 NSMenuItem *menuItem = (NSMenuItem *)item; 2841 if ([menuItem isKindOfClass:[NSMenuItem class]]) 2842 [menuItem setState:[self isAutomaticSpellingCorrectionEnabled] ? NSOnState : NSOffState]; 2843 return [self _canEdit]; 2844 } 2845#endif 2846 2847 Editor::Command command = [self coreCommandBySelector:action]; 2848 if (command.isSupported()) { 2849 NSMenuItem *menuItem = (NSMenuItem *)item; 2850 if ([menuItem isKindOfClass:[NSMenuItem class]]) 2851 [menuItem setState:kit(command.state())]; 2852 return command.isEnabled(); 2853 } 2854 2855 return YES; 2856} 2857 2858- (BOOL)validateUserInterfaceItem:(id <NSValidatedUserInterfaceItem>)item 2859{ 2860 // This can be called during teardown when _webView is nil. Return NO when this happens, because CallUIDelegateReturningBoolean 2861 // assumes the WebVIew is non-nil. 2862 if (![self _webView]) 2863 return NO; 2864 BOOL result = [self validateUserInterfaceItemWithoutDelegate:item]; 2865 return CallUIDelegateReturningBoolean(result, [self _webView], @selector(webView:validateUserInterfaceItem:defaultValidation:), item, result); 2866} 2867 2868- (BOOL)acceptsFirstResponder 2869{ 2870 // Don't accept first responder when we first click on this view. 2871 // We have to pass the event down through WebCore first to be sure we don't hit a subview. 2872 // Do accept first responder at any other time, for example from keyboard events, 2873 // or from calls back from WebCore once we begin mouse-down event handling. 2874 NSEvent *event = [NSApp currentEvent]; 2875 if ([event type] == NSLeftMouseDown 2876 && !_private->handlingMouseDownEvent 2877 && NSPointInRect([event locationInWindow], [self convertRect:[self visibleRect] toView:nil])) { 2878 return NO; 2879 } 2880 return YES; 2881} 2882 2883- (BOOL)maintainsInactiveSelection 2884{ 2885 // This method helps to determine whether the WebHTMLView should maintain 2886 // an inactive selection when it's not first responder. 2887 // Traditionally, these views have not maintained such selections, 2888 // clearing them when the view was not first responder. However, 2889 // to fix bugs like this one: 2890 // <rdar://problem/3672088>: "Editable WebViews should maintain a selection even 2891 // when they're not firstResponder" 2892 // it was decided to add a switch to act more like an NSTextView. 2893 2894 if ([[self _webView] maintainsInactiveSelection]) 2895 return YES; 2896 2897 // Predict the case where we are losing first responder status only to 2898 // gain it back again. Want to keep the selection in that case. 2899 id nextResponder = [[self window] _newFirstResponderAfterResigning]; 2900 if ([nextResponder isKindOfClass:[NSScrollView class]]) { 2901 id contentView = [nextResponder contentView]; 2902 if (contentView) 2903 nextResponder = contentView; 2904 } 2905 if ([nextResponder isKindOfClass:[NSClipView class]]) { 2906 id documentView = [nextResponder documentView]; 2907 if (documentView) 2908 nextResponder = documentView; 2909 } 2910 if (nextResponder == self) 2911 return YES; 2912 2913 Frame* coreFrame = core([self _frame]); 2914 bool selectionIsEditable = coreFrame && coreFrame->selection()->isContentEditable(); 2915 bool nextResponderIsInWebView = [nextResponder isKindOfClass:[NSView class]] 2916 && [nextResponder isDescendantOf:[[[self _webView] mainFrame] frameView]]; 2917 2918 return selectionIsEditable && nextResponderIsInWebView; 2919} 2920 2921- (void)addMouseMovedObserver 2922{ 2923 if (!_private->dataSource || ![self _isTopHTMLView] || _private->observingMouseMovedNotifications) 2924 return; 2925 2926 // Unless the Dashboard asks us to do this for all windows, keep an observer going only for the key window. 2927 if (!([[self window] isKeyWindow] 2928#if ENABLE(DASHBOARD_SUPPORT) 2929 || [[self _webView] _dashboardBehavior:WebDashboardBehaviorAlwaysSendMouseEventsToAllWindows] 2930#endif 2931 )) 2932 return; 2933 2934 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(mouseMovedNotification:) 2935 name:WKMouseMovedNotification() object:nil]; 2936 [self _frameOrBoundsChanged]; 2937 _private->observingMouseMovedNotifications = true; 2938} 2939 2940- (void)removeMouseMovedObserver 2941{ 2942#if ENABLE(DASHBOARD_SUPPORT) 2943 // Don't remove the observer if we're running the Dashboard. 2944 if ([[self _webView] _dashboardBehavior:WebDashboardBehaviorAlwaysSendMouseEventsToAllWindows]) 2945 return; 2946#endif 2947 2948 [[self _webView] _mouseDidMoveOverElement:nil modifierFlags:0]; 2949 [self _removeMouseMovedObserverUnconditionally]; 2950} 2951 2952- (void)addSuperviewObservers 2953{ 2954 if (_private->observingSuperviewNotifications) 2955 return; 2956 2957 NSView *superview = [self superview]; 2958 if (!superview || ![self window]) 2959 return; 2960 2961 NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; 2962 [notificationCenter addObserver:self selector:@selector(_frameOrBoundsChanged) name:NSViewFrameDidChangeNotification object:superview]; 2963 [notificationCenter addObserver:self selector:@selector(_frameOrBoundsChanged) name:NSViewBoundsDidChangeNotification object:superview]; 2964 2965 // In addition to registering for frame/bounds change notifications, call -_frameOrBoundsChanged. 2966 // It will check the current scroll against the previous layout's scroll. We need to 2967 // do this here to catch the case where the WebView is laid out at one size, removed from its 2968 // window, resized, and inserted into another window. Our frame/bounds changed notifications 2969 // will not be sent in that situation, since we only watch for changes while in the view hierarchy. 2970 [self _frameOrBoundsChanged]; 2971 2972 _private->observingSuperviewNotifications = true; 2973} 2974 2975- (void)addWindowObservers 2976{ 2977 if (_private->observingWindowNotifications) 2978 return; 2979 2980 NSWindow *window = [self window]; 2981 if (!window) 2982 return; 2983 2984 NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; 2985 [notificationCenter addObserver:self selector:@selector(windowDidBecomeKey:) name:NSWindowDidBecomeKeyNotification object:nil]; 2986 [notificationCenter addObserver:self selector:@selector(windowDidResignKey:) name:NSWindowDidResignKeyNotification object:nil]; 2987 [notificationCenter addObserver:self selector:@selector(windowWillClose:) name:NSWindowWillCloseNotification object:window]; 2988 2989 _private->observingWindowNotifications = true; 2990} 2991 2992- (void)viewWillMoveToSuperview:(NSView *)newSuperview 2993{ 2994 [self _removeSuperviewObservers]; 2995} 2996 2997- (void)viewDidMoveToSuperview 2998{ 2999 if ([self superview] != nil) 3000 [self addSuperviewObservers]; 3001 3002#if USE(ACCELERATED_COMPOSITING) 3003 if ([self superview] && [self _isUsingAcceleratedCompositing]) { 3004 WebView *webView = [self _webView]; 3005 if ([webView _postsAcceleratedCompositingNotifications]) 3006 [[NSNotificationCenter defaultCenter] postNotificationName:_WebViewDidStartAcceleratedCompositingNotification object:webView userInfo:nil]; 3007 } 3008#endif 3009} 3010 3011- (void)viewWillMoveToWindow:(NSWindow *)window 3012{ 3013 // Don't do anything if we aren't initialized. This happens 3014 // when decoding a WebView. When WebViews are decoded their subviews 3015 // are created by initWithCoder: and so won't be normally 3016 // initialized. The stub views are discarded by WebView. 3017 if (!_private) 3018 return; 3019 3020 // FIXME: Some of these calls may not work because this view may be already removed from it's superview. 3021 [self _removeMouseMovedObserverUnconditionally]; 3022 [self _removeWindowObservers]; 3023 [self _removeSuperviewObservers]; 3024 [self _cancelUpdateMouseoverTimer]; 3025 3026 // FIXME: This accomplishes the same thing as the call to setCanStartMedia(false) in 3027 // WebView. It would be nice to have a single mechanism instead of two. 3028 [[self _pluginController] stopAllPlugins]; 3029} 3030 3031- (void)viewDidMoveToWindow 3032{ 3033 // Don't do anything if we aren't initialized. This happens 3034 // when decoding a WebView. When WebViews are decoded their subviews 3035 // are created by initWithCoder: and so won't be normally 3036 // initialized. The stub views are discarded by WebView. 3037 if (!_private || _private->closed) 3038 return; 3039 3040 [self _stopAutoscrollTimer]; 3041 if ([self window]) { 3042 _private->lastScrollPosition = [[self superview] bounds].origin; 3043 [self addWindowObservers]; 3044 [self addSuperviewObservers]; 3045 [self addMouseMovedObserver]; 3046 3047 // FIXME: This accomplishes the same thing as the call to setCanStartMedia(true) in 3048 // WebView. It would be nice to have a single mechanism instead of two. 3049 [[self _pluginController] startAllPlugins]; 3050 3051 _private->lastScrollPosition = NSZeroPoint; 3052 3053#if USE(ACCELERATED_COMPOSITING) && !defined(BUILDING_ON_LEOPARD) 3054 // We may have created the layer hosting view while outside the window. Update the scale factor 3055 // now that we have a window to get it from. 3056 if (_private->layerHostingView) { 3057 CGFloat scaleFactor; 3058#if !defined(BUILDING_ON_SNOW_LEOPARD) 3059 scaleFactor = [[self window] backingScaleFactor]; 3060#else 3061 scaleFactor = [[self window] userSpaceScaleFactor]; 3062#endif 3063 [[_private->layerHostingView layer] setTransform:CATransform3DMakeScale(scaleFactor, scaleFactor, 1)]; 3064 } 3065#endif 3066 } 3067} 3068 3069- (void)_web_makePluginSubviewsPerformSelector:(SEL)selector withObject:(id)object 3070{ 3071#if ENABLE(NETSCAPE_PLUGIN_API) 3072 // Copy subviews because [self subviews] returns the view's mutable internal array, 3073 // and we must avoid mutating the array while enumerating it. 3074 NSArray *subviews = [[self subviews] copy]; 3075 3076 NSEnumerator *enumerator = [subviews objectEnumerator]; 3077 WebNetscapePluginView *view; 3078 while ((view = [enumerator nextObject]) != nil) 3079 if ([view isKindOfClass:[WebBaseNetscapePluginView class]]) 3080 [view performSelector:selector withObject:object]; 3081 3082 [subviews release]; 3083#endif 3084} 3085 3086- (void)viewWillMoveToHostWindow:(NSWindow *)hostWindow 3087{ 3088 [self _web_makePluginSubviewsPerformSelector:@selector(viewWillMoveToHostWindow:) withObject:hostWindow]; 3089} 3090 3091- (void)viewDidMoveToHostWindow 3092{ 3093 [self _web_makePluginSubviewsPerformSelector:@selector(viewDidMoveToHostWindow) withObject:nil]; 3094} 3095 3096 3097- (void)addSubview:(NSView *)view 3098{ 3099 [super addSubview:view]; 3100 3101 if ([WebPluginController isPlugInView:view]) 3102 [[self _pluginController] addPlugin:view]; 3103} 3104 3105- (void)willRemoveSubview:(NSView *)subview 3106{ 3107#ifndef NDEBUG 3108 // Have to null-check _private, since this can be called via -dealloc when 3109 // cleaning up the the layerHostingView. 3110 if (_private && _private->enumeratingSubviews) 3111 LOG(View, "A view of class %s was removed during subview enumeration for layout or printing mode change. We will still do layout or the printing mode change even though this view is no longer in the view hierarchy.", object_getClassName([subview class])); 3112#endif 3113 3114 if ([WebPluginController isPlugInView:subview]) 3115 [[self _pluginController] destroyPlugin:subview]; 3116 3117 [super willRemoveSubview:subview]; 3118} 3119 3120- (void)reapplyStyles 3121{ 3122#ifdef LOG_TIMES 3123 double start = CFAbsoluteTimeGetCurrent(); 3124#endif 3125 3126 if (Frame* coreFrame = core([self _frame])) 3127 coreFrame->document()->styleSelectorChanged(RecalcStyleImmediately); 3128 3129#ifdef LOG_TIMES 3130 double thisTime = CFAbsoluteTimeGetCurrent() - start; 3131 LOG(Timing, "%s apply style seconds = %f", [self URL], thisTime); 3132#endif 3133} 3134 3135// Do a layout, but set up a new fixed width for the purposes of doing printing layout. 3136// minPageWidth==0 implies a non-printing layout 3137- (void)layoutToMinimumPageWidth:(float)minPageLogicalWidth height:(float)minPageLogicalHeight maximumPageWidth:(float)maxPageLogicalWidth adjustingViewSize:(BOOL)adjustViewSize 3138{ 3139 if (![self _needsLayout]) 3140 return; 3141 3142#ifdef LOG_TIMES 3143 double start = CFAbsoluteTimeGetCurrent(); 3144#endif 3145 3146 LOG(View, "%@ doing layout", self); 3147 3148 Frame* coreFrame = core([self _frame]); 3149 if (!coreFrame) 3150 return; 3151 3152 if (FrameView* coreView = coreFrame->view()) { 3153 if (minPageLogicalWidth > 0.0) { 3154 FloatSize pageSize(minPageLogicalWidth, minPageLogicalHeight); 3155 if (coreFrame->document() && coreFrame->document()->renderView() && !coreFrame->document()->renderView()->style()->isHorizontalWritingMode()) 3156 pageSize = FloatSize(minPageLogicalHeight, minPageLogicalWidth); 3157 coreView->forceLayoutForPagination(pageSize, maxPageLogicalWidth / minPageLogicalWidth, adjustViewSize ? Frame::AdjustViewSize : Frame::DoNotAdjustViewSize); 3158 } else { 3159 coreView->forceLayout(!adjustViewSize); 3160 if (adjustViewSize) 3161 coreView->adjustViewSize(); 3162 } 3163 } 3164 3165#ifdef LOG_TIMES 3166 double thisTime = CFAbsoluteTimeGetCurrent() - start; 3167 LOG(Timing, "%s layout seconds = %f", [self URL], thisTime); 3168#endif 3169} 3170 3171- (void)layout 3172{ 3173 [self layoutToMinimumPageWidth:0 height:0 maximumPageWidth:0 adjustingViewSize:NO]; 3174} 3175 3176// Deliver mouseup events to the DOM for button 2. 3177- (void)rightMouseUp:(NSEvent *)event 3178{ 3179 // There's a chance that if we run a nested event loop the event will be released. 3180 // Retaining and then autoreleasing prevents that from causing a problem later here or 3181 // inside AppKit code. 3182 [[event retain] autorelease]; 3183 3184 [super rightMouseUp:event]; 3185 3186 if (Frame* coreframe = core([self _frame])) 3187 coreframe->eventHandler()->mouseUp(event); 3188} 3189 3190static void setMenuItemTarget(NSMenuItem* menuItem) 3191{ 3192 // Don't set the menu item's action to the context menu action forwarder if we already 3193 // have an action. 3194 if ([menuItem action]) 3195 return; 3196 3197 [menuItem setTarget:[WebMenuTarget sharedMenuTarget]]; 3198 [menuItem setAction:@selector(forwardContextMenuAction:)]; 3199} 3200 3201static void setMenuTargets(NSMenu* menu) 3202{ 3203 NSInteger itemCount = [menu numberOfItems]; 3204 for (NSInteger i = 0; i < itemCount; ++i) { 3205 NSMenuItem *item = [menu itemAtIndex:i]; 3206 setMenuItemTarget(item); 3207 if ([item hasSubmenu]) 3208 setMenuTargets([item submenu]); 3209 } 3210} 3211 3212- (NSMenu *)menuForEvent:(NSEvent *)event 3213{ 3214 // There's a chance that if we run a nested event loop the event will be released. 3215 // Retaining and then autoreleasing prevents that from causing a problem later here or 3216 // inside AppKit code. 3217 [[event retain] autorelease]; 3218 3219 [_private->completionController endRevertingChange:NO moveLeft:NO]; 3220 3221 RefPtr<Frame> coreFrame = core([self _frame]); 3222 if (!coreFrame) 3223 return nil; 3224 3225 Page* page = coreFrame->page(); 3226 if (!page) 3227 return nil; 3228 3229 // Match behavior of other browsers by sending a mousedown event for right clicks. 3230 _private->handlingMouseDownEvent = YES; 3231 page->contextMenuController()->clearContextMenu(); 3232 coreFrame->eventHandler()->mouseDown(event); 3233 BOOL handledEvent = coreFrame->eventHandler()->sendContextMenuEvent(event); 3234 _private->handlingMouseDownEvent = NO; 3235 3236 if (!handledEvent) 3237 return nil; 3238 3239 // Re-get page, since it might have gone away during event handling. 3240 page = coreFrame->page(); 3241 if (!page) 3242 return nil; 3243 3244 ContextMenu* coreMenu = page->contextMenuController()->contextMenu(); 3245 if (!coreMenu) 3246 return nil; 3247 3248 NSArray* menuItems = coreMenu->platformDescription(); 3249 if (!menuItems) 3250 return nil; 3251 3252 NSUInteger count = [menuItems count]; 3253 if (!count) 3254 return nil; 3255 3256 NSMenu* menu = [[[NSMenu alloc] init] autorelease]; 3257 for (NSUInteger i = 0; i < count; i++) 3258 [menu addItem:[menuItems objectAtIndex:i]]; 3259 setMenuTargets(menu); 3260 3261 [[WebMenuTarget sharedMenuTarget] setMenuController:page->contextMenuController()]; 3262 3263 return menu; 3264} 3265 3266- (BOOL)searchFor:(NSString *)string direction:(BOOL)forward caseSensitive:(BOOL)caseFlag wrap:(BOOL)wrapFlag 3267{ 3268 return [self searchFor:string direction:forward caseSensitive:caseFlag wrap:wrapFlag startInSelection:NO]; 3269} 3270 3271- (void)clearFocus 3272{ 3273 Frame* coreFrame = core([self _frame]); 3274 if (!coreFrame) 3275 return; 3276 Document* document = coreFrame->document(); 3277 if (!document) 3278 return; 3279 3280 document->setFocusedNode(0); 3281} 3282 3283- (BOOL)isOpaque 3284{ 3285 return [[self _webView] drawsBackground]; 3286} 3287 3288#if !LOG_DISABLED 3289- (void)setNeedsDisplay:(BOOL)flag 3290{ 3291 LOG(View, "%@ setNeedsDisplay:%@", self, flag ? @"YES" : @"NO"); 3292 [super setNeedsDisplay:flag]; 3293} 3294#endif 3295 3296#ifndef BUILDING_ON_TIGER 3297- (void)setNeedsDisplayInRect:(NSRect)invalidRect 3298{ 3299 if (_private->inScrollPositionChanged) { 3300 // When scrolling, the dirty regions are adjusted for the scroll only 3301 // after NSViewBoundsDidChangeNotification is sent. Translate the invalid 3302 // rect to pre-scrolled coordinates in order to get the right dirty region 3303 // after adjustment. See <rdar://problem/7678927>. 3304 NSPoint origin = [[self superview] bounds].origin; 3305 invalidRect.origin.x -= _private->lastScrollPosition.x - origin.x; 3306 invalidRect.origin.y -= _private->lastScrollPosition.y - origin.y; 3307 } 3308 [super setNeedsDisplayInRect:invalidRect]; 3309} 3310#endif 3311 3312- (void)setNeedsLayout: (BOOL)flag 3313{ 3314 LOG(View, "%@ setNeedsLayout:%@", self, flag ? @"YES" : @"NO"); 3315 if (!flag) 3316 return; // There's no way to say you don't need a layout. 3317 if (Frame* frame = core([self _frame])) { 3318 if (frame->document() && frame->document()->inPageCache()) 3319 return; 3320 if (FrameView* view = frame->view()) 3321 view->setNeedsLayout(); 3322 } 3323} 3324 3325- (void)setNeedsToApplyStyles: (BOOL)flag 3326{ 3327 LOG(View, "%@ setNeedsToApplyStyles:%@", self, flag ? @"YES" : @"NO"); 3328 if (!flag) 3329 return; // There's no way to say you don't need a style recalc. 3330 if (Frame* frame = core([self _frame])) { 3331 if (frame->document() && frame->document()->inPageCache()) 3332 return; 3333 frame->document()->scheduleForcedStyleRecalc(); 3334 } 3335} 3336 3337- (void)drawSingleRect:(NSRect)rect 3338{ 3339 [NSGraphicsContext saveGraphicsState]; 3340 NSRectClip(rect); 3341 3342 ASSERT([[self superview] isKindOfClass:[WebClipView class]]); 3343 3344 [(WebClipView *)[self superview] setAdditionalClip:rect]; 3345 3346 @try { 3347 if ([self _transparentBackground]) { 3348 [[NSColor clearColor] set]; 3349 NSRectFill (rect); 3350 } 3351 3352 [[self _frame] _drawRect:rect contentsOnly:YES]; 3353 3354 WebView *webView = [self _webView]; 3355 3356 // This hack is needed for <rdar://problem/5023545>. We can hit a race condition where drawRect will be 3357 // called after the WebView has closed. If the client did not properly close the WebView and set the 3358 // UIDelegate to nil, then the UIDelegate will be stale and this code will crash. 3359 static BOOL version3OrLaterClient = WebKitLinkedOnOrAfter(WEBKIT_FIRST_VERSION_WITHOUT_QUICKBOOKS_QUIRK); 3360 if (version3OrLaterClient) 3361 [[webView _UIDelegateForwarder] webView:webView didDrawRect:[webView convertRect:rect fromView:self]]; 3362 3363 if (WebNodeHighlight *currentHighlight = [webView currentNodeHighlight]) 3364 [currentHighlight setNeedsUpdateInTargetViewRect:[self convertRect:rect toView:[currentHighlight targetView]]]; 3365 3366 [(WebClipView *)[self superview] resetAdditionalClip]; 3367 3368 [NSGraphicsContext restoreGraphicsState]; 3369 } @catch (NSException *localException) { 3370 [(WebClipView *)[self superview] resetAdditionalClip]; 3371 [NSGraphicsContext restoreGraphicsState]; 3372 LOG_ERROR("Exception caught while drawing: %@", localException); 3373 [localException raise]; 3374 } 3375} 3376 3377- (void)drawRect:(NSRect)rect 3378{ 3379 ASSERT_MAIN_THREAD(); 3380 LOG(View, "%@ drawing", self); 3381 3382 const NSRect *rects; 3383 NSInteger count; 3384 [self getRectsBeingDrawn:&rects count:&count]; 3385 3386 BOOL subviewsWereSetAside = _private->subviewsSetAside; 3387 if (subviewsWereSetAside) 3388 [self _restoreSubviews]; 3389 3390#ifdef LOG_TIMES 3391 double start = CFAbsoluteTimeGetCurrent(); 3392#endif 3393 3394 WebView *webView = [self _webView]; 3395 if ([webView _mustDrawUnionedRect:rect singleRects:rects count:count]) 3396 [self drawSingleRect:rect]; 3397 else 3398 for (int i = 0; i < count; ++i) 3399 [self drawSingleRect:rects[i]]; 3400 3401#ifdef LOG_TIMES 3402 double thisTime = CFAbsoluteTimeGetCurrent() - start; 3403 LOG(Timing, "%s draw seconds = %f", widget->part()->baseURL().URL().latin1(), thisTime); 3404#endif 3405 3406 if (subviewsWereSetAside) 3407 [self _setAsideSubviews]; 3408 3409#if USE(ACCELERATED_COMPOSITING) 3410 // Only do the synchronization dance if we're drawing into the window, otherwise 3411 // we risk disabling screen updates when no flush is pending. 3412 if ([NSGraphicsContext currentContext] == [[self window] graphicsContext] && [webView _needsOneShotDrawingSynchronization]) { 3413 // Disable screen updates to minimize the chances of the race between the CA 3414 // display link and AppKit drawing causing flashes. 3415 [[self window] disableScreenUpdatesUntilFlush]; 3416 3417 // Make sure any layer changes that happened as a result of layout 3418 // via -viewWillDraw are committed. 3419 [CATransaction flush]; 3420 [webView _setNeedsOneShotDrawingSynchronization:NO]; 3421 } 3422#endif 3423 3424 if (webView) 3425 CallUIDelegate(webView, @selector(webView:didDrawFrame:), [self _frame]); 3426} 3427 3428// Turn off the additional clip while computing our visibleRect. 3429- (NSRect)visibleRect 3430{ 3431 if (!([[self superview] isKindOfClass:[WebClipView class]])) 3432 return [super visibleRect]; 3433 3434 WebClipView *clipView = (WebClipView *)[self superview]; 3435 3436 BOOL hasAdditionalClip = [clipView hasAdditionalClip]; 3437 if (!hasAdditionalClip) { 3438 return [super visibleRect]; 3439 } 3440 3441 NSRect additionalClip = [clipView additionalClip]; 3442 [clipView resetAdditionalClip]; 3443 NSRect visibleRect = [super visibleRect]; 3444 [clipView setAdditionalClip:additionalClip]; 3445 return visibleRect; 3446} 3447 3448- (void)_invalidateGStatesForTree 3449{ 3450 // AppKit is in the process of traversing the NSView tree, and is going to send -renewGState to 3451 // descendants, including plug-in views. This can result in calls out to plug-in code and back into 3452 // WebCore via JavaScript, which could normally mutate the NSView tree while it is being traversed. 3453 // Defer those mutations while descendants are being traveresed. 3454 RenderWidget::suspendWidgetHierarchyUpdates(); 3455 [super _invalidateGStatesForTree]; 3456 RenderWidget::resumeWidgetHierarchyUpdates(); 3457} 3458 3459- (BOOL)isFlipped 3460{ 3461 return YES; 3462} 3463 3464- (void)windowDidBecomeKey:(NSNotification *)notification 3465{ 3466 if (!pthread_main_np()) { 3467 [self performSelectorOnMainThread:_cmd withObject:notification waitUntilDone:NO]; 3468 return; 3469 } 3470 3471 NSWindow *keyWindow = [notification object]; 3472 3473 if (keyWindow == [self window]) { 3474 [self addMouseMovedObserver]; 3475 [self _updateSecureInputState]; 3476 } 3477} 3478 3479- (void)windowDidResignKey:(NSNotification *)notification 3480{ 3481 if (!pthread_main_np()) { 3482 [self performSelectorOnMainThread:_cmd withObject:notification waitUntilDone:NO]; 3483 return; 3484 } 3485 3486 NSWindow *formerKeyWindow = [notification object]; 3487 3488 if (formerKeyWindow == [self window]) 3489 [self removeMouseMovedObserver]; 3490 3491 if (formerKeyWindow == [self window] || formerKeyWindow == [[self window] attachedSheet]) { 3492 [self _updateSecureInputState]; 3493 [_private->completionController endRevertingChange:NO moveLeft:NO]; 3494 } 3495} 3496 3497- (void)windowWillClose:(NSNotification *)notification 3498{ 3499 if (!pthread_main_np()) { 3500 [self performSelectorOnMainThread:_cmd withObject:notification waitUntilDone:NO]; 3501 return; 3502 } 3503 3504 [_private->completionController endRevertingChange:NO moveLeft:NO]; 3505 [[self _pluginController] destroyAllPlugins]; 3506} 3507 3508- (void)scrollWheel:(NSEvent *)event 3509{ 3510 // There's a chance that responding to this event will run a nested event loop, and 3511 // fetching a new event might release the old one. Retaining and then autoreleasing 3512 // the current event prevents that from causing a problem inside WebKit or AppKit code. 3513 [[event retain] autorelease]; 3514 3515 Frame* frame = core([self _frame]); 3516 if (!frame || !frame->eventHandler()->wheelEvent(event)) 3517 [super scrollWheel:event]; 3518} 3519 3520- (BOOL)_isSelectionEvent:(NSEvent *)event 3521{ 3522 NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil]; 3523 return [[[self elementAtPoint:point allowShadowContent:YES] objectForKey:WebElementIsSelectedKey] boolValue]; 3524} 3525 3526- (BOOL)_isScrollBarEvent:(NSEvent *)event 3527{ 3528 NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil]; 3529 return [[[self elementAtPoint:point allowShadowContent:YES] objectForKey:WebElementIsInScrollBarKey] boolValue]; 3530} 3531 3532- (BOOL)acceptsFirstMouse:(NSEvent *)event 3533{ 3534 // There's a chance that responding to this event will run a nested event loop, and 3535 // fetching a new event might release the old one. Retaining and then autoreleasing 3536 // the current event prevents that from causing a problem inside WebKit or AppKit code. 3537 [[event retain] autorelease]; 3538 3539 NSView *hitView = [self _hitViewForEvent:event]; 3540 WebHTMLView *hitHTMLView = [hitView isKindOfClass:[self class]] ? (WebHTMLView *)hitView : nil; 3541 3542#if ENABLE(DASHBOARD_SUPPORT) 3543 if ([[self _webView] _dashboardBehavior:WebDashboardBehaviorAlwaysAcceptsFirstMouse]) 3544 return YES; 3545#endif 3546 3547 if (hitHTMLView) { 3548 bool result = false; 3549 if (Frame* coreFrame = core([hitHTMLView _frame])) { 3550 coreFrame->eventHandler()->setActivationEventNumber([event eventNumber]); 3551 [hitHTMLView _setMouseDownEvent:event]; 3552 if ([hitHTMLView _isSelectionEvent:event]) 3553 result = coreFrame->eventHandler()->eventMayStartDrag(event); 3554 else if ([hitHTMLView _isScrollBarEvent:event]) 3555 result = true; 3556 [hitHTMLView _setMouseDownEvent:nil]; 3557 } 3558 return result; 3559 } 3560 return [hitView acceptsFirstMouse:event]; 3561} 3562 3563- (BOOL)shouldDelayWindowOrderingForEvent:(NSEvent *)event 3564{ 3565 // There's a chance that responding to this event will run a nested event loop, and 3566 // fetching a new event might release the old one. Retaining and then autoreleasing 3567 // the current event prevents that from causing a problem inside WebKit or AppKit code. 3568 [[event retain] autorelease]; 3569 3570 NSView *hitView = [self _hitViewForEvent:event]; 3571 WebHTMLView *hitHTMLView = [hitView isKindOfClass:[self class]] ? (WebHTMLView *)hitView : nil; 3572 if (hitHTMLView) { 3573 bool result = false; 3574 if ([hitHTMLView _isSelectionEvent:event]) { 3575 if (Frame* coreFrame = core([hitHTMLView _frame])) { 3576 [hitHTMLView _setMouseDownEvent:event]; 3577 result = coreFrame->eventHandler()->eventMayStartDrag(event); 3578 [hitHTMLView _setMouseDownEvent:nil]; 3579 } 3580 } 3581 return result; 3582 } 3583 return [hitView shouldDelayWindowOrderingForEvent:event]; 3584} 3585 3586- (void)mouseDown:(NSEvent *)event 3587{ 3588 // There's a chance that responding to this event will run a nested event loop, and 3589 // fetching a new event might release the old one. Retaining and then autoreleasing 3590 // the current event prevents that from causing a problem inside WebKit or AppKit code. 3591 [[event retain] autorelease]; 3592 3593 RetainPtr<WebHTMLView> protector = self; 3594 if ([[self inputContext] wantsToHandleMouseEvents] && [[self inputContext] handleMouseEvent:event]) 3595 return; 3596 3597 _private->handlingMouseDownEvent = YES; 3598 3599 // Record the mouse down position so we can determine drag hysteresis. 3600 [self _setMouseDownEvent:event]; 3601 3602 NSInputManager *currentInputManager = [NSInputManager currentInputManager]; 3603 if ([currentInputManager wantsToHandleMouseEvents] && [currentInputManager handleMouseEvent:event]) 3604 goto done; 3605 3606 [_private->completionController endRevertingChange:NO moveLeft:NO]; 3607 3608 // If the web page handles the context menu event and menuForEvent: returns nil, we'll get control click events here. 3609 // We don't want to pass them along to KHTML a second time. 3610 if (!([event modifierFlags] & NSControlKeyMask)) { 3611 _private->ignoringMouseDraggedEvents = NO; 3612 3613 // Don't do any mouseover while the mouse is down. 3614 [self _cancelUpdateMouseoverTimer]; 3615 3616 // Let WebCore get a chance to deal with the event. This will call back to us 3617 // to start the autoscroll timer if appropriate. 3618 if (Frame* coreframe = core([self _frame])) 3619 coreframe->eventHandler()->mouseDown(event); 3620 } 3621 3622done: 3623 _private->handlingMouseDownEvent = NO; 3624} 3625 3626- (void)dragImage:(NSImage *)dragImage 3627 at:(NSPoint)at 3628 offset:(NSSize)offset 3629 event:(NSEvent *)event 3630 pasteboard:(NSPasteboard *)pasteboard 3631 source:(id)source 3632 slideBack:(BOOL)slideBack 3633{ 3634 ASSERT(self == [self _topHTMLView]); 3635 [super dragImage:dragImage at:at offset:offset event:event pasteboard:pasteboard source:source slideBack:slideBack]; 3636} 3637 3638- (void)mouseDragged:(NSEvent *)event 3639{ 3640 // There's a chance that responding to this event will run a nested event loop, and 3641 // fetching a new event might release the old one. Retaining and then autoreleasing 3642 // the current event prevents that from causing a problem inside WebKit or AppKit code. 3643 [[event retain] autorelease]; 3644 3645 NSInputManager *currentInputManager = [NSInputManager currentInputManager]; 3646 if ([currentInputManager wantsToHandleMouseEvents] && [currentInputManager handleMouseEvent:event]) 3647 return; 3648 3649 [self retain]; 3650 3651 if (!_private->ignoringMouseDraggedEvents) { 3652 if (Frame* frame = core([self _frame])) { 3653 if (Page* page = frame->page()) 3654 page->mainFrame()->eventHandler()->mouseDragged(event); 3655 } 3656 } 3657 3658 [self release]; 3659} 3660 3661- (NSDragOperation)draggingSourceOperationMaskForLocal:(BOOL)isLocal 3662{ 3663 ASSERT(![self _webView] || [self _isTopHTMLView]); 3664 3665 Page* page = core([self _webView]); 3666 if (!page) 3667 return NSDragOperationNone; 3668 3669 return (NSDragOperation)page->dragController()->sourceDragOperation(); 3670} 3671 3672- (void)draggedImage:(NSImage *)anImage endedAt:(NSPoint)aPoint operation:(NSDragOperation)operation 3673{ 3674 ASSERT(![self _webView] || [self _isTopHTMLView]); 3675 3676 NSPoint windowImageLoc = [[self window] convertScreenToBase:aPoint]; 3677 NSPoint windowMouseLoc = windowImageLoc; 3678 3679 if (Page* page = core([self _webView])) { 3680 DragController* dragController = page->dragController(); 3681 windowMouseLoc = NSMakePoint(windowImageLoc.x + dragController->dragOffset().x(), windowImageLoc.y + dragController->dragOffset().y()); 3682 dragController->dragEnded(); 3683 } 3684 3685 [[self _frame] _dragSourceEndedAt:windowMouseLoc operation:operation]; 3686 3687 // Prevent queued mouseDragged events from coming after the drag and fake mouseUp event. 3688 _private->ignoringMouseDraggedEvents = YES; 3689 3690 // Once the dragging machinery kicks in, we no longer get mouse drags or the up event. 3691 // WebCore expects to get balanced down/up's, so we must fake up a mouseup. 3692 NSEvent *fakeEvent = [NSEvent mouseEventWithType:NSLeftMouseUp 3693 location:windowMouseLoc 3694 modifierFlags:[[NSApp currentEvent] modifierFlags] 3695 timestamp:[NSDate timeIntervalSinceReferenceDate] 3696 windowNumber:[[self window] windowNumber] 3697 context:[[NSApp currentEvent] context] 3698 eventNumber:0 clickCount:0 pressure:0]; 3699 [self mouseUp:fakeEvent]; // This will also update the mouseover state. 3700} 3701 3702- (NSArray *)namesOfPromisedFilesDroppedAtDestination:(NSURL *)dropDestination 3703{ 3704 NSFileWrapper *wrapper = nil; 3705 NSURL *draggingImageURL = nil; 3706 3707 if (WebCore::CachedImage* tiffResource = [self promisedDragTIFFDataSource]) { 3708 3709 SharedBuffer *buffer = static_cast<CachedResource*>(tiffResource)->data(); 3710 if (!buffer) 3711 goto noPromisedData; 3712 3713 NSData *data = buffer->createNSData(); 3714 NSURLResponse *response = tiffResource->response().nsURLResponse(); 3715 draggingImageURL = [response URL]; 3716 wrapper = [[[NSFileWrapper alloc] initRegularFileWithContents:data] autorelease]; 3717 NSString* filename = [response suggestedFilename]; 3718 NSString* trueExtension(tiffResource->image()->filenameExtension()); 3719 if (!matchesExtensionOrEquivalent(filename, trueExtension)) 3720 filename = [[filename stringByAppendingString:@"."] stringByAppendingString:trueExtension]; 3721 [wrapper setPreferredFilename:filename]; 3722 } 3723 3724noPromisedData: 3725 3726 if (!wrapper) { 3727 ASSERT(![self _webView] || [self _isTopHTMLView]); 3728 Page* page = core([self _webView]); 3729 3730 //If a load occurs midway through a drag, the view may be detached, which gives 3731 //us no ability to get to the original Page, so we cannot access any drag state 3732 //FIXME: is there a way to recover? 3733 if (!page) 3734 return nil; 3735 3736 const KURL& imageURL = page->dragController()->draggingImageURL(); 3737 ASSERT(!imageURL.isEmpty()); 3738 draggingImageURL = imageURL; 3739 3740 wrapper = [[self _dataSource] _fileWrapperForURL:draggingImageURL]; 3741 } 3742 3743 if (wrapper == nil) { 3744 LOG_ERROR("Failed to create image file."); 3745 return nil; 3746 } 3747 3748 // FIXME: Report an error if we fail to create a file. 3749 NSString *path = [[dropDestination path] stringByAppendingPathComponent:[wrapper preferredFilename]]; 3750 path = [[NSFileManager defaultManager] _webkit_pathWithUniqueFilenameForPath:path]; 3751 if (![wrapper writeToFile:path atomically:NO updateFilenames:YES]) 3752 LOG_ERROR("Failed to create image file via -[NSFileWrapper writeToFile:atomically:updateFilenames:]"); 3753 3754 if (draggingImageURL) 3755 [[NSFileManager defaultManager] _webkit_setMetadataURL:[draggingImageURL absoluteString] referrer:nil atPath:path]; 3756 3757 return [NSArray arrayWithObject:[path lastPathComponent]]; 3758} 3759 3760- (void)mouseUp:(NSEvent *)event 3761{ 3762 // There's a chance that responding to this event will run a nested event loop, and 3763 // fetching a new event might release the old one. Retaining and then autoreleasing 3764 // the current event prevents that from causing a problem inside WebKit or AppKit code. 3765 [[event retain] autorelease]; 3766 3767 [self _setMouseDownEvent:nil]; 3768 3769 NSInputManager *currentInputManager = [NSInputManager currentInputManager]; 3770 if ([currentInputManager wantsToHandleMouseEvents] && [currentInputManager handleMouseEvent:event]) 3771 return; 3772 3773 [self retain]; 3774 3775 [self _stopAutoscrollTimer]; 3776 if (Frame* frame = core([self _frame])) { 3777 if (Page* page = frame->page()) 3778 page->mainFrame()->eventHandler()->mouseUp(event); 3779 } 3780 [self _updateMouseoverWithFakeEvent]; 3781 3782 [self release]; 3783} 3784 3785- (void)mouseMovedNotification:(NSNotification *)notification 3786{ 3787 [self _updateMouseoverWithEvent:[[notification userInfo] objectForKey:@"NSEvent"]]; 3788} 3789 3790// returning YES from this method is the way we tell AppKit that it is ok for this view 3791// to be in the key loop even when "tab to all controls" is not on. 3792- (BOOL)needsPanelToBecomeKey 3793{ 3794 return YES; 3795} 3796 3797// Utility function to make sure we don't return anything through the NSTextInput 3798// API when an editable region is not currently focused. 3799static BOOL isTextInput(Frame* coreFrame) 3800{ 3801 return coreFrame && !coreFrame->selection()->isNone() && coreFrame->selection()->isContentEditable(); 3802} 3803 3804static BOOL isInPasswordField(Frame* coreFrame) 3805{ 3806 return coreFrame && coreFrame->selection()->isInPasswordField(); 3807} 3808 3809- (BOOL)becomeFirstResponder 3810{ 3811 NSSelectionDirection direction = NSDirectSelection; 3812 if (![[self _webView] _isPerformingProgrammaticFocus]) 3813 direction = [[self window] keyViewSelectionDirection]; 3814 3815 [self _updateFontPanel]; 3816 3817 Frame* frame = core([self _frame]); 3818 if (!frame) 3819 return YES; 3820 3821 BOOL exposeInputContext = isTextInput(frame) && !isInPasswordField(frame); 3822 if (exposeInputContext != _private->exposeInputContext) { 3823 _private->exposeInputContext = exposeInputContext; 3824 [NSApp updateWindows]; 3825 } 3826 3827 _private->_forceUpdateSecureInputState = YES; 3828 [self _updateSecureInputState]; 3829 _private->_forceUpdateSecureInputState = NO; 3830 3831 frame->editor()->setStartNewKillRingSequence(true); 3832 3833 Page* page = frame->page(); 3834 if (!page) 3835 return YES; 3836 3837 if (![[self _webView] _isPerformingProgrammaticFocus]) 3838 page->focusController()->setFocusedFrame(frame); 3839 3840 page->focusController()->setFocused(true); 3841 3842 if (direction == NSDirectSelection) 3843 return YES; 3844 3845 if (Document* document = frame->document()) 3846 document->setFocusedNode(0); 3847 page->focusController()->setInitialFocus(direction == NSSelectingNext ? FocusDirectionForward : FocusDirectionBackward, 3848 frame->eventHandler()->currentKeyboardEvent().get()); 3849 return YES; 3850} 3851 3852- (BOOL)resignFirstResponder 3853{ 3854 BOOL resign = [super resignFirstResponder]; 3855 if (resign) { 3856 if (_private->isInSecureInputState) { 3857 DisableSecureEventInput(); 3858 _private->isInSecureInputState = NO; 3859 } 3860 [_private->completionController endRevertingChange:NO moveLeft:NO]; 3861 Frame* coreFrame = core([self _frame]); 3862 if (!coreFrame) 3863 return resign; 3864 Page* page = coreFrame->page(); 3865 if (!page) 3866 return resign; 3867 if (![self maintainsInactiveSelection]) { 3868 [self deselectAll]; 3869 if (![[self _webView] _isPerformingProgrammaticFocus]) 3870 [self clearFocus]; 3871 } 3872 3873 id nextResponder = [[self window] _newFirstResponderAfterResigning]; 3874 bool nextResponderIsInWebView = [nextResponder isKindOfClass:[NSView class]] 3875 && [nextResponder isDescendantOf:[[[self _webView] mainFrame] frameView]]; 3876 if (!nextResponderIsInWebView) 3877 page->focusController()->setFocused(false); 3878 } 3879 return resign; 3880} 3881 3882- (void)setDataSource:(WebDataSource *)dataSource 3883{ 3884 ASSERT(dataSource); 3885 if (_private->dataSource != dataSource) { 3886 ASSERT(!_private->closed); 3887 BOOL hadDataSource = _private->dataSource != nil; 3888 3889 [dataSource retain]; 3890 [_private->dataSource release]; 3891 _private->dataSource = dataSource; 3892 [_private->pluginController setDataSource:dataSource]; 3893 3894 if (!hadDataSource) 3895 [self addMouseMovedObserver]; 3896 } 3897} 3898 3899- (void)dataSourceUpdated:(WebDataSource *)dataSource 3900{ 3901} 3902 3903// This is an override of an NSControl method that wants to repaint the entire view when the window resigns/becomes 3904// key. WebHTMLView is an NSControl only because it hosts NSCells that are painted by WebCore's Aqua theme 3905// renderer (and those cells must be hosted by an enclosing NSControl in order to paint properly). 3906- (void)updateCell:(NSCell*)cell 3907{ 3908} 3909 3910// Does setNeedsDisplay:NO as a side effect when printing is ending. 3911// pageWidth != 0 implies we will relayout to a new width 3912- (void)_setPrinting:(BOOL)printing minimumPageLogicalWidth:(float)minPageLogicalWidth logicalHeight:(float)minPageLogicalHeight maximumPageLogicalWidth:(float)maxPageLogicalWidth adjustViewSize:(BOOL)adjustViewSize paginateScreenContent:(BOOL)paginateScreenContent 3913{ 3914 if (printing == _private->printing && paginateScreenContent == _private->paginateScreenContent) 3915 return; 3916 3917 WebFrame *frame = [self _frame]; 3918 NSArray *subframes = [frame childFrames]; 3919 unsigned n = [subframes count]; 3920 unsigned i; 3921 for (i = 0; i != n; ++i) { 3922 WebFrame *subframe = [subframes objectAtIndex:i]; 3923 WebFrameView *frameView = [subframe frameView]; 3924 if ([[subframe _dataSource] _isDocumentHTML]) { 3925 [(WebHTMLView *)[frameView documentView] _setPrinting:printing minimumPageLogicalWidth:0 logicalHeight:0 maximumPageLogicalWidth:0 adjustViewSize:adjustViewSize paginateScreenContent:paginateScreenContent]; 3926 } 3927 } 3928 3929 [_private->pageRects release]; 3930 _private->pageRects = nil; 3931 _private->printing = printing; 3932 _private->paginateScreenContent = paginateScreenContent; 3933 3934 Frame* coreFrame = core([self _frame]); 3935 if (coreFrame) { 3936 if (FrameView* coreView = coreFrame->view()) 3937 coreView->setMediaType(_private->printing ? "print" : "screen"); 3938 if (Document* document = coreFrame->document()) { 3939 document->setPaginatedForScreen(_private->paginateScreenContent); 3940 document->setPrinting(_private->printing); 3941 document->styleSelectorChanged(RecalcStyleImmediately); 3942 } 3943 } 3944 3945 [self setNeedsLayout:YES]; 3946 [self layoutToMinimumPageWidth:minPageLogicalWidth height:minPageLogicalHeight maximumPageWidth:maxPageLogicalWidth adjustingViewSize:adjustViewSize]; 3947 if (!printing) { 3948 // Can't do this when starting printing or nested printing won't work, see 3491427. 3949 [self setNeedsDisplay:NO]; 3950 } 3951} 3952 3953- (BOOL)canPrintHeadersAndFooters 3954{ 3955 return YES; 3956} 3957 3958// This is needed for the case where the webview is embedded in the view that's being printed. 3959// It shouldn't be called when the webview is being printed directly. 3960- (void)adjustPageHeightNew:(CGFloat *)newBottom top:(CGFloat)oldTop bottom:(CGFloat)oldBottom limit:(CGFloat)bottomLimit 3961{ 3962 // This helps when we print as part of a larger print process. 3963 // If the WebHTMLView itself is what we're printing, then we will never have to do this. 3964 BOOL wasInPrintingMode = _private->printing; 3965 if (!wasInPrintingMode) 3966 [self _setPrinting:YES minimumPageLogicalWidth:0 logicalHeight:0 maximumPageLogicalWidth:0 adjustViewSize:NO paginateScreenContent:[self _isInScreenPaginationMode]]; 3967 3968 *newBottom = [self _adjustedBottomOfPageWithTop:oldTop bottom:oldBottom limit:bottomLimit]; 3969 3970 if (!wasInPrintingMode) { 3971 NSPrintOperation *currenPrintOperation = [NSPrintOperation currentOperation]; 3972 if (currenPrintOperation) 3973 // delay _setPrinting:NO until back to main loop as this method may get called repeatedly 3974 [self performSelector:@selector(_delayedEndPrintMode:) withObject:currenPrintOperation afterDelay:0]; 3975 else 3976 // not sure if this is actually ever invoked, it probably shouldn't be 3977 [self _setPrinting:NO minimumPageLogicalWidth:0 logicalHeight:0 maximumPageLogicalWidth:0 adjustViewSize:NO paginateScreenContent:[self _isInScreenPaginationMode]]; 3978 } 3979} 3980 3981- (float)_scaleFactorForPrintOperation:(NSPrintOperation *)printOperation 3982{ 3983 bool useViewWidth = true; 3984 Frame* coreFrame = core([self _frame]); 3985 if (coreFrame) { 3986 Document* document = coreFrame->document(); 3987 if (document && document->renderView()) 3988 useViewWidth = document->renderView()->style()->isHorizontalWritingMode(); 3989 } 3990 3991 float viewLogicalWidth = useViewWidth ? NSWidth([self bounds]) : NSHeight([self bounds]); 3992 if (viewLogicalWidth < 1) { 3993 LOG_ERROR("%@ has no logical width when printing", self); 3994 return 1.0f; 3995 } 3996 3997 float userScaleFactor = [printOperation _web_pageSetupScaleFactor]; 3998 float maxShrinkToFitScaleFactor = 1.0f / _WebHTMLViewPrintingMaximumShrinkFactor; 3999 float shrinkToFitScaleFactor = (useViewWidth ? [printOperation _web_availablePaperWidth] : [printOperation _web_availablePaperHeight]) / viewLogicalWidth; 4000 return userScaleFactor * max(maxShrinkToFitScaleFactor, shrinkToFitScaleFactor); 4001} 4002 4003// FIXME 3491344: This is a secret AppKit-internal method that we need to override in order 4004// to get our shrink-to-fit to work with a custom pagination scheme. We can do this better 4005// if AppKit makes it SPI/API. 4006- (CGFloat)_provideTotalScaleFactorForPrintOperation:(NSPrintOperation *)printOperation 4007{ 4008 return [self _scaleFactorForPrintOperation:printOperation]; 4009} 4010 4011// This is used for Carbon printing. At some point we might want to make this public API. 4012- (void)setPageWidthForPrinting:(float)pageWidth 4013{ 4014 [self _setPrinting:NO minimumPageLogicalWidth:0 logicalHeight:0 maximumPageLogicalWidth:0 adjustViewSize:NO paginateScreenContent:[self _isInScreenPaginationMode]]; 4015 [self _setPrinting:YES minimumPageLogicalWidth:pageWidth logicalHeight:0 maximumPageLogicalWidth:pageWidth adjustViewSize:YES paginateScreenContent:[self _isInScreenPaginationMode]]; 4016} 4017 4018- (void)_endPrintModeAndRestoreWindowAutodisplay 4019{ 4020 [self _endPrintMode]; 4021 [[self window] setAutodisplay:YES]; 4022} 4023 4024- (void)_delayedEndPrintMode:(NSPrintOperation *)initiatingOperation 4025{ 4026 ASSERT_ARG(initiatingOperation, initiatingOperation != nil); 4027 NSPrintOperation *currentOperation = [NSPrintOperation currentOperation]; 4028 if (initiatingOperation == currentOperation) { 4029 // The print operation is still underway. We don't expect this to ever happen, hence the assert, but we're 4030 // being extra paranoid here since the printing code is so fragile. Delay the cleanup 4031 // further. 4032 ASSERT_NOT_REACHED(); 4033 [self performSelector:@selector(_delayedEndPrintMode:) withObject:initiatingOperation afterDelay:0]; 4034 } else if ([currentOperation view] == self) { 4035 // A new print job has started, but it is printing the same WebHTMLView again. We don't expect 4036 // this to ever happen, hence the assert, but we're being extra paranoid here since the printing code is so 4037 // fragile. Do nothing, because we don't want to break the print job currently in progress, and 4038 // the print job currently in progress is responsible for its own cleanup. 4039 ASSERT_NOT_REACHED(); 4040 } else { 4041 // The print job that kicked off this delayed call has finished, and this view is not being 4042 // printed again. We expect that no other print job has started. Since this delayed call wasn't 4043 // cancelled, beginDocument and endDocument must not have been called, and we need to clean up 4044 // the print mode here. 4045 ASSERT(currentOperation == nil); 4046 [self _endPrintModeAndRestoreWindowAutodisplay]; 4047 } 4048} 4049 4050// Return the number of pages available for printing 4051- (BOOL)knowsPageRange:(NSRangePointer)range 4052{ 4053 // Must do this explicit display here, because otherwise the view might redisplay while the print 4054 // sheet was up, using printer fonts (and looking different). 4055 [self displayIfNeeded]; 4056 [[self window] setAutodisplay:NO]; 4057 4058 [[self _webView] _adjustPrintingMarginsForHeaderAndFooter]; 4059 NSPrintOperation *printOperation = [NSPrintOperation currentOperation]; 4060 if (![self _beginPrintModeWithPageWidth:[printOperation _web_availablePaperWidth] height:[printOperation _web_availablePaperHeight] shrinkToFit:YES]) 4061 return NO; 4062 4063 // Certain types of errors, including invalid page ranges, can cause beginDocument and 4064 // endDocument to be skipped after we've put ourselves in print mode (see 4145905). In those cases 4065 // we need to get out of print mode without relying on any more callbacks from the printing mechanism. 4066 // If we get as far as beginDocument without trouble, then this delayed request will be cancelled. 4067 // If not cancelled, this delayed call will be invoked in the next pass through the main event loop, 4068 // which is after beginDocument and endDocument would be called. 4069 [self performSelector:@selector(_delayedEndPrintMode:) withObject:printOperation afterDelay:0]; 4070 4071 // There is a theoretical chance that someone could do some drawing between here and endDocument, 4072 // if something caused setNeedsDisplay after this point. If so, it's not a big tragedy, because 4073 // you'd simply see the printer fonts on screen. As of this writing, this does not happen with Safari. 4074 4075 range->location = 1; 4076 float totalScaleFactor = [self _scaleFactorForPrintOperation:printOperation]; 4077 float userScaleFactor = [printOperation _web_pageSetupScaleFactor]; 4078 [_private->pageRects release]; 4079 float fullPageWidth = floorf([printOperation _web_availablePaperWidth] / totalScaleFactor); 4080 float fullPageHeight = floorf([printOperation _web_availablePaperHeight] / totalScaleFactor); 4081 WebFrame *frame = [self _frame]; 4082 NSArray *newPageRects = [frame _computePageRectsWithPrintScaleFactor:userScaleFactor pageSize:NSMakeSize(fullPageWidth, fullPageHeight)]; 4083 4084 // AppKit gets all messed up if you give it a zero-length page count (see 3576334), so if we 4085 // hit that case we'll pass along a degenerate 1 pixel square to print. This will print 4086 // a blank page (with correct-looking header and footer if that option is on), which matches 4087 // the behavior of IE and Camino at least. 4088 if ([newPageRects count] == 0) 4089 newPageRects = [NSArray arrayWithObject:[NSValue valueWithRect:NSMakeRect(0, 0, 1, 1)]]; 4090 4091 _private->pageRects = [newPageRects retain]; 4092 4093 range->length = [_private->pageRects count]; 4094 4095 return YES; 4096} 4097 4098// Return the drawing rectangle for a particular page number 4099- (NSRect)rectForPage:(NSInteger)page 4100{ 4101 return [[_private->pageRects objectAtIndex:page - 1] rectValue]; 4102} 4103 4104- (void)drawPageBorderWithSize:(NSSize)borderSize 4105{ 4106 ASSERT(NSEqualSizes(borderSize, [[[NSPrintOperation currentOperation] printInfo] paperSize])); 4107 [[self _webView] _drawHeaderAndFooter]; 4108} 4109 4110- (void)beginDocument 4111{ 4112 @try { 4113 // From now on we'll get a chance to call _endPrintMode in either beginDocument or 4114 // endDocument, so we can cancel the "just in case" pending call. 4115 [NSObject cancelPreviousPerformRequestsWithTarget:self 4116 selector:@selector(_delayedEndPrintMode:) 4117 object:[NSPrintOperation currentOperation]]; 4118 [super beginDocument]; 4119 } @catch (NSException *localException) { 4120 // Exception during [super beginDocument] means that endDocument will not get called, 4121 // so we need to clean up our "print mode" here. 4122 [self _endPrintModeAndRestoreWindowAutodisplay]; 4123 } 4124} 4125 4126- (void)endDocument 4127{ 4128 [super endDocument]; 4129 // Note sadly at this point [NSGraphicsContext currentContextDrawingToScreen] is still NO 4130 [self _endPrintModeAndRestoreWindowAutodisplay]; 4131} 4132 4133- (void)keyDown:(NSEvent *)event 4134{ 4135 // There's a chance that responding to this event will run a nested event loop, and 4136 // fetching a new event might release the old one. Retaining and then autoreleasing 4137 // the current event prevents that from causing a problem inside WebKit or AppKit code. 4138 [[event retain] autorelease]; 4139 4140 RetainPtr<WebHTMLView> selfProtector = self; 4141 BOOL eventWasSentToWebCore = (_private->keyDownEvent == event); 4142 4143 BOOL callSuper = NO; 4144 4145 [_private->keyDownEvent release]; 4146 _private->keyDownEvent = [event retain]; 4147 4148 BOOL completionPopupWasOpen = _private->completionController && [_private->completionController popupWindowIsOpen]; 4149 Frame* coreFrame = core([self _frame]); 4150 if (!eventWasSentToWebCore && coreFrame && coreFrame->eventHandler()->keyEvent(event)) { 4151 // WebCore processed a key event, bail on any preexisting complete: UI 4152 if (completionPopupWasOpen) 4153 [_private->completionController endRevertingChange:YES moveLeft:NO]; 4154 } else if (!_private->completionController || ![_private->completionController filterKeyDown:event]) { 4155 // Not consumed by complete: popup window 4156 [_private->completionController endRevertingChange:YES moveLeft:NO]; 4157 callSuper = YES; 4158 } 4159 if (callSuper) 4160 [super keyDown:event]; 4161 else 4162 [NSCursor setHiddenUntilMouseMoves:YES]; 4163} 4164 4165- (void)keyUp:(NSEvent *)event 4166{ 4167 // There's a chance that responding to this event will run a nested event loop, and 4168 // fetching a new event might release the old one. Retaining and then autoreleasing 4169 // the current event prevents that from causing a problem inside WebKit or AppKit code. 4170 [[event retain] autorelease]; 4171 4172 BOOL eventWasSentToWebCore = (_private->keyDownEvent == event); 4173 4174 RetainPtr<WebHTMLView> selfProtector = self; 4175 Frame* coreFrame = core([self _frame]); 4176 if (coreFrame && !eventWasSentToWebCore) 4177 coreFrame->eventHandler()->keyEvent(event); 4178 else 4179 [super keyUp:event]; 4180} 4181 4182- (void)flagsChanged:(NSEvent *)event 4183{ 4184 // There's a chance that responding to this event will run a nested event loop, and 4185 // fetching a new event might release the old one. Retaining and then autoreleasing 4186 // the current event prevents that from causing a problem inside WebKit or AppKit code. 4187 [[event retain] autorelease]; 4188 4189 RetainPtr<WebHTMLView> selfProtector = self; 4190 4191 Frame* coreFrame = core([self _frame]); 4192 unsigned short keyCode = [event keyCode]; 4193 4194 // Don't make an event from the num lock and function keys. 4195 if (coreFrame && keyCode != 0 && keyCode != 10 && keyCode != 63) { 4196 coreFrame->eventHandler()->keyEvent(PlatformKeyboardEvent(event)); 4197 return; 4198 } 4199 4200 [super flagsChanged:event]; 4201} 4202 4203- (id)accessibilityAttributeValue:(NSString*)attributeName 4204{ 4205 if ([attributeName isEqualToString: NSAccessibilityChildrenAttribute]) { 4206 id accTree = [[self _frame] accessibilityRoot]; 4207 if (accTree) 4208 return [NSArray arrayWithObject:accTree]; 4209 return nil; 4210 } 4211 return [super accessibilityAttributeValue:attributeName]; 4212} 4213 4214- (id)accessibilityFocusedUIElement 4215{ 4216 id accTree = [[self _frame] accessibilityRoot]; 4217 if (accTree) 4218 return [accTree accessibilityFocusedUIElement]; 4219 return self; 4220} 4221 4222- (id)accessibilityHitTest:(NSPoint)point 4223{ 4224 id accTree = [[self _frame] accessibilityRoot]; 4225 if (accTree) { 4226 NSPoint windowCoord = [[self window] convertScreenToBase:point]; 4227 return [accTree accessibilityHitTest:[self convertPoint:windowCoord fromView:nil]]; 4228 } 4229 return self; 4230} 4231 4232- (id)_accessibilityParentForSubview:(NSView *)subview 4233{ 4234 id accTree = [[self _frame] accessibilityRoot]; 4235 if (!accTree) 4236 return self; 4237 id parent = [accTree _accessibilityParentForSubview:subview]; 4238 if (!parent) 4239 return self; 4240 return parent; 4241} 4242 4243- (void)centerSelectionInVisibleArea:(id)sender 4244{ 4245 COMMAND_PROLOGUE 4246 4247 if (Frame* coreFrame = core([self _frame])) 4248 coreFrame->selection()->revealSelection(ScrollAlignment::alignCenterAlways); 4249} 4250 4251- (NSData *)_selectionStartFontAttributesAsRTF 4252{ 4253 Frame* coreFrame = core([self _frame]); 4254 NSAttributedString *string = [[NSAttributedString alloc] initWithString:@"x" 4255 attributes:coreFrame ? coreFrame->editor()->fontAttributesForSelectionStart() : nil]; 4256 NSData *data = [string RTFFromRange:NSMakeRange(0, [string length]) documentAttributes:nil]; 4257 [string release]; 4258 return data; 4259} 4260 4261- (NSDictionary *)_fontAttributesFromFontPasteboard 4262{ 4263 NSPasteboard *fontPasteboard = [NSPasteboard pasteboardWithName:NSFontPboard]; 4264 if (fontPasteboard == nil) 4265 return nil; 4266 NSData *data = [fontPasteboard dataForType:NSFontPboardType]; 4267 if (data == nil || [data length] == 0) 4268 return nil; 4269 // NSTextView does something more efficient by parsing the attributes only, but that's not available in API. 4270 NSAttributedString *string = [[[NSAttributedString alloc] initWithRTF:data documentAttributes:NULL] autorelease]; 4271 if (string == nil || [string length] == 0) 4272 return nil; 4273 return [string fontAttributesInRange:NSMakeRange(0, 1)]; 4274} 4275 4276- (DOMCSSStyleDeclaration *)_emptyStyle 4277{ 4278 return [[[self _frame] DOMDocument] createCSSStyleDeclaration]; 4279} 4280 4281- (NSString *)_colorAsString:(NSColor *)color 4282{ 4283 NSColor *rgbColor = [color colorUsingColorSpaceName:NSDeviceRGBColorSpace]; 4284 // FIXME: If color is non-nil and rgbColor is nil, that means we got some kind 4285 // of fancy color that can't be converted to RGB. Changing that to "transparent" 4286 // might not be great, but it's probably OK. 4287 if (rgbColor == nil) 4288 return @"transparent"; 4289 float r = [rgbColor redComponent]; 4290 float g = [rgbColor greenComponent]; 4291 float b = [rgbColor blueComponent]; 4292 float a = [rgbColor alphaComponent]; 4293 if (a == 0) 4294 return @"transparent"; 4295 if (r == 0 && g == 0 && b == 0 && a == 1) 4296 return @"black"; 4297 if (r == 1 && g == 1 && b == 1 && a == 1) 4298 return @"white"; 4299 // FIXME: Lots more named colors. Maybe we could use the table in WebCore? 4300 if (a == 1) 4301 return [NSString stringWithFormat:@"rgb(%.0f,%.0f,%.0f)", r * 255, g * 255, b * 255]; 4302 return [NSString stringWithFormat:@"rgba(%.0f,%.0f,%.0f,%f)", r * 255, g * 255, b * 255, a]; 4303} 4304 4305- (NSString *)_shadowAsString:(NSShadow *)shadow 4306{ 4307 if (shadow == nil) 4308 return @"none"; 4309 NSSize offset = [shadow shadowOffset]; 4310 float blurRadius = [shadow shadowBlurRadius]; 4311 if (offset.width == 0 && offset.height == 0 && blurRadius == 0) 4312 return @"none"; 4313 NSColor *color = [shadow shadowColor]; 4314 if (color == nil) 4315 return @"none"; 4316 // FIXME: Handle non-integral values here? 4317 if (blurRadius == 0) 4318 return [NSString stringWithFormat:@"%@ %.0fpx %.0fpx", [self _colorAsString:color], offset.width, offset.height]; 4319 return [NSString stringWithFormat:@"%@ %.0fpx %.0fpx %.0fpx", [self _colorAsString:color], offset.width, offset.height, blurRadius]; 4320} 4321 4322- (DOMCSSStyleDeclaration *)_styleFromFontAttributes:(NSDictionary *)dictionary 4323{ 4324 DOMCSSStyleDeclaration *style = [self _emptyStyle]; 4325 4326 NSColor *color = [dictionary objectForKey:NSBackgroundColorAttributeName]; 4327 [style setBackgroundColor:[self _colorAsString:color]]; 4328 4329 NSFont *font = [dictionary objectForKey:NSFontAttributeName]; 4330 if (!font) { 4331 [style setFontFamily:@"Helvetica"]; 4332 [style setFontSize:@"12px"]; 4333 [style setFontWeight:@"normal"]; 4334 [style setFontStyle:@"normal"]; 4335 } else { 4336 NSFontManager *fm = [NSFontManager sharedFontManager]; 4337 // FIXME: Need more sophisticated escaping code if we want to handle family names 4338 // with characters like single quote or backslash in their names. 4339 [style setFontFamily:[NSString stringWithFormat:@"'%@'", [font familyName]]]; 4340 [style setFontSize:[NSString stringWithFormat:@"%0.fpx", [font pointSize]]]; 4341 // FIXME: Map to the entire range of CSS weight values. 4342 if ([fm weightOfFont:font] >= MIN_BOLD_WEIGHT) 4343 [style setFontWeight:@"bold"]; 4344 else 4345 [style setFontWeight:@"normal"]; 4346 if ([fm traitsOfFont:font] & NSItalicFontMask) 4347 [style setFontStyle:@"italic"]; 4348 else 4349 [style setFontStyle:@"normal"]; 4350 } 4351 4352 color = [dictionary objectForKey:NSForegroundColorAttributeName]; 4353 [style setColor:color ? [self _colorAsString:color] : (NSString *)@"black"]; 4354 4355 NSShadow *shadow = [dictionary objectForKey:NSShadowAttributeName]; 4356 [style setTextShadow:[self _shadowAsString:shadow]]; 4357 4358 int strikethroughInt = [[dictionary objectForKey:NSStrikethroughStyleAttributeName] intValue]; 4359 4360 int superscriptInt = [[dictionary objectForKey:NSSuperscriptAttributeName] intValue]; 4361 if (superscriptInt > 0) 4362 [style setVerticalAlign:@"super"]; 4363 else if (superscriptInt < 0) 4364 [style setVerticalAlign:@"sub"]; 4365 else 4366 [style setVerticalAlign:@"baseline"]; 4367 int underlineInt = [[dictionary objectForKey:NSUnderlineStyleAttributeName] intValue]; 4368 // FIXME: Underline wins here if we have both (see bug 3790443). 4369 if (strikethroughInt == NSUnderlineStyleNone && underlineInt == NSUnderlineStyleNone) 4370 [style setProperty:@"-khtml-text-decorations-in-effect" value:@"none" priority:@""]; 4371 else if (underlineInt == NSUnderlineStyleNone) 4372 [style setProperty:@"-khtml-text-decorations-in-effect" value:@"line-through" priority:@""]; 4373 else 4374 [style setProperty:@"-khtml-text-decorations-in-effect" value:@"underline" priority:@""]; 4375 4376 return style; 4377} 4378 4379- (void)_applyStyleToSelection:(DOMCSSStyleDeclaration *)style withUndoAction:(EditAction)undoAction 4380{ 4381 if (Frame* coreFrame = core([self _frame])) 4382 coreFrame->editor()->applyStyleToSelection(core(style), undoAction); 4383} 4384 4385- (void)_applyParagraphStyleToSelection:(DOMCSSStyleDeclaration *)style withUndoAction:(EditAction)undoAction 4386{ 4387 if (Frame* coreFrame = core([self _frame])) 4388 coreFrame->editor()->applyParagraphStyleToSelection(core(style), undoAction); 4389} 4390 4391- (BOOL)_handleStyleKeyEquivalent:(NSEvent *)event 4392{ 4393 WebView *webView = [self _webView]; 4394 if (!webView) 4395 return NO; 4396 4397 if (![[webView preferences] respectStandardStyleKeyEquivalents]) 4398 return NO; 4399 4400 if (![self _canEdit]) 4401 return NO; 4402 4403 if (([event modifierFlags] & NSDeviceIndependentModifierFlagsMask) != NSCommandKeyMask) 4404 return NO; 4405 4406 NSString *string = [event characters]; 4407 if ([string caseInsensitiveCompare:@"b"] == NSOrderedSame) { 4408 [self executeCoreCommandByName:"ToggleBold"]; 4409 return YES; 4410 } 4411 if ([string caseInsensitiveCompare:@"i"] == NSOrderedSame) { 4412 [self executeCoreCommandByName:"ToggleItalic"]; 4413 return YES; 4414 } 4415 4416 return NO; 4417} 4418 4419- (BOOL)performKeyEquivalent:(NSEvent *)event 4420{ 4421 // There's a chance that responding to this event will run a nested event loop, and 4422 // fetching a new event might release the old one. Retaining and then autoreleasing 4423 // the current event prevents that from causing a problem inside WebKit or AppKit code. 4424 [[event retain] autorelease]; 4425 4426 BOOL eventWasSentToWebCore = (_private->keyDownEvent == event); 4427 BOOL ret = NO; 4428 4429 [_private->keyDownEvent release]; 4430 _private->keyDownEvent = [event retain]; 4431 4432 [self retain]; 4433 4434 // Pass command-key combos through WebCore if there is a key binding available for 4435 // this event. This lets web pages have a crack at intercepting command-modified keypresses. 4436 // But don't do it if we have already handled the event. 4437 // Pressing Esc results in a fake event being sent - don't pass it to WebCore. 4438 if (!eventWasSentToWebCore && event == [NSApp currentEvent] && self == [[self window] firstResponder]) 4439 if (Frame* frame = core([self _frame])) 4440 ret = frame->eventHandler()->keyEvent(event); 4441 4442 if (!ret) 4443 ret = [self _handleStyleKeyEquivalent:event] || [super performKeyEquivalent:event]; 4444 4445 [self release]; 4446 4447 return ret; 4448} 4449 4450- (void)copyFont:(id)sender 4451{ 4452 COMMAND_PROLOGUE 4453 4454 // Put RTF with font attributes on the pasteboard. 4455 // Maybe later we should add a pasteboard type that contains CSS text for "native" copy and paste font. 4456 NSPasteboard *fontPasteboard = [NSPasteboard pasteboardWithName:NSFontPboard]; 4457 [fontPasteboard declareTypes:[NSArray arrayWithObject:NSFontPboardType] owner:nil]; 4458 [fontPasteboard setData:[self _selectionStartFontAttributesAsRTF] forType:NSFontPboardType]; 4459} 4460 4461- (void)pasteFont:(id)sender 4462{ 4463 COMMAND_PROLOGUE 4464 4465 // Read RTF with font attributes from the pasteboard. 4466 // Maybe later we should add a pasteboard type that contains CSS text for "native" copy and paste font. 4467 [self _applyStyleToSelection:[self _styleFromFontAttributes:[self _fontAttributesFromFontPasteboard]] withUndoAction:EditActionPasteFont]; 4468} 4469 4470- (void)pasteAsRichText:(id)sender 4471{ 4472 COMMAND_PROLOGUE 4473 4474 // Since rich text always beats plain text when both are on the pasteboard, it's not 4475 // clear how this is different from plain old paste. 4476 [self _pasteWithPasteboard:[NSPasteboard generalPasteboard] allowPlainText:NO]; 4477} 4478 4479- (NSFont *)_originalFontA 4480{ 4481 return [[NSFontManager sharedFontManager] fontWithFamily:@"Helvetica" traits:0 weight:STANDARD_WEIGHT size:10.0f]; 4482} 4483 4484- (NSFont *)_originalFontB 4485{ 4486 return [[NSFontManager sharedFontManager] fontWithFamily:@"Times" traits:NSFontItalicTrait weight:STANDARD_BOLD_WEIGHT size:12.0f]; 4487} 4488 4489- (void)_addToStyle:(DOMCSSStyleDeclaration *)style fontA:(NSFont *)a fontB:(NSFont *)b 4490{ 4491 // Since there's no way to directly ask NSFontManager what style change it's going to do 4492 // we instead pass two "specimen" fonts to it and let it change them. We then deduce what 4493 // style change it was doing by looking at what happened to each of the two fonts. 4494 // So if it was making the text bold, both fonts will be bold after the fact. 4495 4496 if (a == nil || b == nil) 4497 return; 4498 4499 NSFontManager *fm = [NSFontManager sharedFontManager]; 4500 4501 NSFont *oa = [self _originalFontA]; 4502 4503 NSString *aFamilyName = [a familyName]; 4504 NSString *bFamilyName = [b familyName]; 4505 4506 int aPointSize = (int)[a pointSize]; 4507 int bPointSize = (int)[b pointSize]; 4508 4509 int aWeight = [fm weightOfFont:a]; 4510 int bWeight = [fm weightOfFont:b]; 4511 4512 BOOL aIsItalic = ([fm traitsOfFont:a] & NSItalicFontMask) != 0; 4513 BOOL bIsItalic = ([fm traitsOfFont:b] & NSItalicFontMask) != 0; 4514 4515 BOOL aIsBold = aWeight > MIN_BOLD_WEIGHT; 4516 4517 if ([aFamilyName isEqualToString:bFamilyName]) { 4518 NSString *familyNameForCSS = aFamilyName; 4519 4520 // The family name may not be specific enough to get us the font specified. 4521 // In some cases, the only way to get exactly what we are looking for is to use 4522 // the Postscript name. 4523 4524 // Find the font the same way the rendering code would later if it encountered this CSS. 4525 NSFontTraitMask traits = aIsItalic ? NSFontItalicTrait : 0; 4526 int weight = aIsBold ? STANDARD_BOLD_WEIGHT : STANDARD_WEIGHT; 4527 NSFont *foundFont = [WebFontCache fontWithFamily:aFamilyName traits:traits weight:weight size:aPointSize]; 4528 4529 // If we don't find a font with the same Postscript name, then we'll have to use the 4530 // Postscript name to make the CSS specific enough. 4531 if (![[foundFont fontName] isEqualToString:[a fontName]]) 4532 familyNameForCSS = [a fontName]; 4533 4534 // FIXME: Need more sophisticated escaping code if we want to handle family names 4535 // with characters like single quote or backslash in their names. 4536 [style setFontFamily:[NSString stringWithFormat:@"'%@'", familyNameForCSS]]; 4537 } 4538 4539 int soa = (int)[oa pointSize]; 4540 if (aPointSize == bPointSize) 4541 [style setFontSize:[NSString stringWithFormat:@"%dpx", aPointSize]]; 4542 else if (aPointSize < soa) 4543 [style _setFontSizeDelta:@"-1px"]; 4544 else if (aPointSize > soa) 4545 [style _setFontSizeDelta:@"1px"]; 4546 4547 // FIXME: Map to the entire range of CSS weight values. 4548 if (aWeight == bWeight) 4549 [style setFontWeight:aIsBold ? @"bold" : @"normal"]; 4550 4551 if (aIsItalic == bIsItalic) 4552 [style setFontStyle:aIsItalic ? @"italic" : @"normal"]; 4553} 4554 4555- (DOMCSSStyleDeclaration *)_styleFromFontManagerOperation 4556{ 4557 DOMCSSStyleDeclaration *style = [self _emptyStyle]; 4558 4559 NSFontManager *fm = [NSFontManager sharedFontManager]; 4560 4561 NSFont *oa = [self _originalFontA]; 4562 NSFont *ob = [self _originalFontB]; 4563 [self _addToStyle:style fontA:[fm convertFont:oa] fontB:[fm convertFont:ob]]; 4564 4565 return style; 4566} 4567 4568- (void)changeFont:(id)sender 4569{ 4570 COMMAND_PROLOGUE 4571 4572 [self _applyStyleToSelection:[self _styleFromFontManagerOperation] withUndoAction:EditActionSetFont]; 4573} 4574 4575- (DOMCSSStyleDeclaration *)_styleForAttributeChange:(id)sender 4576{ 4577 DOMCSSStyleDeclaration *style = [self _emptyStyle]; 4578 4579 NSShadow *shadow = [[NSShadow alloc] init]; 4580 [shadow setShadowOffset:NSMakeSize(1, 1)]; 4581 4582 NSDictionary *oa = [NSDictionary dictionaryWithObjectsAndKeys: 4583 [self _originalFontA], NSFontAttributeName, 4584 nil]; 4585 NSDictionary *ob = [NSDictionary dictionaryWithObjectsAndKeys: 4586 [NSColor blackColor], NSBackgroundColorAttributeName, 4587 [self _originalFontB], NSFontAttributeName, 4588 [NSColor whiteColor], NSForegroundColorAttributeName, 4589 shadow, NSShadowAttributeName, 4590 [NSNumber numberWithInt:NSUnderlineStyleSingle], NSStrikethroughStyleAttributeName, 4591 [NSNumber numberWithInt:1], NSSuperscriptAttributeName, 4592 [NSNumber numberWithInt:NSUnderlineStyleSingle], NSUnderlineStyleAttributeName, 4593 nil]; 4594 4595 [shadow release]; 4596 4597#if 0 4598 4599NSObliquenessAttributeName /* float; skew to be applied to glyphs, default 0: no skew */ 4600 // font-style, but that is just an on-off switch 4601 4602NSExpansionAttributeName /* float; log of expansion factor to be applied to glyphs, default 0: no expansion */ 4603 // font-stretch? 4604 4605NSKernAttributeName /* float, amount to modify default kerning, if 0, kerning off */ 4606 // letter-spacing? probably not good enough 4607 4608NSUnderlineColorAttributeName /* NSColor, default nil: same as foreground color */ 4609NSStrikethroughColorAttributeName /* NSColor, default nil: same as foreground color */ 4610 // text-decoration-color? 4611 4612NSLigatureAttributeName /* int, default 1: default ligatures, 0: no ligatures, 2: all ligatures */ 4613NSBaselineOffsetAttributeName /* float, in points; offset from baseline, default 0 */ 4614NSStrokeWidthAttributeName /* float, in percent of font point size, default 0: no stroke; positive for stroke alone, negative for stroke and fill (a typical value for outlined text would be 3.0) */ 4615NSStrokeColorAttributeName /* NSColor, default nil: same as foreground color */ 4616 // need extensions? 4617 4618#endif 4619 4620 NSDictionary *a = [sender convertAttributes:oa]; 4621 NSDictionary *b = [sender convertAttributes:ob]; 4622 4623 NSColor *ca = [a objectForKey:NSBackgroundColorAttributeName]; 4624 NSColor *cb = [b objectForKey:NSBackgroundColorAttributeName]; 4625 if (ca == cb) { 4626 [style setBackgroundColor:[self _colorAsString:ca]]; 4627 } 4628 4629 [self _addToStyle:style fontA:[a objectForKey:NSFontAttributeName] fontB:[b objectForKey:NSFontAttributeName]]; 4630 4631 ca = [a objectForKey:NSForegroundColorAttributeName]; 4632 cb = [b objectForKey:NSForegroundColorAttributeName]; 4633 if (ca == cb) { 4634 [style setColor:[self _colorAsString:ca]]; 4635 } 4636 4637 NSShadow *sha = [a objectForKey:NSShadowAttributeName]; 4638 if (sha) 4639 [style setTextShadow:[self _shadowAsString:sha]]; 4640 else if ([b objectForKey:NSShadowAttributeName] == nil) 4641 [style setTextShadow:@"none"]; 4642 4643 int sa = [[a objectForKey:NSStrikethroughStyleAttributeName] intValue]; 4644 int sb = [[b objectForKey:NSStrikethroughStyleAttributeName] intValue]; 4645 if (sa == sb) { 4646 if (sa == NSUnderlineStyleNone) 4647 [style setProperty:@"-khtml-text-decorations-in-effect" value:@"none" priority:@""]; 4648 // we really mean "no line-through" rather than "none" 4649 else 4650 [style setProperty:@"-khtml-text-decorations-in-effect" value:@"line-through" priority:@""]; 4651 // we really mean "add line-through" rather than "line-through" 4652 } 4653 4654 sa = [[a objectForKey:NSSuperscriptAttributeName] intValue]; 4655 sb = [[b objectForKey:NSSuperscriptAttributeName] intValue]; 4656 if (sa == sb) { 4657 if (sa > 0) 4658 [style setVerticalAlign:@"super"]; 4659 else if (sa < 0) 4660 [style setVerticalAlign:@"sub"]; 4661 else 4662 [style setVerticalAlign:@"baseline"]; 4663 } 4664 4665 int ua = [[a objectForKey:NSUnderlineStyleAttributeName] intValue]; 4666 int ub = [[b objectForKey:NSUnderlineStyleAttributeName] intValue]; 4667 if (ua == ub) { 4668 if (ua == NSUnderlineStyleNone) 4669 [style setProperty:@"-khtml-text-decorations-in-effect" value:@"none" priority:@""]; 4670 // we really mean "no underline" rather than "none" 4671 else 4672 [style setProperty:@"-khtml-text-decorations-in-effect" value:@"underline" priority:@""]; 4673 // we really mean "add underline" rather than "underline" 4674 } 4675 4676 return style; 4677} 4678 4679- (void)changeAttributes:(id)sender 4680{ 4681 COMMAND_PROLOGUE 4682 4683 [self _applyStyleToSelection:[self _styleForAttributeChange:sender] withUndoAction:EditActionChangeAttributes]; 4684} 4685 4686- (DOMCSSStyleDeclaration *)_styleFromColorPanelWithSelector:(SEL)selector 4687{ 4688 DOMCSSStyleDeclaration *style = [self _emptyStyle]; 4689 4690 ASSERT([style respondsToSelector:selector]); 4691 [style performSelector:selector withObject:[self _colorAsString:[[NSColorPanel sharedColorPanel] color]]]; 4692 4693 return style; 4694} 4695 4696- (EditAction)_undoActionFromColorPanelWithSelector:(SEL)selector 4697{ 4698 if (selector == @selector(setBackgroundColor:)) 4699 return EditActionSetBackgroundColor; 4700 return EditActionSetColor; 4701} 4702 4703- (void)_changeCSSColorUsingSelector:(SEL)selector inRange:(DOMRange *)range 4704{ 4705 DOMCSSStyleDeclaration *style = [self _styleFromColorPanelWithSelector:selector]; 4706 WebView *webView = [self _webView]; 4707 if ([[webView _editingDelegateForwarder] webView:webView shouldApplyStyle:style toElementsInDOMRange:range]) 4708 if (Frame* coreFrame = core([self _frame])) 4709 coreFrame->editor()->applyStyle(core(style), [self _undoActionFromColorPanelWithSelector:selector]); 4710} 4711 4712- (void)changeDocumentBackgroundColor:(id)sender 4713{ 4714 COMMAND_PROLOGUE 4715 4716 // Mimicking NSTextView, this method sets the background color for the 4717 // entire document. There is no NSTextView API for setting the background 4718 // color on the selected range only. Note that this method is currently 4719 // never called from the UI (see comment in changeColor:). 4720 // FIXME: this actually has no effect when called, probably due to 3654850. _documentRange seems 4721 // to do the right thing because it works in startSpeaking:, and I know setBackgroundColor: does the 4722 // right thing because I tested it with [self _selectedRange]. 4723 // FIXME: This won't actually apply the style to the entire range here, because it ends up calling 4724 // [frame _applyStyle:], which operates on the current selection. To make this work right, we'll 4725 // need to save off the selection, temporarily set it to the entire range, make the change, then 4726 // restore the old selection. 4727 [self _changeCSSColorUsingSelector:@selector(setBackgroundColor:) inRange:[self _documentRange]]; 4728} 4729 4730- (void)changeColor:(id)sender 4731{ 4732 COMMAND_PROLOGUE 4733 4734 // FIXME: in NSTextView, this method calls changeDocumentBackgroundColor: when a 4735 // private call has earlier been made by [NSFontFontEffectsBox changeColor:], see 3674493. 4736 // AppKit will have to be revised to allow this to work with anything that isn't an 4737 // NSTextView. However, this might not be required for Tiger, since the background-color 4738 // changing box in the font panel doesn't work in Mail (3674481), though it does in TextEdit. 4739 [self _applyStyleToSelection:[self _styleFromColorPanelWithSelector:@selector(setColor:)] 4740 withUndoAction:EditActionSetColor]; 4741} 4742 4743- (void)_changeWordCaseWithSelector:(SEL)selector 4744{ 4745 if (![self _canEdit]) 4746 return; 4747 4748 WebFrame *frame = [self _frame]; 4749 [self selectWord:nil]; 4750 NSString *word = [[frame _selectedString] performSelector:selector]; 4751 // FIXME: Does this need a different action context other than "typed"? 4752 if ([self _shouldReplaceSelectionWithText:word givenAction:WebViewInsertActionTyped]) 4753 [frame _replaceSelectionWithText:word selectReplacement:NO smartReplace:NO]; 4754} 4755 4756- (void)uppercaseWord:(id)sender 4757{ 4758 COMMAND_PROLOGUE 4759 4760 [self _changeWordCaseWithSelector:@selector(uppercaseString)]; 4761} 4762 4763- (void)lowercaseWord:(id)sender 4764{ 4765 COMMAND_PROLOGUE 4766 4767 [self _changeWordCaseWithSelector:@selector(lowercaseString)]; 4768} 4769 4770- (void)capitalizeWord:(id)sender 4771{ 4772 COMMAND_PROLOGUE 4773 4774 [self _changeWordCaseWithSelector:@selector(capitalizedString)]; 4775} 4776 4777- (void)complete:(id)sender 4778{ 4779 COMMAND_PROLOGUE 4780 4781 if (![self _canEdit]) 4782 return; 4783 if (!_private->completionController) 4784 _private->completionController = [[WebTextCompletionController alloc] initWithWebView:[self _webView] HTMLView:self]; 4785 [_private->completionController doCompletion]; 4786} 4787 4788- (void)checkSpelling:(id)sender 4789{ 4790 COMMAND_PROLOGUE 4791 4792 if (Frame* coreFrame = core([self _frame])) 4793 coreFrame->editor()->advanceToNextMisspelling(); 4794} 4795 4796- (void)showGuessPanel:(id)sender 4797{ 4798 COMMAND_PROLOGUE 4799 4800 NSSpellChecker *checker = [NSSpellChecker sharedSpellChecker]; 4801 if (!checker) { 4802 LOG_ERROR("No NSSpellChecker"); 4803 return; 4804 } 4805 4806 NSPanel *spellingPanel = [checker spellingPanel]; 4807#ifndef BUILDING_ON_TIGER 4808 // Post-Tiger, this menu item is a show/hide toggle, to match AppKit. Leave Tiger behavior alone 4809 // to match rest of OS X. 4810 if ([spellingPanel isVisible]) { 4811 [spellingPanel orderOut:sender]; 4812 return; 4813 } 4814#endif 4815 4816 if (Frame* coreFrame = core([self _frame])) 4817 coreFrame->editor()->advanceToNextMisspelling(true); 4818 [spellingPanel orderFront:sender]; 4819} 4820 4821- (void)_changeSpellingToWord:(NSString *)newWord 4822{ 4823 if (![self _canEdit]) 4824 return; 4825 4826 // Don't correct to empty string. (AppKit checked this, we might as well too.) 4827 if (![NSSpellChecker sharedSpellChecker]) { 4828 LOG_ERROR("No NSSpellChecker"); 4829 return; 4830 } 4831 4832 if ([newWord isEqualToString:@""]) 4833 return; 4834 4835 if ([self _shouldReplaceSelectionWithText:newWord givenAction:WebViewInsertActionPasted]) 4836 [[self _frame] _replaceSelectionWithText:newWord selectReplacement:YES smartReplace:NO]; 4837} 4838 4839- (void)changeSpelling:(id)sender 4840{ 4841 COMMAND_PROLOGUE 4842 4843 [self _changeSpellingToWord:[[sender selectedCell] stringValue]]; 4844} 4845 4846- (void)performFindPanelAction:(id)sender 4847{ 4848 COMMAND_PROLOGUE 4849 4850 // Implementing this will probably require copying all of NSFindPanel.h and .m. 4851 // We need *almost* the same thing as AppKit, but not quite. 4852 LOG_ERROR("unimplemented"); 4853} 4854 4855- (void)startSpeaking:(id)sender 4856{ 4857 COMMAND_PROLOGUE 4858 4859 WebFrame *frame = [self _frame]; 4860 DOMRange *range = [self _selectedRange]; 4861 if (!range || [range collapsed]) 4862 range = [self _documentRange]; 4863 [NSApp speakString:[frame _stringForRange:range]]; 4864} 4865 4866- (void)stopSpeaking:(id)sender 4867{ 4868 COMMAND_PROLOGUE 4869 4870 [NSApp stopSpeaking:sender]; 4871} 4872 4873- (void)toggleBaseWritingDirection:(id)sender 4874{ 4875 COMMAND_PROLOGUE 4876 4877 if (![self _canEdit]) 4878 return; 4879 4880 Frame* coreFrame = core([self _frame]); 4881 if (!coreFrame) 4882 return; 4883 4884 WritingDirection direction = RightToLeftWritingDirection; 4885 switch (coreFrame->editor()->baseWritingDirectionForSelectionStart()) { 4886 case NSWritingDirectionLeftToRight: 4887 break; 4888 case NSWritingDirectionRightToLeft: 4889 direction = LeftToRightWritingDirection; 4890 break; 4891 // The writingDirectionForSelectionStart method will never return "natural". It 4892 // will always return a concrete direction. So, keep the compiler happy, and assert not reached. 4893 case NSWritingDirectionNatural: 4894 ASSERT_NOT_REACHED(); 4895 break; 4896 } 4897 4898 if (Frame* coreFrame = core([self _frame])) 4899 coreFrame->editor()->setBaseWritingDirection(direction); 4900} 4901 4902- (void)changeBaseWritingDirection:(id)sender 4903{ 4904 COMMAND_PROLOGUE 4905 4906 if (![self _canEdit]) 4907 return; 4908 4909 NSWritingDirection writingDirection = static_cast<NSWritingDirection>([sender tag]); 4910 4911 // We disable the menu item that performs this action because we can't implement 4912 // NSWritingDirectionNatural's behavior using CSS. 4913 ASSERT(writingDirection != NSWritingDirectionNatural); 4914 4915 if (Frame* coreFrame = core([self _frame])) 4916 coreFrame->editor()->setBaseWritingDirection(writingDirection == NSWritingDirectionLeftToRight ? LeftToRightWritingDirection : RightToLeftWritingDirection); 4917} 4918 4919static BOOL writingDirectionKeyBindingsEnabled() 4920{ 4921#if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) 4922 return YES; 4923#else 4924 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; 4925 return [defaults boolForKey:@"NSAllowsBaseWritingDirectionKeyBindings"] || [defaults boolForKey:@"AppleTextDirection"]; 4926#endif 4927} 4928 4929- (void)_changeBaseWritingDirectionTo:(NSWritingDirection)direction 4930{ 4931 if (![self _canEdit]) 4932 return; 4933 4934 static BOOL bindingsEnabled = writingDirectionKeyBindingsEnabled(); 4935 4936 if (!bindingsEnabled) { 4937 NSBeep(); 4938 return; 4939 } 4940 4941 if (Frame* coreFrame = core([self _frame])) 4942 coreFrame->editor()->setBaseWritingDirection(direction == NSWritingDirectionLeftToRight ? LeftToRightWritingDirection : RightToLeftWritingDirection); 4943} 4944 4945- (void)makeBaseWritingDirectionLeftToRight:(id)sender 4946{ 4947 COMMAND_PROLOGUE 4948 4949 [self _changeBaseWritingDirectionTo:NSWritingDirectionLeftToRight]; 4950} 4951 4952- (void)makeBaseWritingDirectionRightToLeft:(id)sender 4953{ 4954 COMMAND_PROLOGUE 4955 4956 [self _changeBaseWritingDirectionTo:NSWritingDirectionRightToLeft]; 4957} 4958 4959#if defined(BUILDING_ON_TIGER) || defined(BUILDING_ON_LEOPARD) 4960- (void)changeBaseWritingDirectionToLTR:(id)sender 4961{ 4962 [self makeBaseWritingDirectionLeftToRight:sender]; 4963} 4964 4965- (void)changeBaseWritingDirectionToRTL:(id)sender 4966{ 4967 [self makeBaseWritingDirectionRightToLeft:sender]; 4968} 4969#endif 4970 4971- (void)makeBaseWritingDirectionNatural:(id)sender 4972{ 4973 LOG_ERROR("Sent from %@.", sender); 4974} 4975 4976#if 0 4977 4978// CSS does not have a way to specify an outline font, which may make this difficult to implement. 4979// Maybe a special case of text-shadow? 4980- (void)outline:(id)sender; 4981 4982// This is part of table support, which may be in NSTextView for Tiger. 4983// It's probably simple to do the equivalent thing for WebKit. 4984- (void)insertTable:(id)sender; 4985 4986// This could be important. 4987- (void)toggleTraditionalCharacterShape:(id)sender; 4988 4989// I'm not sure what the equivalents of these in the web world are. 4990- (void)insertLineSeparator:(id)sender; 4991- (void)insertPageBreak:(id)sender; 4992 4993// These methods are not implemented in NSTextView yet at the time of this writing. 4994- (void)changeCaseOfLetter:(id)sender; 4995- (void)transposeWords:(id)sender; 4996 4997#endif 4998 4999#ifndef BUILDING_ON_TIGER 5000 5001// Override this so that AppKit will send us arrow keys as key down events so we can 5002// support them via the key bindings mechanism. 5003- (BOOL)_wantsKeyDownForEvent:(NSEvent *)event 5004{ 5005 bool haveWebCoreFrame = core([self _frame]); 5006 5007 // If we have a frame, our keyDown method will handle key bindings after sending 5008 // the event through the DOM, so ask AppKit not to do its early special key binding 5009 // mapping. If we don't have a frame, just let things work the normal way without 5010 // a keyDown. 5011 return haveWebCoreFrame; 5012} 5013 5014#else 5015 5016// Super-hack alert. 5017// All this code accomplishes the same thing as the _wantsKeyDownForEvent method above. 5018 5019// Returns a selector only if called while: 5020// 1) first responder is self 5021// 2) handling a key down event 5022// 3) not yet inside keyDown: method 5023// 4) key is an arrow key 5024// The selector is the one that gets sent by -[NSWindow _processKeyboardUIKey] for this key. 5025- (SEL)_arrowKeyDownEventSelectorIfPreprocessing 5026{ 5027 NSWindow *w = [self window]; 5028 if ([w firstResponder] != self) 5029 return NULL; 5030 NSEvent *e = [w currentEvent]; 5031 if ([e type] != NSKeyDown) 5032 return NULL; 5033 if (e == _private->keyDownEvent) 5034 return NULL; 5035 NSString *s = [e charactersIgnoringModifiers]; 5036 if ([s length] == 0) 5037 return NULL; 5038 switch ([s characterAtIndex:0]) { 5039 case NSDownArrowFunctionKey: 5040 return @selector(moveDown:); 5041 case NSLeftArrowFunctionKey: 5042 return @selector(moveLeft:); 5043 case NSRightArrowFunctionKey: 5044 return @selector(moveRight:); 5045 case NSUpArrowFunctionKey: 5046 return @selector(moveUp:); 5047 default: 5048 return NULL; 5049 } 5050} 5051 5052// Returns NO instead of YES if called on the selector that the 5053// _arrowKeyDownEventSelectorIfPreprocessing method returns. 5054// This should only happen inside -[NSWindow _processKeyboardUIKey], 5055// and together with the change below should cause that method 5056// to return NO rather than handling the key. 5057// Also set a 1-shot flag for the nextResponder check below. 5058- (BOOL)respondsToSelector:(SEL)selector 5059{ 5060 if (![super respondsToSelector:selector]) 5061 return NO; 5062 SEL arrowKeySelector = [self _arrowKeyDownEventSelectorIfPreprocessing]; 5063 if (selector != arrowKeySelector) 5064 return YES; 5065 _private->nextResponderDisabledOnce = YES; 5066 return NO; 5067} 5068 5069// Returns nil instead of the next responder if called when the 5070// one-shot flag is set, and _arrowKeyDownEventSelectorIfPreprocessing 5071// returns something other than NULL. This should only happen inside 5072// -[NSWindow _processKeyboardUIKey] and together with the change above 5073// should cause that method to return NO rather than handling the key. 5074- (NSResponder *)nextResponder 5075{ 5076 BOOL disabled = _private->nextResponderDisabledOnce; 5077 _private->nextResponderDisabledOnce = NO; 5078 if (disabled && [self _arrowKeyDownEventSelectorIfPreprocessing] != NULL) 5079 return nil; 5080 return [super nextResponder]; 5081} 5082 5083#endif 5084 5085- (void)_updateControlTints 5086{ 5087 Frame* frame = core([self _frame]); 5088 if (!frame) 5089 return; 5090 FrameView* view = frame->view(); 5091 if (!view) 5092 return; 5093 view->updateControlTints(); 5094} 5095 5096// Despite its name, this is called at different times than windowDidBecomeKey is. 5097// It takes into account all the other factors that determine when NSCell draws 5098// with different tints, so it's the right call to use for control tints. We'd prefer 5099// to do this with API. <rdar://problem/5136760> 5100- (void)_windowChangedKeyState 5101{ 5102 if (pthread_main_np()) 5103 [self _updateControlTints]; 5104 else 5105 [self performSelectorOnMainThread:@selector(_updateControlTints) withObject:nil waitUntilDone:NO]; 5106 5107 [super _windowChangedKeyState]; 5108} 5109 5110- (void)otherMouseDown:(NSEvent *)event 5111{ 5112 if ([event buttonNumber] == 2) 5113 [self mouseDown:event]; 5114 else 5115 [super otherMouseDown:event]; 5116} 5117 5118- (void)otherMouseDragged:(NSEvent *)event 5119{ 5120 if ([event buttonNumber] == 2) 5121 [self mouseDragged:event]; 5122 else 5123 [super otherMouseDragged:event]; 5124} 5125 5126- (void)otherMouseUp:(NSEvent *)event 5127{ 5128 if ([event buttonNumber] == 2) 5129 [self mouseUp:event]; 5130 else 5131 [super otherMouseUp:event]; 5132} 5133 5134@end 5135 5136@implementation WebHTMLView (WebInternal) 5137 5138- (void)_selectionChanged 5139{ 5140 [self _updateSelectionForInputManager]; 5141 [self _updateFontPanel]; 5142 if (Frame* coreFrame = core([self _frame])) 5143 coreFrame->editor()->setStartNewKillRingSequence(true); 5144} 5145 5146- (void)_updateFontPanel 5147{ 5148 // FIXME: NSTextView bails out if becoming or resigning first responder, for which it has ivar flags. Not 5149 // sure if we need to do something similar. 5150 5151 if (![self _canEdit]) 5152 return; 5153 5154 NSWindow *window = [self window]; 5155 // FIXME: is this first-responder check correct? What happens if a subframe is editable and is first responder? 5156 if (![window isKeyWindow] || [window firstResponder] != self) 5157 return; 5158 5159 bool multipleFonts = false; 5160 NSFont *font = nil; 5161 if (Frame* coreFrame = core([self _frame])) { 5162 if (const SimpleFontData* fd = coreFrame->editor()->fontForSelection(multipleFonts)) 5163 font = fd->getNSFont(); 5164 } 5165 5166 // FIXME: for now, return a bogus font that distinguishes the empty selection from the non-empty 5167 // selection. We should be able to remove this once the rest of this code works properly. 5168 if (font == nil) 5169 font = [self _hasSelection] ? [NSFont menuFontOfSize:23] : [NSFont toolTipsFontOfSize:17]; 5170 ASSERT(font != nil); 5171 5172 [[NSFontManager sharedFontManager] setSelectedFont:font isMultiple:multipleFonts]; 5173 5174 // FIXME: we don't keep track of selected attributes, or set them on the font panel. This 5175 // appears to have no effect on the UI. E.g., underlined text in Mail or TextEdit is 5176 // not reflected in the font panel. Maybe someday this will change. 5177} 5178 5179- (BOOL)_canSmartCopyOrDelete 5180{ 5181 if (![[self _webView] smartInsertDeleteEnabled]) 5182 return NO; 5183 Frame* coreFrame = core([self _frame]); 5184 return coreFrame && coreFrame->selection()->granularity() == WordGranularity; 5185} 5186 5187- (NSEvent *)_mouseDownEvent 5188{ 5189 return _private->mouseDownEvent; 5190} 5191 5192- (id<WebHTMLHighlighter>)_highlighterForType:(NSString*)type 5193{ 5194 return [_private->highlighters objectForKey:type]; 5195} 5196 5197- (WebFrame *)_frame 5198{ 5199 return [_private->dataSource webFrame]; 5200} 5201 5202- (void)closeIfNotCurrentView 5203{ 5204 if ([[[self _frame] frameView] documentView] != self) 5205 [self close]; 5206} 5207 5208- (DOMDocumentFragment*)_documentFragmentFromPasteboard:(NSPasteboard *)pasteboard 5209{ 5210 return [self _documentFragmentFromPasteboard:pasteboard inContext:nil allowPlainText:NO]; 5211} 5212 5213#ifndef BUILDING_ON_TIGER 5214 5215- (BOOL)isGrammarCheckingEnabled 5216{ 5217 // FIXME 4799134: WebView is the bottleneck for this grammar-checking logic, but we must implement the method here because 5218 // the AppKit code checks the first responder. 5219 return [[self _webView] isGrammarCheckingEnabled]; 5220} 5221 5222- (void)setGrammarCheckingEnabled:(BOOL)flag 5223{ 5224 // FIXME 4799134: WebView is the bottleneck for this grammar-checking logic, but we must implement the method here because 5225 // the AppKit code checks the first responder. 5226 [[self _webView] setGrammarCheckingEnabled:flag]; 5227} 5228 5229- (void)toggleGrammarChecking:(id)sender 5230{ 5231 // FIXME 4799134: WebView is the bottleneck for this grammar-checking logic, but we must implement the method here because 5232 // the AppKit code checks the first responder. 5233 [[self _webView] toggleGrammarChecking:sender]; 5234} 5235 5236 5237static CGPoint coreGraphicsScreenPointForAppKitScreenPoint(NSPoint point) 5238{ 5239 NSArray *screens = [NSScreen screens]; 5240 5241 if ([screens count] == 0) { 5242 // You could theoretically get here if running with no monitor, in which case it doesn't matter 5243 // much where the "on-screen" point is. 5244 return CGPointMake(point.x, point.y); 5245 } 5246 5247 // Flip the y coordinate from the top of the menu bar screen -- see 4636390 5248 return CGPointMake(point.x, NSMaxY([[screens objectAtIndex:0] frame]) - point.y); 5249} 5250 5251#endif 5252 5253#if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) 5254 5255- (void)orderFrontSubstitutionsPanel:(id)sender 5256{ 5257 COMMAND_PROLOGUE 5258 5259 NSSpellChecker *checker = [NSSpellChecker sharedSpellChecker]; 5260 if (!checker) { 5261 LOG_ERROR("No NSSpellChecker"); 5262 return; 5263 } 5264 5265 NSPanel *substitutionsPanel = [checker substitutionsPanel]; 5266 if ([substitutionsPanel isVisible]) { 5267 [substitutionsPanel orderOut:sender]; 5268 return; 5269 } 5270 [substitutionsPanel orderFront:sender]; 5271} 5272 5273// FIXME 4799134: WebView is the bottleneck for this logic, but we must implement these methods here because 5274// the AppKit code checks the first responder. 5275 5276- (BOOL)smartInsertDeleteEnabled 5277{ 5278 return [[self _webView] smartInsertDeleteEnabled]; 5279} 5280 5281- (void)setSmartInsertDeleteEnabled:(BOOL)flag 5282{ 5283 [[self _webView] setSmartInsertDeleteEnabled:flag]; 5284} 5285 5286- (void)toggleSmartInsertDelete:(id)sender 5287{ 5288 [[self _webView] toggleSmartInsertDelete:sender]; 5289} 5290 5291- (BOOL)isAutomaticQuoteSubstitutionEnabled 5292{ 5293 return [[self _webView] isAutomaticQuoteSubstitutionEnabled]; 5294} 5295 5296- (void)setAutomaticQuoteSubstitutionEnabled:(BOOL)flag 5297{ 5298 [[self _webView] setAutomaticQuoteSubstitutionEnabled:flag]; 5299} 5300 5301- (void)toggleAutomaticQuoteSubstitution:(id)sender 5302{ 5303 [[self _webView] toggleAutomaticQuoteSubstitution:sender]; 5304} 5305 5306- (BOOL)isAutomaticLinkDetectionEnabled 5307{ 5308 return [[self _webView] isAutomaticLinkDetectionEnabled]; 5309} 5310 5311- (void)setAutomaticLinkDetectionEnabled:(BOOL)flag 5312{ 5313 [[self _webView] setAutomaticLinkDetectionEnabled:flag]; 5314} 5315 5316- (void)toggleAutomaticLinkDetection:(id)sender 5317{ 5318 [[self _webView] toggleAutomaticLinkDetection:sender]; 5319} 5320 5321- (BOOL)isAutomaticDashSubstitutionEnabled 5322{ 5323 return [[self _webView] isAutomaticDashSubstitutionEnabled]; 5324} 5325 5326- (void)setAutomaticDashSubstitutionEnabled:(BOOL)flag 5327{ 5328 [[self _webView] setAutomaticDashSubstitutionEnabled:flag]; 5329} 5330 5331- (void)toggleAutomaticDashSubstitution:(id)sender 5332{ 5333 [[self _webView] toggleAutomaticDashSubstitution:sender]; 5334} 5335 5336- (BOOL)isAutomaticTextReplacementEnabled 5337{ 5338 return [[self _webView] isAutomaticTextReplacementEnabled]; 5339} 5340 5341- (void)setAutomaticTextReplacementEnabled:(BOOL)flag 5342{ 5343 [[self _webView] setAutomaticTextReplacementEnabled:flag]; 5344} 5345 5346- (void)toggleAutomaticTextReplacement:(id)sender 5347{ 5348 [[self _webView] toggleAutomaticTextReplacement:sender]; 5349} 5350 5351- (BOOL)isAutomaticSpellingCorrectionEnabled 5352{ 5353 return [[self _webView] isAutomaticSpellingCorrectionEnabled]; 5354} 5355 5356- (void)setAutomaticSpellingCorrectionEnabled:(BOOL)flag 5357{ 5358 [[self _webView] setAutomaticSpellingCorrectionEnabled:flag]; 5359} 5360 5361- (void)toggleAutomaticSpellingCorrection:(id)sender 5362{ 5363 [[self _webView] toggleAutomaticSpellingCorrection:sender]; 5364} 5365 5366#endif 5367 5368- (void)_lookUpInDictionaryFromMenu:(id)sender 5369{ 5370 // Dictionary API will accept a whitespace-only string and display UI as if it were real text, 5371 // so bail out early to avoid that. 5372 if ([[[self selectedString] _webkit_stringByTrimmingWhitespace] length] == 0) 5373 return; 5374 5375 NSAttributedString *attrString = [self selectedAttributedString]; 5376 5377 Frame* coreFrame = core([self _frame]); 5378 if (!coreFrame) 5379 return; 5380 5381 NSRect rect = coreFrame->selection()->bounds(); 5382 5383#ifndef BUILDING_ON_TIGER 5384 NSDictionary *attributes = [attrString fontAttributesInRange:NSMakeRange(0,1)]; 5385 NSFont *font = [attributes objectForKey:NSFontAttributeName]; 5386 if (font) 5387 rect.origin.y += [font ascender]; 5388#endif 5389 5390#if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) 5391 [self showDefinitionForAttributedString:attrString atPoint:rect.origin]; 5392 return; 5393#endif 5394 5395 // We soft link to get the function that displays the dictionary (either pop-up window or app) to avoid the performance 5396 // penalty of linking to another framework. This function changed signature as well as framework between Tiger and Leopard, 5397 // so the two cases are handled separately. 5398 5399#ifdef BUILDING_ON_TIGER 5400 typedef OSStatus (*ServiceWindowShowFunction)(id inWordString, NSRect inWordBoundary, UInt16 inLineDirection); 5401 const char *frameworkPath = "/System/Library/Frameworks/ApplicationServices.framework/Frameworks/LangAnalysis.framework/LangAnalysis"; 5402 const char *functionName = "DCMDictionaryServiceWindowShow"; 5403#else 5404 typedef void (*ServiceWindowShowFunction)(id unusedDictionaryRef, id inWordString, CFRange selectionRange, id unusedFont, CGPoint textOrigin, Boolean verticalText, id unusedTransform); 5405 const char *frameworkPath = "/System/Library/Frameworks/Carbon.framework/Frameworks/HIToolbox.framework/HIToolbox"; 5406 const char *functionName = "HIDictionaryWindowShow"; 5407#endif 5408 5409 static bool lookedForFunction = false; 5410 static ServiceWindowShowFunction dictionaryServiceWindowShow = NULL; 5411 5412 if (!lookedForFunction) { 5413 void* langAnalysisFramework = dlopen(frameworkPath, RTLD_LAZY); 5414 ASSERT(langAnalysisFramework); 5415 if (langAnalysisFramework) 5416 dictionaryServiceWindowShow = (ServiceWindowShowFunction)dlsym(langAnalysisFramework, functionName); 5417 lookedForFunction = true; 5418 } 5419 5420 ASSERT(dictionaryServiceWindowShow); 5421 if (!dictionaryServiceWindowShow) { 5422 NSLog(@"Couldn't find the %s function in %s", functionName, frameworkPath); 5423 return; 5424 } 5425 5426#ifdef BUILDING_ON_TIGER 5427 // FIXME: must check for right-to-left here 5428 NSWritingDirection writingDirection = NSWritingDirectionLeftToRight; 5429 5430 // FIXME: the dictionary API expects the rect for the first line of selection. Passing 5431 // the rect for the entire selection, as we do here, positions the pop-up window near 5432 // the bottom of the selection rather than at the selected word. 5433 rect = [self convertRect:rect toView:nil]; 5434 rect.origin = [[self window] convertBaseToScreen:rect.origin]; 5435 NSData *data = [attrString RTFFromRange:NSMakeRange(0, [attrString length]) documentAttributes:nil]; 5436 dictionaryServiceWindowShow(data, rect, (writingDirection == NSWritingDirectionRightToLeft) ? 1 : 0); 5437#else 5438 // The HIDictionaryWindowShow function requires the origin, in CG screen coordinates, of the first character of text in the selection. 5439 // FIXME 4945808: We approximate this in a way that works well when a single word is selected, and less well in some other cases 5440 // (but no worse than we did in Tiger) 5441 NSPoint windowPoint = [self convertPoint:rect.origin toView:nil]; 5442 NSPoint screenPoint = [[self window] convertBaseToScreen:windowPoint]; 5443 5444 dictionaryServiceWindowShow(nil, attrString, CFRangeMake(0, [attrString length]), nil, 5445 coreGraphicsScreenPointForAppKitScreenPoint(screenPoint), false, nil); 5446#endif 5447} 5448 5449- (void)_hoverFeedbackSuspendedChanged 5450{ 5451 [self _updateMouseoverWithFakeEvent]; 5452} 5453 5454- (void)_executeSavedKeypressCommands 5455{ 5456 WebHTMLViewInterpretKeyEventsParameters* parameters = _private->interpretKeyEventsParameters; 5457 if (!parameters || parameters->event->keypressCommands().isEmpty()) 5458 return; 5459 5460 // We could be called again if the execution of one command triggers a call to selectedRange. 5461 // In this case, the state is up to date, and we don't need to execute any more saved commands to return a result 5462 if (parameters->executingSavedKeypressCommands) 5463 return; 5464 5465 // Avoid an infinite loop that would occur if executing a command appended it to event->keypressCommands() again. 5466 bool wasSavingCommands = parameters->shouldSaveCommands; 5467 parameters->shouldSaveCommands = false; 5468 parameters->executingSavedKeypressCommands = true; 5469 5470 const Vector<KeypressCommand>& commands = parameters->event->keypressCommands(); 5471 5472 for (size_t i = 0; i < commands.size(); ++i) { 5473 if (commands[i].commandName == "insertText:") 5474 [self insertText:commands[i].text]; 5475 else if (commands[i].commandName == "noop:") 5476 ; // Do nothing. This case can be removed once <rdar://problem/9025012> is fixed. 5477 else 5478 [self doCommandBySelector:NSSelectorFromString(commands[i].commandName)]; 5479 } 5480 parameters->event->keypressCommands().clear(); 5481 parameters->shouldSaveCommands = wasSavingCommands; 5482 parameters->executingSavedKeypressCommands = false; 5483} 5484 5485- (BOOL)_interpretKeyEvent:(KeyboardEvent*)event savingCommands:(BOOL)savingCommands 5486{ 5487 ASSERT(core([self _frame]) == event->target()->toNode()->document()->frame()); 5488 ASSERT(!savingCommands || event->keypressCommands().isEmpty()); // Save commands once for each event. 5489 5490 WebHTMLViewInterpretKeyEventsParameters parameters; 5491 parameters.eventInterpretationHadSideEffects = false; 5492 parameters.shouldSaveCommands = savingCommands; 5493 parameters.executingSavedKeypressCommands = false; 5494 // If we're intercepting the initial IM call we assume that the IM has consumed the event, 5495 // and only change this assumption if one of the NSTextInput/Responder callbacks is used. 5496 // We assume the IM will *not* consume hotkey sequences 5497 parameters.consumedByIM = savingCommands && !event->metaKey(); 5498 5499 const PlatformKeyboardEvent* platformEvent = event->keyEvent(); 5500 if (!platformEvent) 5501 return NO; 5502 5503 NSEvent *macEvent = platformEvent->macEvent(); 5504 if ([macEvent type] == NSKeyDown && [_private->completionController filterKeyDown:macEvent]) 5505 return YES; 5506 5507 if ([macEvent type] == NSFlagsChanged) 5508 return NO; 5509 5510 parameters.event = event; 5511 _private->interpretKeyEventsParameters = ¶meters; 5512 const Vector<KeypressCommand>& commands = event->keypressCommands(); 5513 5514 if (savingCommands) { 5515 // AppKit will respond with a series of NSTextInput protocol method calls. There are three groups that we heuristically differentiate: 5516 // 1. Key Bindings. Only doCommandBySelector: and insertText: calls will be made, which we save in the event for execution 5517 // after DOM dispatch. This is safe, because neither returns a result, so there is no branching on AppKit side. 5518 // 2. Plain text input. Here as well, we need to dispatch DOM events prior to inserting text, so we save the insertText: command. 5519 // 3. Input method processing. An IM can make any NSTextInput calls, and can base its decisions on results it gets, so we must 5520 // execute the calls immediately. DOM events like keydown are tweaked to have keyCode of 229, and canceling them has no effect. 5521 // Unfortunately, there is no real difference between plain text input and IM processing - for example, AppKit queries hasMarkedText 5522 // when typing with U.S. keyboard, and inserts marked text for dead keys. 5523 [self interpretKeyEvents:[NSArray arrayWithObject:macEvent]]; 5524 } else { 5525 // Are there commands that could just cause text insertion if executed via Editor? 5526 // WebKit doesn't have enough information about mode to decide how they should be treated, so we leave it upon WebCore 5527 // to either handle them immediately (e.g. Tab that changes focus) or let a keypress event be generated 5528 // (e.g. Tab that inserts a Tab character, or Enter). 5529 bool haveTextInsertionCommands = false; 5530 for (size_t i = 0; i < commands.size(); ++i) { 5531 if ([self coreCommandBySelector:NSSelectorFromString(commands[i].commandName)].isTextInsertion()) 5532 haveTextInsertionCommands = true; 5533 } 5534 // If there are no text insertion commands, default keydown handler is the right time to execute the commands. 5535 // Keypress (Char event) handler is the latest opportunity to execute. 5536 if (!haveTextInsertionCommands || platformEvent->type() == PlatformKeyboardEvent::Char) 5537 [self _executeSavedKeypressCommands]; 5538 } 5539 _private->interpretKeyEventsParameters = 0; 5540 5541 // An input method may make several actions per keypress. For example, pressing Return with Korean IM both confirms it and sends a newline. 5542 // IM-like actions are handled immediately (so parameters.eventInterpretationHadSideEffects is true), but there are saved commands that 5543 // should be handled like normal text input after DOM event dispatch. 5544 if (!event->keypressCommands().isEmpty()) 5545 return NO; 5546 5547 // An input method may consume an event and not tell us (e.g. when displaying a candidate window), 5548 // in which case we should not bubble the event up the DOM. 5549 if (parameters.consumedByIM) 5550 return YES; 5551 5552 // If we have already executed all commands, don't do it again. 5553 return parameters.eventInterpretationHadSideEffects; 5554} 5555 5556- (WebCore::CachedImage*)promisedDragTIFFDataSource 5557{ 5558 return _private->promisedDragTIFFDataSource; 5559} 5560 5561- (void)setPromisedDragTIFFDataSource:(WebCore::CachedImage*)source 5562{ 5563 if (source) 5564 source->addClient(promisedDataClient()); 5565 5566 if (_private->promisedDragTIFFDataSource) 5567 _private->promisedDragTIFFDataSource->removeClient(promisedDataClient()); 5568 _private->promisedDragTIFFDataSource = source; 5569} 5570 5571#undef COMMAND_PROLOGUE 5572 5573- (void)_layoutIfNeeded 5574{ 5575 ASSERT(!_private->subviewsSetAside); 5576 5577 if ([self _needsLayout]) 5578 [self layout]; 5579} 5580 5581- (void)_web_updateLayoutAndStyleIfNeededRecursive 5582{ 5583 WebFrame *webFrame = [self _frame]; 5584 Frame* coreFrame = core(webFrame); 5585 if (coreFrame && coreFrame->view()) 5586 coreFrame->view()->updateLayoutAndStyleIfNeededRecursive(); 5587} 5588 5589- (void) _destroyAllWebPlugins 5590{ 5591 [[self _pluginController] destroyAllPlugins]; 5592} 5593 5594- (BOOL)_needsLayout 5595{ 5596 return [[self _frame] _needsLayout]; 5597} 5598 5599#if USE(ACCELERATED_COMPOSITING) 5600- (void)attachRootLayer:(CALayer*)layer 5601{ 5602 if (!_private->layerHostingView) { 5603 NSView* hostingView = [[NSView alloc] initWithFrame:[self bounds]]; 5604#if !defined(BUILDING_ON_LEOPARD) 5605 [hostingView setAutoresizingMask:(NSViewWidthSizable | NSViewHeightSizable)]; 5606#endif 5607 [self addSubview:hostingView]; 5608 [hostingView release]; 5609 // hostingView is owned by being a subview of self 5610 _private->layerHostingView = hostingView; 5611 } 5612 5613 // Make a container layer, which will get sized/positioned by AppKit and CA. 5614 CALayer* viewLayer = [CALayer layer]; 5615 5616#if defined(BUILDING_ON_LEOPARD) 5617 // Turn off default animations. 5618 NSNull *nullValue = [NSNull null]; 5619 NSDictionary *actions = [NSDictionary dictionaryWithObjectsAndKeys: 5620 nullValue, @"anchorPoint", 5621 nullValue, @"bounds", 5622 nullValue, @"contents", 5623 nullValue, @"contentsRect", 5624 nullValue, @"opacity", 5625 nullValue, @"position", 5626 nullValue, @"sublayerTransform", 5627 nullValue, @"sublayers", 5628 nullValue, @"transform", 5629 nil]; 5630 [viewLayer setStyle:[NSDictionary dictionaryWithObject:actions forKey:@"actions"]]; 5631#endif 5632 5633#if !defined(BUILDING_ON_LEOPARD) 5634 // If we aren't in the window yet, we'll use the screen's scale factor now, and reset the scale 5635 // via -viewDidMoveToWindow. 5636 NSWindow *window = [self window]; 5637 CGFloat scaleFactor; 5638#if !defined(BUILDING_ON_SNOW_LEOPARD) 5639 if (window) 5640 scaleFactor = [window backingScaleFactor]; 5641 else 5642 scaleFactor = [[NSScreen mainScreen] backingScaleFactor]; 5643#else 5644 if (window) 5645 scaleFactor = [window userSpaceScaleFactor]; 5646 else 5647 scaleFactor = [[NSScreen mainScreen] userSpaceScaleFactor]; 5648#endif 5649 5650 [viewLayer setTransform:CATransform3DMakeScale(scaleFactor, scaleFactor, 1)]; 5651#endif 5652 5653 if ([self layer]) { 5654 // If we are in a layer-backed view, we need to manually initialize the geometry for our layer. 5655 [viewLayer setBounds:NSRectToCGRect([_private->layerHostingView bounds])]; 5656 [viewLayer setAnchorPoint:CGPointMake(0, [self isFlipped] ? 1 : 0)]; 5657 CGPoint layerPosition = NSPointToCGPoint([self convertPointToBase:[_private->layerHostingView frame].origin]); 5658 [viewLayer setPosition:layerPosition]; 5659 } 5660 5661 [_private->layerHostingView setLayer:viewLayer]; 5662 [_private->layerHostingView setWantsLayer:YES]; 5663 5664 // Parent our root layer in the container layer 5665 [viewLayer addSublayer:layer]; 5666 5667 if ([[self _webView] _postsAcceleratedCompositingNotifications]) 5668 [[NSNotificationCenter defaultCenter] postNotificationName:_WebViewDidStartAcceleratedCompositingNotification object:[self _webView] userInfo:nil]; 5669 5670#if defined(BUILDING_ON_LEOPARD) 5671 [viewLayer setSublayerTransform:CATransform3DMakeScale(1, -1, 1)]; // setGeometryFlipped: doesn't exist on Leopard. 5672 [self _updateLayerHostingViewPosition]; 5673#else 5674 // Do geometry flipping here, which flips all the compositing layers so they are top-down. 5675 [viewLayer setGeometryFlipped:YES]; 5676#endif 5677} 5678 5679- (void)detachRootLayer 5680{ 5681 if (_private->layerHostingView) { 5682 [_private->layerHostingView setLayer:nil]; 5683 [_private->layerHostingView setWantsLayer:NO]; 5684 [_private->layerHostingView removeFromSuperview]; 5685 _private->layerHostingView = nil; 5686 } 5687} 5688 5689#if defined(BUILDING_ON_LEOPARD) 5690// This method is necessary on Leopard to work around <rdar://problem/7067892>. 5691- (void)_updateLayerHostingViewPosition 5692{ 5693 if (!_private->layerHostingView) 5694 return; 5695 5696 const CGFloat maxHeight = 2048; 5697 NSRect layerViewFrame = [self bounds]; 5698 5699 if (layerViewFrame.size.height > maxHeight) { 5700 // Clamp the size of the view to <= maxHeight to avoid the bug. 5701 layerViewFrame.size.height = maxHeight; 5702 NSRect visibleRect = [[self enclosingScrollView] documentVisibleRect]; 5703 5704 // Place the top of the layer-hosting view at the top of the visibleRect. 5705 CGFloat topOffset = NSMinY(visibleRect); 5706 layerViewFrame.origin.y = topOffset; 5707 5708 // Compensate for the moved view by adjusting the sublayer transform on the view's layer (using flipped coords). 5709 CATransform3D flipTransform = CATransform3DMakeTranslation(0, topOffset, 0); 5710 flipTransform = CATransform3DScale(flipTransform, 1, -1, 1); 5711 [[_private->layerHostingView layer] setSublayerTransform:flipTransform]; 5712 } 5713 5714 [_private->layerHostingView _updateLayerGeometryFromView]; // Workaround for <rdar://problem/7071636> 5715 [_private->layerHostingView setFrame:layerViewFrame]; 5716} 5717#endif // defined(BUILDING_ON_LEOPARD) 5718 5719- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx 5720{ 5721 if (_private) { 5722 ASSERT(!_private->drawingIntoLayer); 5723 _private->drawingIntoLayer = YES; 5724 } 5725 5726 [super drawLayer:layer inContext:ctx]; 5727 5728 if (_private) 5729 _private->drawingIntoLayer = NO; 5730} 5731 5732- (BOOL)_web_isDrawingIntoLayer 5733{ 5734 return _private->drawingIntoLayer; 5735} 5736 5737#endif // USE(ACCELERATED_COMPOSITING) 5738 5739@end 5740 5741@implementation WebHTMLView (WebNSTextInputSupport) 5742 5743- (NSArray *)validAttributesForMarkedText 5744{ 5745 static NSArray *validAttributes; 5746 if (!validAttributes) { 5747 validAttributes = [[NSArray alloc] initWithObjects: 5748 NSUnderlineStyleAttributeName, NSUnderlineColorAttributeName, 5749 NSMarkedClauseSegmentAttributeName, NSTextInputReplacementRangeAttributeName, nil]; 5750 // NSText also supports the following attributes, but it's 5751 // hard to tell which are really required for text input to 5752 // work well; I have not seen any input method make use of them yet. 5753 // NSFontAttributeName, NSForegroundColorAttributeName, 5754 // NSBackgroundColorAttributeName, NSLanguageAttributeName. 5755 CFRetain(validAttributes); 5756 } 5757 LOG(TextInput, "validAttributesForMarkedText -> (...)"); 5758 return validAttributes; 5759} 5760 5761- (NSTextInputContext *)inputContext 5762{ 5763 return _private->exposeInputContext ? [super inputContext] : nil; 5764} 5765 5766- (NSAttributedString *)textStorage 5767{ 5768 if (!_private->exposeInputContext) { 5769 LOG(TextInput, "textStorage -> nil"); 5770 return nil; 5771 } 5772 NSAttributedString *result = [self attributedSubstringFromRange:NSMakeRange(0, UINT_MAX)]; 5773 5774 LOG(TextInput, "textStorage -> \"%@\"", result ? [result string] : @""); 5775 5776 // We have to return an empty string rather than null to prevent TSM from calling -string 5777 return result ? result : [[[NSAttributedString alloc] initWithString:@""] autorelease]; 5778} 5779 5780- (NSUInteger)characterIndexForPoint:(NSPoint)thePoint 5781{ 5782 [self _executeSavedKeypressCommands]; 5783 5784 NSWindow *window = [self window]; 5785 WebFrame *frame = [self _frame]; 5786 5787 if (window) 5788 thePoint = [window convertScreenToBase:thePoint]; 5789 thePoint = [self convertPoint:thePoint fromView:nil]; 5790 5791 DOMRange *range = [frame _characterRangeAtPoint:thePoint]; 5792 if (!range) { 5793 LOG(TextInput, "characterIndexForPoint:(%f, %f) -> NSNotFound", thePoint.x, thePoint.y); 5794 return NSNotFound; 5795 } 5796 5797 unsigned result = [frame _convertDOMRangeToNSRange:range].location; 5798 LOG(TextInput, "characterIndexForPoint:(%f, %f) -> %u", thePoint.x, thePoint.y, result); 5799 return result; 5800} 5801 5802- (NSRect)firstRectForCharacterRange:(NSRange)theRange 5803{ 5804 [self _executeSavedKeypressCommands]; 5805 5806 WebFrame *frame = [self _frame]; 5807 5808 // Just to match NSTextView's behavior. Regression tests cannot detect this; 5809 // to reproduce, use a test application from http://bugs.webkit.org/show_bug.cgi?id=4682 5810 // (type something; try ranges (1, -1) and (2, -1). 5811 if ((theRange.location + theRange.length < theRange.location) && (theRange.location + theRange.length != 0)) 5812 theRange.length = 0; 5813 5814 DOMRange *range = [frame _convertNSRangeToDOMRange:theRange]; 5815 if (!range) { 5816 LOG(TextInput, "firstRectForCharacterRange:(%u, %u) -> (0, 0, 0, 0)", theRange.location, theRange.length); 5817 return NSMakeRect(0, 0, 0, 0); 5818 } 5819 5820 ASSERT([range startContainer]); 5821 ASSERT([range endContainer]); 5822 5823 NSRect resultRect = [frame _firstRectForDOMRange:range]; 5824 resultRect = [self convertRect:resultRect toView:nil]; 5825 5826 NSWindow *window = [self window]; 5827 if (window) 5828 resultRect.origin = [window convertBaseToScreen:resultRect.origin]; 5829 5830 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); 5831 return resultRect; 5832} 5833 5834- (NSRange)selectedRange 5835{ 5836 [self _executeSavedKeypressCommands]; 5837 5838 if (!isTextInput(core([self _frame]))) { 5839 LOG(TextInput, "selectedRange -> (NSNotFound, 0)"); 5840 return NSMakeRange(NSNotFound, 0); 5841 } 5842 NSRange result = [[self _frame] _selectedNSRange]; 5843 5844 LOG(TextInput, "selectedRange -> (%u, %u)", result.location, result.length); 5845 return result; 5846} 5847 5848- (NSRange)markedRange 5849{ 5850 [self _executeSavedKeypressCommands]; 5851 5852 WebFrame *webFrame = [self _frame]; 5853 Frame* coreFrame = core(webFrame); 5854 if (!coreFrame) 5855 return NSMakeRange(0, 0); 5856 NSRange result = [webFrame _convertToNSRange:coreFrame->editor()->compositionRange().get()]; 5857 5858 LOG(TextInput, "markedRange -> (%u, %u)", result.location, result.length); 5859 return result; 5860} 5861 5862- (NSAttributedString *)attributedSubstringFromRange:(NSRange)nsRange 5863{ 5864 [self _executeSavedKeypressCommands]; 5865 5866 WebFrame *frame = [self _frame]; 5867 Frame* coreFrame = core(frame); 5868 if (!isTextInput(coreFrame) || isInPasswordField(coreFrame)) { 5869 LOG(TextInput, "attributedSubstringFromRange:(%u, %u) -> nil", nsRange.location, nsRange.length); 5870 return nil; 5871 } 5872 RefPtr<Range> range = [frame _convertToDOMRange:nsRange]; 5873 if (!range) { 5874 LOG(TextInput, "attributedSubstringFromRange:(%u, %u) -> nil", nsRange.location, nsRange.length); 5875 return nil; 5876 } 5877 5878 NSAttributedString *result = [WebHTMLConverter editingAttributedStringFromRange:range.get()]; 5879 5880 // [WebHTMLConverter editingAttributedStringFromRange:] insists on inserting a trailing 5881 // whitespace at the end of the string which breaks the ATOK input method. <rdar://problem/5400551> 5882 // To work around this we truncate the resultant string to the correct length. 5883 if ([result length] > nsRange.length) { 5884 ASSERT([result length] == nsRange.length + 1); 5885 ASSERT([[result string] characterAtIndex:nsRange.length] == '\n' || [[result string] characterAtIndex:nsRange.length] == ' '); 5886 result = [result attributedSubstringFromRange:NSMakeRange(0, nsRange.length)]; 5887 } 5888 LOG(TextInput, "attributedSubstringFromRange:(%u, %u) -> \"%@\"", nsRange.location, nsRange.length, [result string]); 5889 return result; 5890} 5891 5892// test for 10.4 because of <rdar://problem/4243463> 5893#ifdef BUILDING_ON_TIGER 5894- (long)conversationIdentifier 5895{ 5896 return (long)self; 5897} 5898#else 5899- (NSInteger)conversationIdentifier 5900{ 5901 return (NSInteger)self; 5902} 5903#endif 5904 5905- (BOOL)hasMarkedText 5906{ 5907 Frame* coreFrame = core([self _frame]); 5908 BOOL result = coreFrame && coreFrame->editor()->hasComposition(); 5909 5910 if (result) { 5911 // A saved command can confirm a composition, but it cannot start a new one. 5912 [self _executeSavedKeypressCommands]; 5913 result = coreFrame->editor()->hasComposition(); 5914 } 5915 5916 LOG(TextInput, "hasMarkedText -> %u", result); 5917 return result; 5918} 5919 5920- (void)unmarkText 5921{ 5922 [self _executeSavedKeypressCommands]; 5923 5924 LOG(TextInput, "unmarkText"); 5925 5926 // Use pointer to get parameters passed to us by the caller of interpretKeyEvents. 5927 WebHTMLViewInterpretKeyEventsParameters* parameters = _private->interpretKeyEventsParameters; 5928 5929 if (parameters) { 5930 parameters->eventInterpretationHadSideEffects = true; 5931 parameters->consumedByIM = false; 5932 } 5933 5934 if (Frame* coreFrame = core([self _frame])) 5935 coreFrame->editor()->confirmComposition(); 5936} 5937 5938static void extractUnderlines(NSAttributedString *string, Vector<CompositionUnderline>& result) 5939{ 5940 int length = [[string string] length]; 5941 5942 int i = 0; 5943 while (i < length) { 5944 NSRange range; 5945 NSDictionary *attrs = [string attributesAtIndex:i longestEffectiveRange:&range inRange:NSMakeRange(i, length - i)]; 5946 5947 if (NSNumber *style = [attrs objectForKey:NSUnderlineStyleAttributeName]) { 5948 Color color = Color::black; 5949 if (NSColor *colorAttr = [attrs objectForKey:NSUnderlineColorAttributeName]) 5950 color = colorFromNSColor([colorAttr colorUsingColorSpaceName:NSDeviceRGBColorSpace]); 5951 result.append(CompositionUnderline(range.location, NSMaxRange(range), color, [style intValue] > 1)); 5952 } 5953 5954 i = range.location + range.length; 5955 } 5956} 5957 5958- (void)setMarkedText:(id)string selectedRange:(NSRange)newSelRange 5959{ 5960 [self _executeSavedKeypressCommands]; 5961 5962 BOOL isAttributedString = [string isKindOfClass:[NSAttributedString class]]; 5963 ASSERT(isAttributedString || [string isKindOfClass:[NSString class]]); 5964 5965 LOG(TextInput, "setMarkedText:\"%@\" selectedRange:(%u, %u)", isAttributedString ? [string string] : string, newSelRange.location, newSelRange.length); 5966 5967 // Use pointer to get parameters passed to us by the caller of interpretKeyEvents. 5968 WebHTMLViewInterpretKeyEventsParameters* parameters = _private->interpretKeyEventsParameters; 5969 5970 if (parameters) { 5971 parameters->eventInterpretationHadSideEffects = true; 5972 parameters->consumedByIM = false; 5973 } 5974 5975 Frame* coreFrame = core([self _frame]); 5976 if (!coreFrame) 5977 return; 5978 5979 if (![self _isEditable]) 5980 return; 5981 5982 Vector<CompositionUnderline> underlines; 5983 NSString *text; 5984 NSRange replacementRange = { NSNotFound, 0 }; 5985 5986 if (isAttributedString) { 5987 // FIXME: We ignore most attributes from the string, so an input method cannot specify e.g. a font or a glyph variation. 5988 text = [string string]; 5989 NSString *rangeString = [string attribute:NSTextInputReplacementRangeAttributeName atIndex:0 longestEffectiveRange:0 inRange:NSMakeRange(0, [text length])]; 5990 LOG(TextInput, " ReplacementRange: %@", rangeString); 5991 // The AppKit adds a 'secret' property to the string that contains the replacement range. 5992 // The replacement range is the range of the the text that should be replaced with the new string. 5993 if (rangeString) 5994 replacementRange = NSRangeFromString(rangeString); 5995 5996 extractUnderlines(string, underlines); 5997 } else 5998 text = string; 5999 6000 if (replacementRange.location != NSNotFound) 6001 [[self _frame] _selectNSRange:replacementRange]; 6002 6003 coreFrame->editor()->setComposition(text, underlines, newSelRange.location, NSMaxRange(newSelRange)); 6004} 6005 6006- (void)doCommandBySelector:(SEL)selector 6007{ 6008 LOG(TextInput, "doCommandBySelector:\"%s\"", sel_getName(selector)); 6009 6010 // Use pointer to get parameters passed to us by the caller of interpretKeyEvents. 6011 // The same call to interpretKeyEvents can do more than one command. 6012 WebHTMLViewInterpretKeyEventsParameters* parameters = _private->interpretKeyEventsParameters; 6013 if (parameters) 6014 parameters->consumedByIM = false; 6015 6016 KeyboardEvent* event = parameters ? parameters->event : 0; 6017 bool shouldSaveCommand = parameters && parameters->shouldSaveCommands; 6018 6019 // As in insertText:, we assume that the call comes from an input method if there is marked text. 6020 RefPtr<Frame> coreFrame = core([self _frame]); 6021 bool isFromInputMethod = coreFrame && coreFrame->editor()->hasComposition(); 6022 6023 if (event && shouldSaveCommand && !isFromInputMethod) 6024 event->keypressCommands().append(KeypressCommand(NSStringFromSelector(selector))); 6025 else { 6026 // Make sure that only direct calls to doCommandBySelector: see the parameters by setting to 0. 6027 _private->interpretKeyEventsParameters = 0; 6028 6029 bool eventWasHandled; 6030 6031 WebView *webView = [self _webView]; 6032 if ([[webView _editingDelegateForwarder] webView:webView doCommandBySelector:selector]) 6033 eventWasHandled = true; 6034 else { 6035 Editor::Command command = [self coreCommandBySelector:selector]; 6036 if (command.isSupported()) 6037 eventWasHandled = command.execute(event); 6038 else { 6039 // If WebKit does not support this command, we need to pass the selector to super. 6040 _private->selectorForDoCommandBySelector = selector; 6041 6042 // The sink does two things: 1) Tells us if the responder went unhandled, and 6043 // 2) prevents any NSBeep; we don't ever want to beep here. 6044 WebResponderChainSink *sink = [[WebResponderChainSink alloc] initWithResponderChain:self]; 6045 [super doCommandBySelector:selector]; 6046 eventWasHandled = ![sink receivedUnhandledCommand]; 6047 [sink detach]; 6048 [sink release]; 6049 6050 _private->selectorForDoCommandBySelector = 0; 6051 } 6052 } 6053 6054 if (parameters) 6055 parameters->eventInterpretationHadSideEffects |= eventWasHandled; 6056 6057 _private->interpretKeyEventsParameters = parameters; 6058 } 6059} 6060 6061- (void)insertText:(id)string 6062{ 6063 BOOL isAttributedString = [string isKindOfClass:[NSAttributedString class]]; 6064 ASSERT(isAttributedString || [string isKindOfClass:[NSString class]]); 6065 6066 LOG(TextInput, "insertText:\"%@\"", isAttributedString ? [string string] : string); 6067 6068 WebHTMLViewInterpretKeyEventsParameters* parameters = _private->interpretKeyEventsParameters; 6069 if (parameters) 6070 parameters->consumedByIM = false; 6071 6072 RefPtr<Frame> coreFrame = core([self _frame]); 6073 NSString *text; 6074 NSRange replacementRange = { NSNotFound, 0 }; 6075 bool isFromInputMethod = coreFrame && coreFrame->editor()->hasComposition(); 6076 6077 if (isAttributedString) { 6078 // FIXME: We ignore most attributes from the string, so for example inserting from Character Palette loses font and glyph variation data. 6079 // It does not look like any input methods ever use insertText: with attributes other than NSTextInputReplacementRangeAttributeName. 6080 text = [string string]; 6081 NSString *rangeString = [string attribute:NSTextInputReplacementRangeAttributeName atIndex:0 longestEffectiveRange:0 inRange:NSMakeRange(0, [text length])]; 6082 LOG(TextInput, " ReplacementRange: %@", rangeString); 6083 if (rangeString) { 6084 replacementRange = NSRangeFromString(rangeString); 6085 isFromInputMethod = true; 6086 } 6087 } else 6088 text = string; 6089 6090 KeyboardEvent* event = parameters ? parameters->event : 0; 6091 6092 // insertText can be called for several reasons: 6093 // - If it's from normal key event processing (including key bindings), we may need to save the action to perform it later. 6094 // - 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. 6095 // 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. 6096 // - If it's sent outside of keyboard event processing (e.g. from Character Viewer, or when confirming an inline input area with a mouse), 6097 // then we also execute it immediately, as there will be no other chance. 6098 bool shouldSaveCommand = parameters && parameters->shouldSaveCommands; 6099 if (event && shouldSaveCommand && !isFromInputMethod) { 6100 event->keypressCommands().append(KeypressCommand("insertText:", text)); 6101 return; 6102 } 6103 6104 if (!coreFrame || !coreFrame->editor()->canEdit()) 6105 return; 6106 6107 if (replacementRange.location != NSNotFound) 6108 [[self _frame] _selectNSRange:replacementRange]; 6109 6110 bool eventHandled = false; 6111 String eventText = text; 6112 eventText.replace(NSBackTabCharacter, NSTabCharacter); // same thing is done in KeyEventMac.mm in WebCore 6113 if (!coreFrame->editor()->hasComposition()) { 6114 // An insertText: might be handled by other responders in the chain if we don't handle it. 6115 // One example is space bar that results in scrolling down the page. 6116 eventHandled = coreFrame->editor()->insertText(eventText, event); 6117 } else { 6118 eventHandled = true; 6119 coreFrame->editor()->confirmComposition(eventText); 6120 } 6121 6122 if (parameters) 6123 parameters->eventInterpretationHadSideEffects |= eventHandled; 6124} 6125 6126- (void)_updateSecureInputState 6127{ 6128 if (![[self window] isKeyWindow] || ([[self window] firstResponder] != self && !_private->_forceUpdateSecureInputState)) { 6129 if (_private->isInSecureInputState) { 6130 DisableSecureEventInput(); 6131 _private->isInSecureInputState = NO; 6132 } 6133 return; 6134 } 6135 6136 Frame* coreFrame = core([self _frame]); 6137 if (!coreFrame) 6138 return; 6139 6140 if (isInPasswordField(coreFrame)) { 6141 if (!_private->isInSecureInputState) 6142 EnableSecureEventInput(); 6143 _private->isInSecureInputState = YES; 6144 // WebKit substitutes nil for input context when in password field, which corresponds to null TSMDocument. So, there is 6145 // no need to call TSMGetActiveDocument(), which may return an incorrect result when selection hasn't been yet updated 6146 // after focusing a node. 6147 static CFArrayRef inputSources = TISCreateASCIICapableInputSourceList(); 6148 TSMSetDocumentProperty(0, kTSMDocumentEnabledInputSourcesPropertyTag, sizeof(CFArrayRef), &inputSources); 6149 } else { 6150 if (_private->isInSecureInputState) 6151 DisableSecureEventInput(); 6152 _private->isInSecureInputState = NO; 6153 TSMRemoveDocumentProperty(0, kTSMDocumentEnabledInputSourcesPropertyTag); 6154 } 6155} 6156 6157- (void)_updateSelectionForInputManager 6158{ 6159 Frame* coreFrame = core([self _frame]); 6160 if (!coreFrame) 6161 return; 6162 6163 BOOL exposeInputContext = isTextInput(coreFrame) && !isInPasswordField(coreFrame); 6164 if (exposeInputContext != _private->exposeInputContext) { 6165 _private->exposeInputContext = exposeInputContext; 6166 // Let AppKit cache a potentially changed input context. 6167 // WebCore routinely sets the selection to None when editing, and IMs become unhappy when an input context suddenly turns nil, see bug 26009. 6168 if (!coreFrame->selection()->isNone()) 6169 [NSApp updateWindows]; 6170 } 6171 6172 [self _updateSecureInputState]; 6173 6174 if (!coreFrame->editor()->hasComposition()) 6175 return; 6176 6177 if (coreFrame->editor()->ignoreCompositionSelectionChange()) 6178 return; 6179 6180 unsigned start; 6181 unsigned end; 6182 if (coreFrame->editor()->getCompositionSelection(start, end)) 6183 [[NSInputManager currentInputManager] markedTextSelectionChanged:NSMakeRange(start, end - start) client:self]; 6184 else { 6185 coreFrame->editor()->confirmCompositionWithoutDisturbingSelection(); 6186 [[NSInputManager currentInputManager] markedTextAbandoned:self]; 6187 } 6188} 6189 6190@end 6191 6192@implementation WebHTMLView (WebDocumentPrivateProtocols) 6193 6194- (NSRect)selectionRect 6195{ 6196 if (![self _hasSelection]) 6197 return NSZeroRect; 6198 return core([self _frame])->selection()->bounds(); 6199} 6200 6201- (NSArray *)selectionTextRects 6202{ 6203 if (![self _hasSelection]) 6204 return nil; 6205 6206 Vector<FloatRect> list; 6207 if (Frame* coreFrame = core([self _frame])) 6208 coreFrame->selection()->getClippedVisibleTextRectangles(list); 6209 6210 size_t size = list.size(); 6211 6212 NSMutableArray *result = [NSMutableArray arrayWithCapacity:size]; 6213 6214 for (size_t i = 0; i < size; ++i) 6215 [result addObject:[NSValue valueWithRect:list[i]]]; 6216 6217 return result; 6218} 6219 6220- (NSView *)selectionView 6221{ 6222 return self; 6223} 6224 6225- (NSImage *)selectionImageForcingBlackText:(BOOL)forceBlackText 6226{ 6227 if (![self _hasSelection]) 6228 return nil; 6229 return core([self _frame])->selectionImage(forceBlackText); 6230} 6231 6232- (NSRect)selectionImageRect 6233{ 6234 if (![self _hasSelection]) 6235 return NSZeroRect; 6236 return core([self _frame])->selection()->bounds(); 6237} 6238 6239- (NSArray *)pasteboardTypesForSelection 6240{ 6241 if ([self _canSmartCopyOrDelete]) { 6242 NSMutableArray *types = [[[[self class] _selectionPasteboardTypes] mutableCopy] autorelease]; 6243 [types addObject:WebSmartPastePboardType]; 6244 return types; 6245 } else { 6246 return [[self class] _selectionPasteboardTypes]; 6247 } 6248} 6249 6250- (void)writeSelectionWithPasteboardTypes:(NSArray *)types toPasteboard:(NSPasteboard *)pasteboard 6251{ 6252 [self _writeSelectionWithPasteboardTypes:types toPasteboard:pasteboard cachedAttributedString:nil]; 6253} 6254 6255- (void)selectAll 6256{ 6257 Frame* coreFrame = core([self _frame]); 6258 if (coreFrame) 6259 coreFrame->selection()->selectAll(); 6260} 6261 6262- (void)deselectAll 6263{ 6264 Frame* coreFrame = core([self _frame]); 6265 if (!coreFrame) 6266 return; 6267 coreFrame->selection()->clear(); 6268} 6269 6270- (NSString *)string 6271{ 6272 return [[self _frame] _stringForRange:[self _documentRange]]; 6273} 6274 6275- (NSAttributedString *)_attributeStringFromDOMRange:(DOMRange *)range 6276{ 6277 NSAttributedString *attributedString; 6278#if !LOG_DISABLED 6279 double start = CFAbsoluteTimeGetCurrent(); 6280#endif 6281 attributedString = [[[NSAttributedString alloc] _initWithDOMRange:range] autorelease]; 6282#if !LOG_DISABLED 6283 double duration = CFAbsoluteTimeGetCurrent() - start; 6284 LOG(Timing, "creating attributed string from selection took %f seconds.", duration); 6285#endif 6286 return attributedString; 6287} 6288 6289- (NSAttributedString *)attributedString 6290{ 6291 DOMDocument *document = [[self _frame] DOMDocument]; 6292 NSAttributedString *attributedString = [self _attributeStringFromDOMRange:[document _documentRange]]; 6293 if (!attributedString) { 6294 Document* coreDocument = core(document); 6295 attributedString = [WebHTMLConverter editingAttributedStringFromRange:Range::create(coreDocument, coreDocument, 0, coreDocument, coreDocument->childNodeCount()).get()]; 6296 } 6297 return attributedString; 6298} 6299 6300- (NSString *)selectedString 6301{ 6302 return [[self _frame] _selectedString]; 6303} 6304 6305- (NSAttributedString *)selectedAttributedString 6306{ 6307 NSAttributedString *attributedString = [self _attributeStringFromDOMRange:[self _selectedRange]]; 6308 if (!attributedString) { 6309 Frame* coreFrame = core([self _frame]); 6310 if (coreFrame) { 6311 RefPtr<Range> range = coreFrame->selection()->selection().toNormalizedRange(); 6312 attributedString = [WebHTMLConverter editingAttributedStringFromRange:range.get()]; 6313 } 6314 } 6315 return attributedString; 6316} 6317 6318- (BOOL)supportsTextEncoding 6319{ 6320 return YES; 6321} 6322 6323- (BOOL)searchFor:(NSString *)string direction:(BOOL)forward caseSensitive:(BOOL)caseFlag wrap:(BOOL)wrapFlag startInSelection:(BOOL)startInSelection 6324{ 6325 return [self _findString:string options:(forward ? 0 : WebFindOptionsBackwards) | (caseFlag ? 0 : WebFindOptionsCaseInsensitive) | (wrapFlag ? WebFindOptionsWrapAround : 0) | (startInSelection ? WebFindOptionsStartInSelection : 0)]; 6326} 6327 6328@end 6329 6330@implementation WebHTMLView (WebDocumentInternalProtocols) 6331 6332- (NSDictionary *)elementAtPoint:(NSPoint)point 6333{ 6334 return [self elementAtPoint:point allowShadowContent:NO]; 6335} 6336 6337- (NSDictionary *)elementAtPoint:(NSPoint)point allowShadowContent:(BOOL)allow 6338{ 6339 Frame* coreFrame = core([self _frame]); 6340 if (!coreFrame) 6341 return nil; 6342 return [[[WebElementDictionary alloc] initWithHitTestResult:coreFrame->eventHandler()->hitTestResultAtPoint(IntPoint(point), allow)] autorelease]; 6343} 6344 6345- (NSUInteger)countMatchesForText:(NSString *)string inDOMRange:(DOMRange *)range options:(WebFindOptions)options limit:(NSUInteger)limit markMatches:(BOOL)markMatches 6346{ 6347 Frame* coreFrame = core([self _frame]); 6348 if (!coreFrame) 6349 return 0; 6350 6351 return coreFrame->editor()->countMatchesForText(string, core(range), coreOptions(options), limit, markMatches); 6352} 6353 6354- (void)setMarkedTextMatchesAreHighlighted:(BOOL)newValue 6355{ 6356 Frame* coreFrame = core([self _frame]); 6357 if (!coreFrame) 6358 return; 6359 coreFrame->editor()->setMarkedTextMatchesAreHighlighted(newValue); 6360} 6361 6362- (BOOL)markedTextMatchesAreHighlighted 6363{ 6364 Frame* coreFrame = core([self _frame]); 6365 return coreFrame && coreFrame->editor()->markedTextMatchesAreHighlighted(); 6366} 6367 6368- (void)unmarkAllTextMatches 6369{ 6370 Frame* coreFrame = core([self _frame]); 6371 if (!coreFrame) 6372 return; 6373 Document* document = coreFrame->document(); 6374 if (!document) 6375 return; 6376 document->markers()->removeMarkers(DocumentMarker::TextMatch); 6377} 6378 6379- (NSArray *)rectsForTextMatches 6380{ 6381 Frame* coreFrame = core([self _frame]); 6382 if (!coreFrame) 6383 return [NSArray array]; 6384 Document* document = coreFrame->document(); 6385 if (!document) 6386 return [NSArray array]; 6387 6388 Vector<IntRect> rects = document->markers()->renderedRectsForMarkers(DocumentMarker::TextMatch); 6389 unsigned count = rects.size(); 6390 NSMutableArray *result = [NSMutableArray arrayWithCapacity:count]; 6391 for (unsigned index = 0; index < count; ++index) 6392 [result addObject:[NSValue valueWithRect:rects[index]]]; 6393 return result; 6394} 6395 6396- (BOOL)_findString:(NSString *)string options:(WebFindOptions)options 6397{ 6398 if (![string length]) 6399 return NO; 6400 Frame* coreFrame = core([self _frame]); 6401 return coreFrame && coreFrame->editor()->findString(string, coreOptions(options)); 6402} 6403 6404@end 6405 6406// This is used by AppKit and is included here so that WebDataProtocolScheme is only defined once. 6407@implementation NSURL (WebDataURL) 6408 6409+ (NSURL *)_web_uniqueWebDataURL 6410{ 6411 CFUUIDRef UUIDRef = CFUUIDCreate(kCFAllocatorDefault); 6412 NSString *UUIDString = (NSString *)CFUUIDCreateString(kCFAllocatorDefault, UUIDRef); 6413 CFRelease(UUIDRef); 6414 NSURL *URL = [NSURL URLWithString:[NSString stringWithFormat:@"%@://%@", WebDataProtocolScheme, UUIDString]]; 6415 CFRelease(UUIDString); 6416 return URL; 6417} 6418 6419@end 6420 6421@implementation WebResponderChainSink 6422 6423- (id)initWithResponderChain:(NSResponder *)chain 6424{ 6425 self = [super init]; 6426 _lastResponderInChain = chain; 6427 while (NSResponder *next = [_lastResponderInChain nextResponder]) 6428 _lastResponderInChain = next; 6429 [_lastResponderInChain setNextResponder:self]; 6430 return self; 6431} 6432 6433- (void)detach 6434{ 6435 [_lastResponderInChain setNextResponder:nil]; 6436 _lastResponderInChain = nil; 6437} 6438 6439- (BOOL)receivedUnhandledCommand 6440{ 6441 return _receivedUnhandledCommand; 6442} 6443 6444- (void)noResponderFor:(SEL)selector 6445{ 6446 _receivedUnhandledCommand = YES; 6447} 6448 6449- (void)doCommandBySelector:(SEL)selector 6450{ 6451 _receivedUnhandledCommand = YES; 6452} 6453 6454- (BOOL)tryToPerform:(SEL)action with:(id)object 6455{ 6456 _receivedUnhandledCommand = YES; 6457 return YES; 6458} 6459 6460@end 6461