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