Bumping manifests a=b2g-bump
[gecko.git] / widget / cocoa / nsCursorManager.mm
blobc4281a438aaa66defaea75ef6980a15fee4d367c
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 #include "imgIContainer.h"
6 #include "nsCocoaUtils.h"
7 #include "nsCursorManager.h"
8 #include "nsObjCExceptions.h"
9 #include <math.h>
11 static nsCursorManager *gInstance;
12 static CGFloat sCursorScaleFactor = 0.0f;
13 static imgIContainer *sCursorImgContainer = nullptr;
14 static const nsCursor sCustomCursor = eCursorCount;
16 /*! @category nsCursorManager(PrivateMethods)
17     Private methods for the cursor manager class.
19 @interface nsCursorManager(PrivateMethods)
20 /*! @method     getCursor:
21     @abstract   Get a reference to the native Mac representation of a cursor.
22     @discussion Gets a reference to the Mac native implementation of a cursor.
23                 If the cursor has been requested before, it is retreived from the cursor cache,
24                 otherwise it is created and cached.
25     @param      aCursor the cursor to get
26     @result     the Mac native implementation of the cursor
28 - (nsMacCursor *) getCursor: (nsCursor) aCursor;
30 /*! @method     setMacCursor:
31  @abstract   Set the current Mac native cursor
32  @discussion Sets the current cursor - this routine is what actually causes the cursor to change.
33  The argument is retained and the old cursor is released.
34  @param      aMacCursor the cursor to set
35  @result     NS_OK
36  */
37 - (nsresult) setMacCursor: (nsMacCursor*) aMacCursor;
39 /*! @method     createCursor:
40     @abstract   Create a Mac native representation of a cursor.
41     @discussion Creates a version of the Mac native representation of this cursor
42     @param      aCursor the cursor to create
43     @result     the Mac native implementation of the cursor
45 + (nsMacCursor *) createCursor: (enum nsCursor) aCursor;
47 @end
49 @implementation nsCursorManager
51 + (nsCursorManager *) sharedInstance
53   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
55   if (!gInstance) {
56     gInstance = [[nsCursorManager alloc] init];
57   }
58   return gInstance;
60   NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
63 + (void) dispose
65   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
67   [gInstance release];
68   gInstance = nil;
70   NS_OBJC_END_TRY_ABORT_BLOCK;
73 + (nsMacCursor *) createCursor: (enum nsCursor) aCursor
75   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
77   switch(aCursor)
78   {
79     SEL cursorSelector;
80     case eCursor_standard:
81       return [nsMacCursor cursorWithCursor:[NSCursor arrowCursor] type:aCursor];
82     case eCursor_wait:
83     case eCursor_spinning:
84     {
85       return [nsMacCursor cursorWithCursor:[NSCursor busyButClickableCursor] type:aCursor];
86     }
87     case eCursor_select:
88       return [nsMacCursor cursorWithCursor:[NSCursor IBeamCursor] type:aCursor];
89     case eCursor_hyperlink:
90       return [nsMacCursor cursorWithCursor:[NSCursor pointingHandCursor] type:aCursor];
91     case eCursor_crosshair:
92       return [nsMacCursor cursorWithCursor:[NSCursor crosshairCursor] type:aCursor];
93     case eCursor_move:
94       return [nsMacCursor cursorWithImageNamed:@"move" hotSpot:NSMakePoint(12,12) type:aCursor];
95     case eCursor_help:
96       return [nsMacCursor cursorWithImageNamed:@"help" hotSpot:NSMakePoint(12,12) type:aCursor];
97     case eCursor_copy:
98       cursorSelector = @selector(dragCopyCursor);
99       return [nsMacCursor cursorWithCursor:[NSCursor respondsToSelector:cursorSelector] ?
100               [NSCursor performSelector:cursorSelector] :
101               [NSCursor arrowCursor] type:aCursor];
102     case eCursor_alias:
103       cursorSelector = @selector(dragLinkCursor);
104       return [nsMacCursor cursorWithCursor:[NSCursor respondsToSelector:cursorSelector] ?
105               [NSCursor performSelector:cursorSelector] :
106               [NSCursor arrowCursor] type:aCursor];
107     case eCursor_context_menu:
108       cursorSelector = @selector(contextualMenuCursor);
109       return [nsMacCursor cursorWithCursor:[NSCursor respondsToSelector:cursorSelector] ?
110               [NSCursor performSelector:cursorSelector] :
111               [NSCursor arrowCursor] type:aCursor];
112     case eCursor_cell:
113       return [nsMacCursor cursorWithImageNamed:@"cell" hotSpot:NSMakePoint(12,12) type:aCursor];
114     case eCursor_grab:
115       return [nsMacCursor cursorWithCursor:[NSCursor openHandCursor] type:aCursor];
116     case eCursor_grabbing:
117       return [nsMacCursor cursorWithCursor:[NSCursor closedHandCursor] type:aCursor];
118     case eCursor_zoom_in:
119       return [nsMacCursor cursorWithImageNamed:@"zoomIn" hotSpot:NSMakePoint(10,10) type:aCursor];
120     case eCursor_zoom_out:
121       return [nsMacCursor cursorWithImageNamed:@"zoomOut" hotSpot:NSMakePoint(10,10) type:aCursor];
122     case eCursor_vertical_text:
123       return [nsMacCursor cursorWithImageNamed:@"vtIBeam" hotSpot:NSMakePoint(12,11) type:aCursor];
124     case eCursor_all_scroll:
125       return [nsMacCursor cursorWithCursor:[NSCursor openHandCursor] type:aCursor];
126     case eCursor_not_allowed:
127     case eCursor_no_drop:
128       cursorSelector = @selector(operationNotAllowedCursor);
129       return [nsMacCursor cursorWithCursor:[NSCursor respondsToSelector:cursorSelector] ?
130               [NSCursor performSelector:cursorSelector] :
131               [NSCursor arrowCursor] type:aCursor];
132     // Resize Cursors:
133     // North
134     case eCursor_n_resize:
135         return [nsMacCursor cursorWithCursor:[NSCursor resizeUpCursor] type:aCursor];
136     // North East
137     case eCursor_ne_resize:
138         return [nsMacCursor cursorWithImageNamed:@"sizeNE" hotSpot:NSMakePoint(12,11) type:aCursor];
139     // East
140     case eCursor_e_resize:
141         return [nsMacCursor cursorWithCursor:[NSCursor resizeRightCursor] type:aCursor];
142     // South East
143     case eCursor_se_resize:
144         return [nsMacCursor cursorWithImageNamed:@"sizeSE" hotSpot:NSMakePoint(12,12) type:aCursor];
145     // South
146     case eCursor_s_resize:
147         return [nsMacCursor cursorWithCursor:[NSCursor resizeDownCursor] type:aCursor];
148     // South West
149     case eCursor_sw_resize:
150         return [nsMacCursor cursorWithImageNamed:@"sizeSW" hotSpot:NSMakePoint(10,12) type:aCursor];
151     // West
152     case eCursor_w_resize:
153         return [nsMacCursor cursorWithCursor:[NSCursor resizeLeftCursor] type:aCursor];
154     // North West
155     case eCursor_nw_resize:
156         return [nsMacCursor cursorWithImageNamed:@"sizeNW" hotSpot:NSMakePoint(11,11) type:aCursor];
157     // North & South
158     case eCursor_ns_resize:
159         return [nsMacCursor cursorWithCursor:[NSCursor resizeUpDownCursor] type:aCursor];
160     // East & West
161     case eCursor_ew_resize:
162         return [nsMacCursor cursorWithCursor:[NSCursor resizeLeftRightCursor] type:aCursor];
163     // North East & South West
164     case eCursor_nesw_resize:
165         return [nsMacCursor cursorWithImageNamed:@"sizeNESW" hotSpot:NSMakePoint(12,12) type:aCursor];
166     // North West & South East
167     case eCursor_nwse_resize:
168         return [nsMacCursor cursorWithImageNamed:@"sizeNWSE" hotSpot:NSMakePoint(12,12) type:aCursor];
169     // Column Resize
170     case eCursor_col_resize:
171         return [nsMacCursor cursorWithImageNamed:@"colResize" hotSpot:NSMakePoint(12,12) type:aCursor];
172     // Row Resize
173     case eCursor_row_resize:
174         return [nsMacCursor cursorWithImageNamed:@"rowResize" hotSpot:NSMakePoint(12,12) type:aCursor];
175     default:
176         return [nsMacCursor cursorWithCursor:[NSCursor arrowCursor] type:aCursor];
177   }
179   NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
182 - (id) init
184   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
186   if ((self = [super init])) {
187     mCursors = [[NSMutableDictionary alloc] initWithCapacity:25];
188   }
189   return self;
191   NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
194 - (nsresult) setCursor: (enum nsCursor) aCursor
196   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
198   // Some plugins mess with our cursors and set a cursor that even
199   // [NSCursor currentCursor] doesn't know about. In case that happens, just
200   // reset the state.
201   [[NSCursor currentCursor] set];
203   nsCursor oldType = [mCurrentMacCursor type];
204   if (oldType != aCursor) {
205     if (aCursor == eCursor_none) {
206       [NSCursor hide];
207     } else if (oldType == eCursor_none) {
208       [NSCursor unhide];
209     }
210   }
211   [self setMacCursor:[self getCursor:aCursor]];
213   // if a custom cursor was previously set, release sCursorImgContainer
214   if (oldType == sCustomCursor) {
215     NS_IF_RELEASE(sCursorImgContainer);
216   }
217   return NS_OK;
218   
219   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
222 - (nsresult) setMacCursor: (nsMacCursor*) aMacCursor
224   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
226   if (mCurrentMacCursor != aMacCursor || ![mCurrentMacCursor isSet]) {
227     [aMacCursor retain];
228     [mCurrentMacCursor unset];
229     [aMacCursor set];
230     [mCurrentMacCursor release];
231     mCurrentMacCursor = aMacCursor;
232   }
234   return NS_OK;
235   
236   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
239 - (nsresult) setCursorWithImage: (imgIContainer*) aCursorImage hotSpotX: (uint32_t) aHotspotX hotSpotY: (uint32_t) aHotspotY scaleFactor: (CGFloat) scaleFactor
241   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
242   // As the user moves the mouse, this gets called repeatedly with the same aCursorImage
243   if (sCursorImgContainer == aCursorImage && sCursorScaleFactor == scaleFactor && mCurrentMacCursor) {
244     [self setMacCursor:mCurrentMacCursor];
245     return NS_OK;
246   }
247   
248   [[NSCursor currentCursor] set];
249   int32_t width = 0, height = 0;
250   aCursorImage->GetWidth(&width);
251   aCursorImage->GetHeight(&height);
252   // prevent DoS attacks
253   if (width > 128 || height > 128) {
254     return NS_OK;
255   }
257   NSImage *cursorImage;
258   nsresult rv = nsCocoaUtils::CreateNSImageFromImageContainer(aCursorImage, imgIContainer::FRAME_FIRST, &cursorImage, scaleFactor);
259   if (NS_FAILED(rv) || !cursorImage) {
260     return NS_ERROR_FAILURE;
261   }
263   // if the hotspot is nonsensical, make it 0,0
264   aHotspotX = (aHotspotX > (uint32_t)width - 1) ? 0 : aHotspotX;
265   aHotspotY = (aHotspotY > (uint32_t)height - 1) ? 0 : aHotspotY;
267   NSPoint hotSpot = ::NSMakePoint(aHotspotX, aHotspotY);
268   [self setMacCursor:[nsMacCursor cursorWithCursor:[[NSCursor alloc] initWithImage:cursorImage hotSpot:hotSpot] type:sCustomCursor]];
269   [cursorImage release];
270   
271   NS_IF_RELEASE(sCursorImgContainer);
272   sCursorImgContainer = aCursorImage;
273   sCursorScaleFactor = scaleFactor;
274   NS_ADDREF(sCursorImgContainer);
275   
276   return NS_OK;
277   
278   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
281 - (nsMacCursor *) getCursor: (enum nsCursor) aCursor
283   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
285   nsMacCursor * result = [mCursors objectForKey:[NSNumber numberWithInt:aCursor]];
286   if (!result) {
287     result = [nsCursorManager createCursor:aCursor];
288     [mCursors setObject:result forKey:[NSNumber numberWithInt:aCursor]];
289   }
290   return result;
292   NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
295 - (void) dealloc
297   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
299   [mCurrentMacCursor unset];
300   [mCurrentMacCursor release];
301   [mCursors release];
302   NS_IF_RELEASE(sCursorImgContainer);
303   [super dealloc];
305   NS_OBJC_END_TRY_ABORT_BLOCK;
308 @end