Implement CSS cursor support for Mac OS X. b=286304 r=josh
authorlordpixel@mac.com
Thu, 11 Mar 2010 11:44:31 -0500
changeset 39278 b0a64e39c7c0990ec706825c6ac700697a0f568d
parent 39277 79443803350cb2fd207b82d1b9c84c7c29a45573
child 39279 37ea3b54ad74ca740c3d7eab0f47c2b581226d2d
push idunknown
push userunknown
push dateunknown
reviewersjosh
bugs286304
milestone1.9.3a3pre
Implement CSS cursor support for Mac OS X. b=286304 r=josh
widget/src/cocoa/nsChildView.mm
widget/src/cocoa/nsCocoaUtils.h
widget/src/cocoa/nsCocoaUtils.mm
widget/src/cocoa/nsCursorManager.h
widget/src/cocoa/nsCursorManager.mm
widget/src/cocoa/nsMacCursor.h
widget/src/cocoa/nsMacCursor.mm
--- a/widget/src/cocoa/nsChildView.mm
+++ b/widget/src/cocoa/nsChildView.mm
@@ -972,27 +972,31 @@ NS_IMETHODIMP nsChildView::SetFocus(PRBo
 NS_IMETHODIMP nsChildView::SetCursor(nsCursor aCursor)
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
 
   if ([mView isDragInProgress])
     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;
 }
 
 // implement to fix "hidden virtual function" warning
 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 -
 
 // Get this component dimension
 NS_IMETHODIMP nsChildView::GetBounds(nsIntRect &aRect)
 {
   aRect = mBounds;
--- a/widget/src/cocoa/nsCocoaUtils.h
+++ b/widget/src/cocoa/nsCocoaUtils.h
@@ -40,16 +40,17 @@
 
 #ifndef nsCocoaUtils_h_
 #define nsCocoaUtils_h_
 
 #import <Cocoa/Cocoa.h>
 
 #include "nsRect.h"
 #include "nsObjCExceptions.h"
+#include "imgIContainer.h"
 
 class nsIWidget;
 
 // Used to retain a Cocoa object for the remainder of a method's execution.
 class nsAutoRetainCocoaObject {
 public:
 nsAutoRetainCocoaObject(id anObject)
 {
@@ -125,11 +126,42 @@ class nsCocoaUtils
 
   // Hides the Menu bar and the Dock. Multiple hide/show requests can be nested.
   static void HideOSChromeOnScreen(PRBool aShouldHide, NSScreen* aScreen);
 
   static nsIWidget* GetHiddenWindowWidget();
 
   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 <code>CGImageRef</code> from a frame contained in an <code>imgIContainer</code>.
+      Copies the pixel data from the indicated frame of the <code>imgIContainer</code> into a new <code>CGImageRef</code>.
+      The caller owns the <code>CGImageRef</code>. 
+      @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 <code>NSImage</code> from a <code>CGImageRef</code>.
+      Copies the pixel data from the <code>CGImageRef</code> into a new <code>NSImage</code>.
+      The caller owns the <code>NSImage</code>. 
+      @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 <code>NSImage</code> from a frame of an <code>imgIContainer</code>.
+      Combines the two methods above. The caller owns the <code>NSImage</code>.
+      @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_
--- a/widget/src/cocoa/nsCocoaUtils.mm
+++ b/widget/src/cocoa/nsCocoaUtils.mm
@@ -33,16 +33,17 @@
  * use your version of this file under the terms of the MPL, indicate your
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
+#include "gfxImageSurface.h"
 #include "nsCocoaUtils.h"
 #include "nsMenuBarX.h"
 #include "nsCocoaWindow.h"
 #include "nsCOMPtr.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsIAppShellService.h"
 #include "nsIXULWindow.h"
 #include "nsIBaseWindow.h"
@@ -238,8 +239,89 @@ void nsCocoaUtils::CleanUpAfterNativeApp
   NSWindow* mainWindow = [NSApp mainWindow];
   if (!mainWindow)
     hiddenWindowMenuBar->Paint();
   else
     [WindowDelegate paintMenubarForWindow:mainWindow];
 
   NS_OBJC_END_TRY_ABORT_BLOCK;
 }
+
+nsresult nsCocoaUtils::CreateCGImageFromImageContainer(imgIContainer *aImage, PRUint32 aWhichFrame, CGImageRef *aResult)
+{
+  nsRefPtr<gfxImageSurface> 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;
+}
+
--- a/widget/src/cocoa/nsCursorManager.h
+++ b/widget/src/cocoa/nsCursorManager.h
@@ -45,26 +45,37 @@
     @discussion Use <code>nsCusorManager</code> to set the current cursor using an XP <code>nsCusor</code> enum value.
                 <code>nsCursorManager</code> encapsulates the details of setting different types of cursors, animating
                 cursors and cleaning up cursors when they are no longer in use.
  */
 @interface nsCursorManager : NSObject
 {
   @private
   NSMutableDictionary *mCursors;
-  nsCursor mCurrentCursor;
+  nsMacCursor *mCurrentMacCursor;
 }
 
 /*! @method     setCursor:
     @abstract   Sets the current cursor.
     @discussion Sets the current cursor to the cursor indicated by the XP cursor constant given as an argument.
                 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.
     @discussion Use this method to obtain a reference to the cursor manager.
     @result a reference to the cursor manager
 */
 + (nsCursorManager *) sharedInstance;
 
--- a/widget/src/cocoa/nsCursorManager.mm
+++ b/widget/src/cocoa/nsCursorManager.mm
@@ -30,37 +30,50 @@
  * use your version of this file under the terms of the MPL, indicate your
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
+#include "imgIContainer.h"
+#include "nsCocoaUtils.h"
 #include "nsCursorManager.h"
 #include "nsObjCExceptions.h"
 #include <math.h>
 
 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.
 */
 @interface nsCursorManager(PrivateMethods)
 /*! @method     getCursor:
     @abstract   Get a reference to the native Mac representation of a cursor.
     @discussion Gets a reference to the Mac native implementation of a cursor.
                 If the cursor has been requested before, it is retreived from the cursor cache,
                 otherwise it is created and cached.
     @param      aCursor the cursor to get
     @result     the Mac native implementation of the cursor
 */
 - (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
     @param      aCursor the cursor to create
     @result     the Mac native implementation of the cursor
 */
 + (nsMacCursor *) createCursor: (enum nsCursor) aCursor;
 
@@ -96,118 +109,118 @@ static NSArray* sSpinCursorFrames = nil;
   gInstance = nil;
 
   [sSpinCursorFrames release];
   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;
 
   switch(aCursor)
   {
     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;
 }
 
 - (id) init
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
@@ -215,63 +228,122 @@ static NSArray* sSpinCursorFrames = nil;
   if ((self = [super init])) {
     mCursors = [[NSMutableDictionary alloc] initWithCapacity:25];
   }
   return self;
 
   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;
+}
+
+- (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;
+}
 
-  NS_OBJC_END_TRY_ABORT_BLOCK;
+- (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;
 
   NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
 }
 
 - (void) dealloc
 {
   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;
 }
 
 @end
--- a/widget/src/cocoa/nsMacCursor.h
+++ b/widget/src/cocoa/nsMacCursor.h
@@ -32,60 +32,65 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef nsMacCursor_h_
 #define nsMacCursor_h_
 
 #import <Cocoa/Cocoa.h>
+#import "nsIWidget.h"
 
 /*! @class      nsMacCursor
     @abstract   Represents a native Mac cursor.
     @discussion <code>nsMacCursor</code> provides a simple API for creating and working with native Macintosh cursors.
                 Cursors can be created used without needing to be aware of the way different cursors are implemented,
                 in particular the details of managing an animated cursor are hidden.
 */
 @interface nsMacCursor : NSObject
 {
   @private
   NSTimer *mTimer;
   @protected
+  nsCursor mType;
   int mFrameCounter;    
 }
 
 /*! @method     cursorWithCursor:
     @abstract   Create a cursor by specifying a Cocoa <code>NSCursor</code>.
     @discussion Creates a cursor representing the given Cocoa built-in cursor.
     @param      aCursor the <code>NSCursor</code> to use
+    @param      aType the corresponding <code>nsCursor</code> constant
     @result     an autoreleased instance of <code>nsMacCursor</code> representing the given <code>NSCursor</code>
  */
-+ (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 <code>+[NSImage imageNamed:]</code> method.
                 <p>The image must be compatible with any restrictions laid down by <code>NSCursor</code>. These vary
                 by operating system version.</p>
                 <p>The hotspot precisely determines the point where the user clicks when using the cursor.</p>
     @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 <code>nsCursor</code> constant
     @result     an autoreleased instance of <code>nsMacCursor</code> 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 <code>NSCursor</code>
     @param      aCursorFrames an array of <code>NSCursor</code>, representing the frames of an animated cursor, in the
                 order they should be played.
+    @param      aType the corresponding <code>nsCursor</code> constant
     @result     an autoreleased instance of <code>nsMacCursor</code> 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.
     @discussion Create a Cocoa NSCursor object with a Gecko image resource name and a hotspot point.
     @param      imageName the name of the gecko image resource, "tiff" extension is assumed, do not append.
     @param      aPoint the point within the cursor to use as the hotspot
     @result     an autoreleased instance of <code>nsMacCursor</code> that will animate the given cursor frames
  */
@@ -113,11 +118,19 @@
 
 /*! @method     isAnimated
     @abstract   Tests whether this cursor is animated.
     @discussion Use this method to determine whether a cursor is animated
     @result     YES if the cursor is animated (has more than one frame), NO if it is a simple static cursor.
  */
 - (BOOL) isAnimated;
 
+/** @method     cursorType
+    @abstract   Get the cursor type for this cursor
+    @discussion This method returns the <code>nsCursor</code> 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_
--- a/widget/src/cocoa/nsMacCursor.mm
+++ b/widget/src/cocoa/nsMacCursor.mm
@@ -113,67 +113,70 @@
 }
 
 /*! @method     initWithFrames:
     @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 <code>NSCursor</code>
     @param      aCursorFrames an array of <code>NSCursor</code>, representing the frames of an animated cursor, in the
                 order they should be played.
+    @param      aType the corresponding <code>nsCursor</code> constant
     @result     an instance of <code>nsCocoaCursor</code> 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 <code>NSCursor</code>.
     @discussion Creates a cursor representing the given Cocoa built-in cursor.
     @param      aCursor the <code>NSCursor</code> to use
+    @param      aType the corresponding <code>nsCursor</code> constant
     @result     an instance of <code>nsCocoaCursor</code> representing the given <code>NSCursor</code>
 */
-- (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.
     @discussion Creates a cursor by loading the named image using the <code>+[NSImage imageNamed:]</code> method.
                 <p>The image must be compatible with any restrictions laid down by <code>NSCursor</code>. These vary
                 by operating system version.</p>
                 <p>The hotspot precisely determines the point where the user clicks when using the cursor.</p>
     @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 <code>nsCursor</code> constant
     @result     an instance of <code>nsCocoaCursor</code> 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;
 }
 
 + (NSCursor *) cocoaCursorWithImageNamed: (NSString *) imageName hotSpot: (NSPoint) aPoint
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
 
@@ -196,17 +199,17 @@
   if (!pathToImage)
     goto INIT_FAILURE;
   pathToImage = [pathToImage stringByAppendingPathComponent:imageName];
   pathToImage = [pathToImage stringByAppendingPathExtension:@"tiff"];
 
   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!");
   [self release];
   return nil;
 
   NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
 }
@@ -217,54 +220,54 @@ INIT_FAILURE:
   return NO;
 }
 
 - (void) set
 {
   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];
   }
 }
 
 - (void) unset
 {
   [self destroyTimer];    
 }
 
 - (BOOL) isAnimated
 {
   return [self numFrames] > 1;
 }
 
 - (int) numFrames
 {
-  //subclasses need to override this to support animation
+  // subclasses need to override this to support animation
   return 1;
 }
 
 - (int) getNextCursorFrame
 {
   mFrameCounter = (mFrameCounter + 1) % [self numFrames];
   return mFrameCounter;
 }
 
 - (void) createTimer
 {
   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;
 }
 
 - (void) destroyTimer
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
@@ -278,87 +281,92 @@ INIT_FAILURE:
   NS_OBJC_END_TRY_ABORT_BLOCK;
 }
 
 - (void) advanceAnimatedCursor: (NSTimer *) aTimer
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
 
   if ([aTimer isValid]) {
-    [self setFrame: [self getNextCursorFrame]];
+    [self setFrame:[self getNextCursorFrame]];
   }
 
   NS_OBJC_END_TRY_ABORT_BLOCK;
 }
 
 - (void) setFrame: (int) aFrameIndex
 {
-  //subclasses need to do something useful here
+  // subclasses need to do something useful here
+}
+
+- (nsCursor) type {
+  return mType;
 }
 
 - (void) dealloc
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
 
   [self destroyTimer];
   [super dealloc];
 
   NS_OBJC_END_TRY_ABORT_BLOCK;
 }
 
 @end
 
 @implementation nsCocoaCursor
 
-- (id) initWithFrames: (NSArray *) aCursorFrames
+- (id) initWithFrames: (NSArray *) aCursorFrames type: (nsCursor) aType
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
 
   self = [super init];
   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;
 }
 
 - (BOOL) isSet
 {
   return [NSCursor currentCursor] == mLastSetCocoaCursor;
 }
 
 - (void) setFrame: (int) aFrameIndex
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
 
-  NSCursor* newCursor = [mFrames objectAtIndex: aFrameIndex];
+  NSCursor* newCursor = [mFrames objectAtIndex:aFrameIndex];
   [newCursor set];
   mLastSetCocoaCursor = newCursor;
 
   NS_OBJC_END_TRY_ABORT_BLOCK;
 }
 
 - (int) numFrames
 {