From 10f77c8a5c9976b16f1cf1dfb6a9f709502989eb Mon Sep 17 00:00:00 2001 From: "lordpixel@mac.com" Date: Thu, 11 Mar 2010 11:44:31 -0500 Subject: [PATCH] Implement CSS cursor support for Mac OS X. b=286304 r=josh --- widget/src/cocoa/nsChildView.mm | 10 +- widget/src/cocoa/nsCocoaUtils.h | 32 ++++ widget/src/cocoa/nsCocoaUtils.mm | 82 ++++++++++ widget/src/cocoa/nsCursorManager.h | 15 +- widget/src/cocoa/nsCursorManager.mm | 224 ++++++++++++++++++---------- widget/src/cocoa/nsMacCursor.h | 23 ++- widget/src/cocoa/nsMacCursor.mm | 64 ++++---- 7 files changed, 336 insertions(+), 114 deletions(-) diff --git a/widget/src/cocoa/nsChildView.mm b/widget/src/cocoa/nsChildView.mm index fafd17122ba..971578819b6 100644 --- a/widget/src/cocoa/nsChildView.mm +++ b/widget/src/cocoa/nsChildView.mm @@ -977,8 +977,7 @@ NS_IMETHODIMP nsChildView::SetCursor(nsCursor aCursor) return NS_OK; // Don't change the cursor during dragging. nsBaseWidget::SetCursor(aCursor); - [[nsCursorManager sharedInstance] setCursor: aCursor]; - return NS_OK; + return [[nsCursorManager sharedInstance] setCursor:aCursor]; NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; } @@ -987,7 +986,12 @@ NS_IMETHODIMP nsChildView::SetCursor(nsCursor aCursor) NS_IMETHODIMP nsChildView::SetCursor(imgIContainer* aCursor, PRUint32 aHotspotX, PRUint32 aHotspotY) { - return nsBaseWidget::SetCursor(aCursor, aHotspotX, aHotspotY); + NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; + + nsBaseWidget::SetCursor(aCursor, aHotspotX, aHotspotY); + return [[nsCursorManager sharedInstance] setCursorWithImage:aCursor hotSpotX:aHotspotX hotSpotY:aHotspotY]; + + NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; } #pragma mark - diff --git a/widget/src/cocoa/nsCocoaUtils.h b/widget/src/cocoa/nsCocoaUtils.h index 1999f339f65..b5c613ec389 100644 --- a/widget/src/cocoa/nsCocoaUtils.h +++ b/widget/src/cocoa/nsCocoaUtils.h @@ -45,6 +45,7 @@ #include "nsRect.h" #include "nsObjCExceptions.h" +#include "imgIContainer.h" class nsIWidget; @@ -130,6 +131,37 @@ class nsCocoaUtils static void PrepareForNativeAppModalDialog(); static void CleanUpAfterNativeAppModalDialog(); + + // 3 utility functions to go from a frame of imgIContainer to CGImage and then to NSImage + // Convert imgIContainer -> CGImageRef, caller owns result + + /** Creates a CGImageRef from a frame contained in an imgIContainer. + Copies the pixel data from the indicated frame of the imgIContainer into a new CGImageRef. + The caller owns the CGImageRef. + @param aImage the image to extract a frame from + @param aWhichFrame the frame to extract (see imgIContainer FRAME_*) + @param aResult the resulting CGImageRef + @return NS_OK if the conversion worked, NS_ERROR_FAILURE otherwise + */ + static nsresult CreateCGImageFromImageContainer(imgIContainer *aImage, PRUint32 aWhichFrame, CGImageRef *aResult); + + /** Creates a Cocoa NSImage from a CGImageRef. + Copies the pixel data from the CGImageRef into a new NSImage. + The caller owns the NSImage. + @param aInputImage the image to convert + @param aResult the resulting NSImage + @return NS_OK if the conversion worked, NS_ERROR_FAILURE otherwise + */ + static nsresult CreateNSImageFromCGImage(CGImageRef aInputImage, NSImage **aResult); + + /** Creates a Cocoa NSImage from a frame of an imgIContainer. + Combines the two methods above. The caller owns the NSImage. + @param aImage the image to extract a frame from + @param aWhichFrame the frame to extract (see imgIContainer FRAME_*) + @param aResult the resulting NSImage + @return NS_OK if the conversion worked, NS_ERROR_FAILURE otherwise + */ + static nsresult CreateNSImageFromImageContainer(imgIContainer *aImage, PRUint32 aWhichFrame, NSImage **aResult); }; #endif // nsCocoaUtils_h_ diff --git a/widget/src/cocoa/nsCocoaUtils.mm b/widget/src/cocoa/nsCocoaUtils.mm index 1c69678ca92..b8a5196ec0c 100644 --- a/widget/src/cocoa/nsCocoaUtils.mm +++ b/widget/src/cocoa/nsCocoaUtils.mm @@ -38,6 +38,7 @@ * * ***** END LICENSE BLOCK ***** */ +#include "gfxImageSurface.h" #include "nsCocoaUtils.h" #include "nsMenuBarX.h" #include "nsCocoaWindow.h" @@ -243,3 +244,84 @@ void nsCocoaUtils::CleanUpAfterNativeAppModalDialog() NS_OBJC_END_TRY_ABORT_BLOCK; } + +nsresult nsCocoaUtils::CreateCGImageFromImageContainer(imgIContainer *aImage, PRUint32 aWhichFrame, CGImageRef *aResult) +{ + nsRefPtr frame; + nsresult rv = aImage->CopyFrame(aWhichFrame, + imgIContainer::FLAG_SYNC_DECODE, + getter_AddRefs(frame)); + if (NS_FAILED(rv) || !frame) { + return NS_ERROR_FAILURE; + } + + PRInt32 width = frame->Width(); + PRInt32 stride = frame->Stride(); + PRInt32 height = frame->Height(); + if ((stride % 4 != 0) || (height < 1) || (width < 1)) { + return NS_ERROR_FAILURE; + } + + // Create a CGImageRef with the bits from the image, taking into account + // the alpha ordering and endianness of the machine so we don't have to + // touch the bits ourselves. + CGDataProviderRef dataProvider = ::CGDataProviderCreateWithData(NULL, + frame->Data(), + stride * height, + NULL); + CGColorSpaceRef colorSpace = ::CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB); + *aResult = ::CGImageCreate(width, + height, + 8, + 32, + stride, + colorSpace, + kCGBitmapByteOrder32Host | kCGImageAlphaFirst, + dataProvider, + NULL, + 0, + kCGRenderingIntentDefault); + ::CGColorSpaceRelease(colorSpace); + ::CGDataProviderRelease(dataProvider); + return *aResult ? NS_OK : NS_ERROR_FAILURE; +} + +nsresult nsCocoaUtils::CreateNSImageFromCGImage(CGImageRef aInputImage, NSImage **aResult) +{ + NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; + + PRInt32 width = ::CGImageGetWidth(aInputImage); + PRInt32 height = ::CGImageGetHeight(aInputImage); + NSRect imageRect = ::NSMakeRect(0.0, 0.0, width, height); + + // Create a new image to receive the Quartz image data. + *aResult = [[NSImage alloc] initWithSize:imageRect.size]; + + [*aResult lockFocus]; + + // Get the Quartz context and draw. + CGContextRef imageContext = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort]; + ::CGContextDrawImage(imageContext, *(CGRect*)&imageRect, aInputImage); + + [*aResult unlockFocus]; + return NS_OK; + + NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; +} + +nsresult nsCocoaUtils::CreateNSImageFromImageContainer(imgIContainer *aImage, PRUint32 aWhichFrame, NSImage **aResult) +{ + CGImageRef imageRef = NULL; + nsresult rv = nsCocoaUtils::CreateCGImageFromImageContainer(aImage, aWhichFrame, &imageRef); + if (NS_FAILED(rv) || !imageRef) { + return NS_ERROR_FAILURE; + } + + rv = nsCocoaUtils::CreateNSImageFromCGImage(imageRef, aResult); + if (NS_FAILED(rv) || !aResult) { + return NS_ERROR_FAILURE; + } + ::CGImageRelease(imageRef); + return NS_OK; +} + diff --git a/widget/src/cocoa/nsCursorManager.h b/widget/src/cocoa/nsCursorManager.h index c8bfa291952..d6c402af8db 100644 --- a/widget/src/cocoa/nsCursorManager.h +++ b/widget/src/cocoa/nsCursorManager.h @@ -50,7 +50,7 @@ { @private NSMutableDictionary *mCursors; - nsCursor mCurrentCursor; + nsMacCursor *mCurrentMacCursor; } /*! @method setCursor: @@ -59,7 +59,18 @@ Resources associated with the previous cursor are cleaned up. @param aCursor the cursor to use */ -- (void) setCursor: (nsCursor) aCursor; +- (nsresult) setCursor: (nsCursor) aCursor; + +/*! @method setCursorWithImage:hotSpotX:hotSpotY: + @abstract Sets the current cursor to a custom image + @discussion Sets the current cursor to the cursor given by the aCursorImage argument. + Resources associated with the previous cursor are cleaned up. + @param aCursorImage the cursor image to use + @param aHotSpotX the x coordinate of the cursor's hotspot + @param aHotSpotY the y coordinate of the cursor's hotspot + */ +- (nsresult) setCursorWithImage: (imgIContainer*) aCursorImage hotSpotX: (PRUint32) aHotspotX hotSpotY: (PRUint32) aHotspotY; + /*! @method sharedInstance @abstract Get the Singleton instance of the cursor manager. diff --git a/widget/src/cocoa/nsCursorManager.mm b/widget/src/cocoa/nsCursorManager.mm index db4707471bb..7545a092f1b 100644 --- a/widget/src/cocoa/nsCursorManager.mm +++ b/widget/src/cocoa/nsCursorManager.mm @@ -35,12 +35,16 @@ * * ***** END LICENSE BLOCK ***** */ +#include "imgIContainer.h" +#include "nsCocoaUtils.h" #include "nsCursorManager.h" #include "nsObjCExceptions.h" #include static nsCursorManager *gInstance; -static NSArray* sSpinCursorFrames = nil; +static NSArray *sSpinCursorFrames = nil; +static imgIContainer *sCursorImgContainer = nsnull; +static const nsCursor sCustomCursor = eCursorCount; /*! @category nsCursorManager(PrivateMethods) Private methods for the cursor manager class. @@ -56,6 +60,15 @@ static NSArray* sSpinCursorFrames = nil; */ - (nsMacCursor *) getCursor: (nsCursor) aCursor; +/*! @method setMacCursor: + @abstract Set the current Mac native cursor + @discussion Sets the current cursor - this routine is what actually causes the cursor to change. + The argument is retained and the old cursor is released. + @param aMacCursor the cursor to set + @result NS_OK + */ +- (nsresult) setMacCursor: (nsMacCursor*) aMacCursor; + /*! @method createCursor: @abstract Create a Mac native representation of a cursor. @discussion Creates a version of the Mac native representation of this cursor @@ -101,7 +114,7 @@ static NSArray* sSpinCursorFrames = nil; NS_OBJC_END_TRY_ABORT_BLOCK; } -+ (nsMacCursor *) createCursor: (enum nsCursor) aCursor ++ (nsMacCursor *) createCursor: (enum nsCursor) aCursor { NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; @@ -109,100 +122,100 @@ static NSArray* sSpinCursorFrames = nil; { SEL cursorSelector; case eCursor_standard: - return [nsMacCursor cursorWithCursor: [NSCursor arrowCursor]]; + return [nsMacCursor cursorWithCursor:[NSCursor arrowCursor] type:aCursor]; case eCursor_wait: case eCursor_spinning: - return [nsMacCursor cursorWithFrames: sSpinCursorFrames]; + return [nsMacCursor cursorWithFrames:sSpinCursorFrames type:aCursor]; case eCursor_select: - return [nsMacCursor cursorWithCursor: [NSCursor IBeamCursor]]; + return [nsMacCursor cursorWithCursor:[NSCursor IBeamCursor] type:aCursor]; case eCursor_hyperlink: - return [nsMacCursor cursorWithCursor: [NSCursor pointingHandCursor]]; + return [nsMacCursor cursorWithCursor:[NSCursor pointingHandCursor] type:aCursor]; case eCursor_crosshair: - return [nsMacCursor cursorWithCursor: [NSCursor crosshairCursor]]; + return [nsMacCursor cursorWithCursor:[NSCursor crosshairCursor] type:aCursor]; case eCursor_move: - return [nsMacCursor cursorWithCursor: [NSCursor openHandCursor]]; + return [nsMacCursor cursorWithCursor:[NSCursor openHandCursor] type:aCursor]; case eCursor_help: - return [nsMacCursor cursorWithImageNamed: @"help" hotSpot: NSMakePoint(1,1)]; + return [nsMacCursor cursorWithImageNamed:@"help" hotSpot:NSMakePoint(1,1) type:aCursor]; case eCursor_copy: cursorSelector = @selector(dragCopyCursor); - return [nsMacCursor cursorWithCursor: [NSCursor respondsToSelector: cursorSelector] ? - [NSCursor performSelector: cursorSelector] : - [NSCursor arrowCursor]]; + return [nsMacCursor cursorWithCursor:[NSCursor respondsToSelector:cursorSelector] ? + [NSCursor performSelector:cursorSelector] : + [NSCursor arrowCursor] type:aCursor]; case eCursor_alias: cursorSelector = @selector(dragLinkCursor); - return [nsMacCursor cursorWithCursor: [NSCursor respondsToSelector: cursorSelector] ? - [NSCursor performSelector: cursorSelector] : - [NSCursor arrowCursor]]; + return [nsMacCursor cursorWithCursor:[NSCursor respondsToSelector:cursorSelector] ? + [NSCursor performSelector:cursorSelector] : + [NSCursor arrowCursor] type:aCursor]; case eCursor_context_menu: cursorSelector = @selector(contextualMenuCursor); - return [nsMacCursor cursorWithCursor: [NSCursor respondsToSelector: cursorSelector] ? - [NSCursor performSelector: cursorSelector] : - [NSCursor arrowCursor]]; + return [nsMacCursor cursorWithCursor:[NSCursor respondsToSelector:cursorSelector] ? + [NSCursor performSelector:cursorSelector] : + [NSCursor arrowCursor] type:aCursor]; case eCursor_cell: - return [nsMacCursor cursorWithCursor: [NSCursor crosshairCursor]]; + return [nsMacCursor cursorWithCursor:[NSCursor crosshairCursor] type:aCursor]; case eCursor_grab: - return [nsMacCursor cursorWithCursor: [NSCursor openHandCursor]]; + return [nsMacCursor cursorWithCursor:[NSCursor openHandCursor] type:aCursor]; case eCursor_grabbing: - return [nsMacCursor cursorWithCursor: [NSCursor closedHandCursor]]; + return [nsMacCursor cursorWithCursor:[NSCursor closedHandCursor] type:aCursor]; case eCursor_zoom_in: - return [nsMacCursor cursorWithImageNamed: @"zoomIn" hotSpot: NSMakePoint(6,6)]; + return [nsMacCursor cursorWithImageNamed:@"zoomIn" hotSpot:NSMakePoint(6,6) type:aCursor]; case eCursor_zoom_out: - return [nsMacCursor cursorWithImageNamed: @"zoomOut" hotSpot: NSMakePoint(6,6)]; + return [nsMacCursor cursorWithImageNamed:@"zoomOut" hotSpot:NSMakePoint(6,6) type:aCursor]; case eCursor_vertical_text: - return [nsMacCursor cursorWithImageNamed: @"vtIBeam" hotSpot: NSMakePoint(7,8)]; + return [nsMacCursor cursorWithImageNamed:@"vtIBeam" hotSpot:NSMakePoint(7,8) type:aCursor]; case eCursor_all_scroll: - return [nsMacCursor cursorWithCursor: [NSCursor openHandCursor]];; + return [nsMacCursor cursorWithCursor:[NSCursor openHandCursor] type:aCursor]; case eCursor_not_allowed: case eCursor_no_drop: cursorSelector = @selector(operationNotAllowedCursor); - return [nsMacCursor cursorWithCursor: [NSCursor respondsToSelector: cursorSelector] ? - [NSCursor performSelector: cursorSelector] : - [NSCursor arrowCursor]]; + return [nsMacCursor cursorWithCursor:[NSCursor respondsToSelector:cursorSelector] ? + [NSCursor performSelector:cursorSelector] : + [NSCursor arrowCursor] type:aCursor]; // Resize Cursors: - //North + // North case eCursor_n_resize: - return [nsMacCursor cursorWithCursor: [NSCursor resizeUpCursor]]; - //North East + return [nsMacCursor cursorWithCursor:[NSCursor resizeUpCursor] type:aCursor]; + // North East case eCursor_ne_resize: - return [nsMacCursor cursorWithImageNamed: @"sizeNE" hotSpot: NSMakePoint(8,7)]; - //East - case eCursor_e_resize: - return [nsMacCursor cursorWithCursor: [NSCursor resizeRightCursor]]; - //South East + return [nsMacCursor cursorWithImageNamed:@"sizeNE" hotSpot:NSMakePoint(8,7) type:aCursor]; + // East + case eCursor_e_resize: + return [nsMacCursor cursorWithCursor:[NSCursor resizeRightCursor] type:aCursor]; + // South East case eCursor_se_resize: - return [nsMacCursor cursorWithImageNamed: @"sizeSE" hotSpot: NSMakePoint(8,8)]; - //South + return [nsMacCursor cursorWithImageNamed:@"sizeSE" hotSpot:NSMakePoint(8,8) type:aCursor]; + // South case eCursor_s_resize: - return [nsMacCursor cursorWithCursor: [NSCursor resizeDownCursor]]; - //South West + return [nsMacCursor cursorWithCursor:[NSCursor resizeDownCursor] type:aCursor]; + // South West case eCursor_sw_resize: - return [nsMacCursor cursorWithImageNamed: @"sizeSW" hotSpot: NSMakePoint(6,8)]; - //West + return [nsMacCursor cursorWithImageNamed:@"sizeSW" hotSpot:NSMakePoint(6,8) type:aCursor]; + // West case eCursor_w_resize: - return [nsMacCursor cursorWithCursor: [NSCursor resizeLeftCursor]]; - //North West + return [nsMacCursor cursorWithCursor:[NSCursor resizeLeftCursor] type:aCursor]; + // North West case eCursor_nw_resize: - return [nsMacCursor cursorWithImageNamed: @"sizeNW" hotSpot: NSMakePoint(7,7)]; - //North & South + return [nsMacCursor cursorWithImageNamed:@"sizeNW" hotSpot:NSMakePoint(7,7) type:aCursor]; + // North & South case eCursor_ns_resize: - return [nsMacCursor cursorWithCursor: [NSCursor resizeUpDownCursor]]; - //East & West + return [nsMacCursor cursorWithCursor:[NSCursor resizeUpDownCursor] type:aCursor]; + // East & West case eCursor_ew_resize: - return [nsMacCursor cursorWithCursor: [NSCursor resizeLeftRightCursor]]; - //North East & South West + return [nsMacCursor cursorWithCursor:[NSCursor resizeLeftRightCursor] type:aCursor]; + // North East & South West case eCursor_nesw_resize: - return [nsMacCursor cursorWithImageNamed: @"sizeNESW" hotSpot: NSMakePoint(8,8)]; - //North West & South East + return [nsMacCursor cursorWithImageNamed:@"sizeNESW" hotSpot:NSMakePoint(8,8) type:aCursor]; + // North West & South East case eCursor_nwse_resize: - return [nsMacCursor cursorWithImageNamed: @"sizeNWSE" hotSpot: NSMakePoint(8,8)]; - //Column Resize + return [nsMacCursor cursorWithImageNamed:@"sizeNWSE" hotSpot:NSMakePoint(8,8) type:aCursor]; + // Column Resize case eCursor_col_resize: - return [nsMacCursor cursorWithImageNamed: @"colResize" hotSpot: NSMakePoint(8,8)]; - //Row Resize + return [nsMacCursor cursorWithImageNamed:@"colResize" hotSpot:NSMakePoint(8,8) type:aCursor]; + // Row Resize case eCursor_row_resize: - return [nsMacCursor cursorWithImageNamed: @"rowResize" hotSpot: NSMakePoint(8,8)]; + return [nsMacCursor cursorWithImageNamed:@"rowResize" hotSpot:NSMakePoint(8,8) type:aCursor]; default: - return [nsMacCursor cursorWithCursor: [NSCursor arrowCursor]]; + return [nsMacCursor cursorWithCursor:[NSCursor arrowCursor] type:aCursor]; } NS_OBJC_END_TRY_ABORT_BLOCK_NIL; @@ -220,43 +233,100 @@ static NSArray* sSpinCursorFrames = nil; NS_OBJC_END_TRY_ABORT_BLOCK_NIL; } -- (void) setCursor: (enum nsCursor) aCursor +- (nsresult) setCursor: (enum nsCursor) aCursor { - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; + NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; // Some plugins mess with our cursors and set a cursor that even // [NSCursor currentCursor] doesn't know about. In case that happens, just // reset the state. [[NSCursor currentCursor] set]; - nsMacCursor* currentCursor = [self getCursor: mCurrentCursor]; - - if (aCursor != mCurrentCursor || ![currentCursor isSet]) { - [currentCursor unset]; - [[self getCursor: aCursor] set]; - } - - if (mCurrentCursor != aCursor) { + nsCursor oldType = [mCurrentMacCursor type]; + if (oldType != aCursor) { if (aCursor == eCursor_none) { [NSCursor hide]; - } else if (mCurrentCursor == eCursor_none) { + } else if (oldType == eCursor_none) { [NSCursor unhide]; } } + [self setMacCursor:[self getCursor:aCursor]]; - mCurrentCursor = aCursor; + // if a custom cursor was previously set, release sCursorImgContainer + if (oldType == sCustomCursor) { + NS_IF_RELEASE(sCursorImgContainer); + } + return NS_OK; + + NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; +} - NS_OBJC_END_TRY_ABORT_BLOCK; +- (nsresult) setMacCursor: (nsMacCursor*) aMacCursor +{ + NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; + + if (mCurrentMacCursor != aMacCursor || ![mCurrentMacCursor isSet]) { + [aMacCursor retain]; + [mCurrentMacCursor unset]; + [aMacCursor set]; + [mCurrentMacCursor release]; + mCurrentMacCursor = aMacCursor; + } + + return NS_OK; + + NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; +} + +- (nsresult) setCursorWithImage: (imgIContainer*) aCursorImage hotSpotX: (PRUint32) aHotspotX hotSpotY: (PRUint32) aHotspotY +{ + NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; + // As the user moves the mouse, this gets called repeatedly with the same aCursorImage + if (sCursorImgContainer == aCursorImage && mCurrentMacCursor) { + [self setMacCursor:mCurrentMacCursor]; + return NS_OK; + } + + [[NSCursor currentCursor] set]; + PRInt32 width = 0, height = 0; + aCursorImage->GetWidth(&width); + aCursorImage->GetHeight(&height); + // prevent DoS attacks + if (width > 128 || height > 128) { + return NS_OK; + } + + NSImage *cursorImage; + nsresult rv = nsCocoaUtils::CreateNSImageFromImageContainer(aCursorImage, imgIContainer::FRAME_FIRST, &cursorImage); + if (NS_FAILED(rv) || !cursorImage) { + return NS_ERROR_FAILURE; + } + + // if the hotspot is nonsensical, make it 0,0 + aHotspotX = (aHotspotX < 0 || aHotspotX > (PRUint32) width - 1) ? 0 :aHotspotX; + aHotspotY = (aHotspotY < 0 || aHotspotY > (PRUint32) height - 1) ? 0 :aHotspotY; + + NSPoint hotSpot = ::NSMakePoint(aHotspotX, aHotspotY); + [self setMacCursor:[nsMacCursor cursorWithCursor:[[NSCursor alloc] initWithImage:cursorImage hotSpot:hotSpot] type:sCustomCursor]]; + [cursorImage release]; + + NS_IF_RELEASE(sCursorImgContainer); + sCursorImgContainer = aCursorImage; + NS_ADDREF(sCursorImgContainer); + + return NS_OK; + + NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; } - (nsMacCursor *) getCursor: (enum nsCursor) aCursor { NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; - nsMacCursor * result = [mCursors objectForKey: [NSNumber numberWithInt: aCursor]]; + nsMacCursor * result = [mCursors objectForKey:[NSNumber numberWithInt:aCursor]]; if (!result) { - result = [nsCursorManager createCursor: aCursor]; - [mCursors setObject: result forKey: [NSNumber numberWithInt: aCursor]]; + result = [nsCursorManager createCursor:aCursor]; + [mCursors setObject:result forKey:[NSNumber numberWithInt:aCursor]]; } return result; @@ -267,8 +337,10 @@ static NSArray* sSpinCursorFrames = nil; { NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - [[self getCursor: mCurrentCursor] unset]; - [mCursors release]; + [mCurrentMacCursor unset]; + [mCurrentMacCursor release]; + [mCursors release]; + NS_IF_RELEASE(sCursorImgContainer); [super dealloc]; NS_OBJC_END_TRY_ABORT_BLOCK; diff --git a/widget/src/cocoa/nsMacCursor.h b/widget/src/cocoa/nsMacCursor.h index ff5fef3800a..85c217de499 100644 --- a/widget/src/cocoa/nsMacCursor.h +++ b/widget/src/cocoa/nsMacCursor.h @@ -37,6 +37,7 @@ #define nsMacCursor_h_ #import +#import "nsIWidget.h" /*! @class nsMacCursor @abstract Represents a native Mac cursor. @@ -49,6 +50,7 @@ @private NSTimer *mTimer; @protected + nsCursor mType; int mFrameCounter; } @@ -56,11 +58,12 @@ @abstract Create a cursor by specifying a Cocoa NSCursor. @discussion Creates a cursor representing the given Cocoa built-in cursor. @param aCursor the NSCursor to use + @param aType the corresponding nsCursor constant @result an autoreleased instance of nsMacCursor representing the given NSCursor */ -+ (nsMacCursor *) cursorWithCursor: (NSCursor *) aCursor; ++ (nsMacCursor *) cursorWithCursor: (NSCursor *) aCursor type: (nsCursor) aType; -/*! @method cursorWithImageNamed:hotSpot: +/*! @method cursorWithImageNamed:hotSpot:type: @abstract Create a cursor by specifying the name of an image resource to use for the cursor and a hotspot. @discussion Creates a cursor by loading the named image using the +[NSImage imageNamed:] method.

The image must be compatible with any restrictions laid down by NSCursor. These vary @@ -68,19 +71,21 @@

The hotspot precisely determines the point where the user clicks when using the cursor.

@param aCursor the name of the image to use for the cursor @param aPoint the point within the cursor to use as the hotspot + @param aType the corresponding nsCursor constant @result an autoreleased instance of nsMacCursor that uses the given image and hotspot */ -+ (nsMacCursor *) cursorWithImageNamed: (NSString *) aCursorImage hotSpot: (NSPoint) aPoint; ++ (nsMacCursor *) cursorWithImageNamed: (NSString *) aCursorImage hotSpot: (NSPoint) aPoint type: (nsCursor) aType; -/*! @method cursorWithFrames: +/*! @method cursorWithFrames:type: @abstract Create an animated cursor by specifying the frames to use for the animation. @discussion Creates a cursor that will animate by cycling through the given frames. Each element of the array must be an instance of NSCursor @param aCursorFrames an array of NSCursor, representing the frames of an animated cursor, in the order they should be played. + @param aType the corresponding nsCursor constant @result an autoreleased instance of nsMacCursor that will animate the given cursor frames */ -+ (nsMacCursor *) cursorWithFrames: (NSArray *) aCursorFrames; ++ (nsMacCursor *) cursorWithFrames: (NSArray *) aCursorFrames type: (nsCursor) aType; /*! @method cocoaCursorWithImageNamed:hotSpot: @abstract Create a Cocoa NSCursor object with a Gecko image resource name and a hotspot point. @@ -118,6 +123,14 @@ */ - (BOOL) isAnimated; +/** @method cursorType + @abstract Get the cursor type for this cursor + @discussion This method returns the nsCursor constant that corresponds to this cursor, which is + equivalent to the CSS name for the cursor. + @result The nsCursor constant corresponding to this cursor, or nsCursor's 'eCursorCount' if the cursor + is a custom cursor loaded from a URI + */ +- (nsCursor) type; @end #endif // nsMacCursor_h_ diff --git a/widget/src/cocoa/nsMacCursor.mm b/widget/src/cocoa/nsMacCursor.mm index fa4b38d43b2..4ac48bd79a0 100644 --- a/widget/src/cocoa/nsMacCursor.mm +++ b/widget/src/cocoa/nsMacCursor.mm @@ -118,17 +118,19 @@ must be an instance of NSCursor @param aCursorFrames an array of NSCursor, representing the frames of an animated cursor, in the order they should be played. + @param aType the corresponding nsCursor constant @result an instance of nsCocoaCursor that will animate the given cursor frames */ -- (id) initWithFrames: (NSArray *) aCursorFrames; +- (id) initWithFrames: (NSArray *) aCursorFrames type: (nsCursor) aType; /*! @method initWithCursor: @abstract Create a cursor by specifying a Cocoa NSCursor. @discussion Creates a cursor representing the given Cocoa built-in cursor. @param aCursor the NSCursor to use + @param aType the corresponding nsCursor constant @result an instance of nsCocoaCursor representing the given NSCursor */ -- (id) initWithCursor: (NSCursor *) aCursor; +- (id) initWithCursor: (NSCursor *) aCursor type: (nsCursor) aType; /*! @method initWithImageNamed:hotSpot: @abstract Create a cursor by specifying the name of an image resource to use for the cursor and a hotspot. @@ -138,37 +140,38 @@

The hotspot precisely determines the point where the user clicks when using the cursor.

@param aCursor the name of the image to use for the cursor @param aPoint the point within the cursor to use as the hotspot + @param aType the corresponding nsCursor constant @result an instance of nsCocoaCursor that uses the given image and hotspot */ -- (id) initWithImageNamed: (NSString *) aCursorImage hotSpot: (NSPoint) aPoint; +- (id) initWithImageNamed: (NSString *) aCursorImage hotSpot: (NSPoint) aPoint type: (nsCursor) aType; @end @implementation nsMacCursor -+ (nsMacCursor *) cursorWithCursor: (NSCursor *) aCursor ++ (nsMacCursor *) cursorWithCursor: (NSCursor *) aCursor type: (nsCursor) aType { NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; - return [[[nsCocoaCursor alloc] initWithCursor: aCursor] autorelease]; + return [[[nsCocoaCursor alloc] initWithCursor:aCursor type:aType] autorelease]; NS_OBJC_END_TRY_ABORT_BLOCK_NIL; } -+ (nsMacCursor *) cursorWithImageNamed: (NSString *) aCursorImage hotSpot: (NSPoint) aPoint ++ (nsMacCursor *) cursorWithImageNamed: (NSString *) aCursorImage hotSpot: (NSPoint) aPoint type: (nsCursor) aType { NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; - return [[[nsCocoaCursor alloc] initWithImageNamed: aCursorImage hotSpot: aPoint] autorelease]; + return [[[nsCocoaCursor alloc] initWithImageNamed:aCursorImage hotSpot:aPoint type:aType] autorelease]; NS_OBJC_END_TRY_ABORT_BLOCK_NIL; } -+ (nsMacCursor *) cursorWithFrames: (NSArray *) aCursorFrames ++ (nsMacCursor *) cursorWithFrames: (NSArray *) aCursorFrames type: (nsCursor) aType { NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; - return [[[nsCocoaCursor alloc] initWithFrames: aCursorFrames] autorelease]; + return [[[nsCocoaCursor alloc] initWithFrames:aCursorFrames type:aType] autorelease]; NS_OBJC_END_TRY_ABORT_BLOCK_NIL; } @@ -201,7 +204,7 @@ cursorImage = [[[NSImage alloc] initWithContentsOfFile:pathToImage] autorelease]; if (!cursorImage) goto INIT_FAILURE; - return [[[NSCursor alloc] initWithImage: cursorImage hotSpot: aPoint] autorelease]; + return [[[NSCursor alloc] initWithImage:cursorImage hotSpot:aPoint] autorelease]; INIT_FAILURE: NS_WARNING("Problem getting path to cursor image file!"); @@ -222,9 +225,9 @@ INIT_FAILURE: if ([self isAnimated]) { [self createTimer]; } - //if the cursor isn't animated or the timer creation fails for any reason... + // if the cursor isn't animated or the timer creation fails for any reason... if (!mTimer) { - [self setFrame: 0]; + [self setFrame:0]; } } @@ -240,7 +243,7 @@ INIT_FAILURE: - (int) numFrames { - //subclasses need to override this to support animation + // subclasses need to override this to support animation return 1; } @@ -255,11 +258,11 @@ INIT_FAILURE: NS_OBJC_BEGIN_TRY_ABORT_BLOCK; if (!mTimer) { - mTimer = [[NSTimer scheduledTimerWithTimeInterval: 0.25 - target: self - selector: @selector(advanceAnimatedCursor:) - userInfo: nil - repeats: YES] retain]; + mTimer = [[NSTimer scheduledTimerWithTimeInterval:0.25 + target:self + selector:@selector(advanceAnimatedCursor:) + userInfo:nil + repeats:YES] retain]; } NS_OBJC_END_TRY_ABORT_BLOCK; @@ -283,7 +286,7 @@ INIT_FAILURE: NS_OBJC_BEGIN_TRY_ABORT_BLOCK; if ([aTimer isValid]) { - [self setFrame: [self getNextCursorFrame]]; + [self setFrame:[self getNextCursorFrame]]; } NS_OBJC_END_TRY_ABORT_BLOCK; @@ -291,7 +294,11 @@ INIT_FAILURE: - (void) setFrame: (int) aFrameIndex { - //subclasses need to do something useful here + // subclasses need to do something useful here +} + +- (nsCursor) type { + return mType; } - (void) dealloc @@ -308,7 +315,7 @@ INIT_FAILURE: @implementation nsCocoaCursor -- (id) initWithFrames: (NSArray *) aCursorFrames +- (id) initWithFrames: (NSArray *) aCursorFrames type: (nsCursor) aType { NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; @@ -316,30 +323,31 @@ INIT_FAILURE: NSEnumerator *it = [aCursorFrames objectEnumerator]; NSObject *frame = nil; while ((frame = [it nextObject])) { - NS_ASSERTION([frame isKindOfClass: [NSCursor class]], "Invalid argument: All frames must be of type NSCursor"); + NS_ASSERTION([frame isKindOfClass:[NSCursor class]], "Invalid argument: All frames must be of type NSCursor"); } mFrames = [aCursorFrames retain]; mFrameCounter = 0; + mType = aType; return self; NS_OBJC_END_TRY_ABORT_BLOCK_NIL; } -- (id) initWithCursor: (NSCursor *) aCursor +- (id) initWithCursor: (NSCursor *) aCursor type: (nsCursor) aType { NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; - NSArray *frame = [NSArray arrayWithObjects: aCursor, nil]; - return [self initWithFrames: frame]; + NSArray *frame = [NSArray arrayWithObjects:aCursor, nil]; + return [self initWithFrames:frame type:aType]; NS_OBJC_END_TRY_ABORT_BLOCK_NIL; } -- (id) initWithImageNamed: (NSString *) aCursorImage hotSpot: (NSPoint) aPoint +- (id) initWithImageNamed: (NSString *) aCursorImage hotSpot: (NSPoint) aPoint type: (nsCursor) aType { NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; - return [self initWithCursor: [nsMacCursor cocoaCursorWithImageNamed: aCursorImage hotSpot: aPoint]]; + return [self initWithCursor:[nsMacCursor cocoaCursorWithImageNamed:aCursorImage hotSpot:aPoint] type:aType]; NS_OBJC_END_TRY_ABORT_BLOCK_NIL; } @@ -353,7 +361,7 @@ INIT_FAILURE: { NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - NSCursor* newCursor = [mFrames objectAtIndex: aFrameIndex]; + NSCursor* newCursor = [mFrames objectAtIndex:aFrameIndex]; [newCursor set]; mLastSetCocoaCursor = newCursor;