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"
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
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;
49 @implementation nsCursorManager
51 + (nsCursorManager*)sharedInstance {
52 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
55 gInstance = [[nsCursorManager alloc] init];
59 NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
63 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
68 NS_OBJC_END_TRY_ABORT_BLOCK;
71 + (nsMacCursor*)createCursor:(enum nsCursor)aCursor {
72 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
76 case eCursor_standard:
77 return [nsMacCursor cursorWithCursor:[NSCursor arrowCursor] type:aCursor];
79 case eCursor_spinning: {
80 return [nsMacCursor cursorWithCursor:[NSCursor busyButClickableCursor] type:aCursor];
83 return [nsMacCursor cursorWithCursor:[NSCursor IBeamCursor] type:aCursor];
84 case eCursor_hyperlink:
85 return [nsMacCursor cursorWithCursor:[NSCursor pointingHandCursor] type:aCursor];
86 case eCursor_crosshair:
87 return [nsMacCursor cursorWithCursor:[NSCursor crosshairCursor] type:aCursor];
89 return [nsMacCursor cursorWithImageNamed:@"move" hotSpot:NSMakePoint(12, 12) type:aCursor];
91 return [nsMacCursor cursorWithImageNamed:@"help" hotSpot:NSMakePoint(12, 12) type:aCursor];
93 cursorSelector = @selector(dragCopyCursor);
94 return [nsMacCursor cursorWithCursor:[NSCursor respondsToSelector:cursorSelector]
95 ? [NSCursor performSelector:cursorSelector]
96 : [NSCursor arrowCursor]
99 cursorSelector = @selector(dragLinkCursor);
100 return [nsMacCursor cursorWithCursor:[NSCursor respondsToSelector:cursorSelector]
101 ? [NSCursor performSelector:cursorSelector]
102 : [NSCursor arrowCursor]
104 case eCursor_context_menu:
105 cursorSelector = @selector(contextualMenuCursor);
106 return [nsMacCursor cursorWithCursor:[NSCursor respondsToSelector:cursorSelector]
107 ? [NSCursor performSelector:cursorSelector]
108 : [NSCursor arrowCursor]
111 return [nsMacCursor cursorWithImageNamed:@"cell" hotSpot:NSMakePoint(12, 12) type:aCursor];
113 return [nsMacCursor cursorWithCursor:[NSCursor openHandCursor] type:aCursor];
114 case eCursor_grabbing:
115 return [nsMacCursor cursorWithCursor:[NSCursor closedHandCursor] type:aCursor];
116 case eCursor_zoom_in:
117 return [nsMacCursor cursorWithImageNamed:@"zoomIn" hotSpot:NSMakePoint(10, 10) type:aCursor];
118 case eCursor_zoom_out:
119 return [nsMacCursor cursorWithImageNamed:@"zoomOut" hotSpot:NSMakePoint(10, 10) type:aCursor];
120 case eCursor_vertical_text:
121 return [nsMacCursor cursorWithImageNamed:@"vtIBeam" hotSpot:NSMakePoint(12, 11) type:aCursor];
122 case eCursor_all_scroll:
123 return [nsMacCursor cursorWithCursor:[NSCursor openHandCursor] type:aCursor];
124 case eCursor_not_allowed:
125 case eCursor_no_drop:
126 cursorSelector = @selector(operationNotAllowedCursor);
127 return [nsMacCursor cursorWithCursor:[NSCursor respondsToSelector:cursorSelector]
128 ? [NSCursor performSelector:cursorSelector]
129 : [NSCursor arrowCursor]
133 case eCursor_n_resize:
134 return [nsMacCursor cursorWithCursor:[NSCursor resizeUpCursor] type:aCursor];
136 case eCursor_ne_resize:
137 return [nsMacCursor cursorWithImageNamed:@"sizeNE" hotSpot:NSMakePoint(12, 11) type:aCursor];
139 case eCursor_e_resize:
140 return [nsMacCursor cursorWithCursor:[NSCursor resizeRightCursor] type:aCursor];
142 case eCursor_se_resize:
143 return [nsMacCursor cursorWithImageNamed:@"sizeSE" hotSpot:NSMakePoint(12, 12) type:aCursor];
145 case eCursor_s_resize:
146 return [nsMacCursor cursorWithCursor:[NSCursor resizeDownCursor] type:aCursor];
148 case eCursor_sw_resize:
149 return [nsMacCursor cursorWithImageNamed:@"sizeSW" hotSpot:NSMakePoint(10, 12) type:aCursor];
151 case eCursor_w_resize:
152 return [nsMacCursor cursorWithCursor:[NSCursor resizeLeftCursor] type:aCursor];
154 case eCursor_nw_resize:
155 return [nsMacCursor cursorWithImageNamed:@"sizeNW" hotSpot:NSMakePoint(11, 11) type:aCursor];
157 case eCursor_ns_resize:
158 return [nsMacCursor cursorWithCursor:[NSCursor resizeUpDownCursor] type:aCursor];
160 case eCursor_ew_resize:
161 return [nsMacCursor cursorWithCursor:[NSCursor resizeLeftRightCursor] type:aCursor];
162 // North East & South West
163 case eCursor_nesw_resize:
164 return [nsMacCursor cursorWithImageNamed:@"sizeNESW"
165 hotSpot:NSMakePoint(12, 12)
167 // North West & South East
168 case eCursor_nwse_resize:
169 return [nsMacCursor cursorWithImageNamed:@"sizeNWSE"
170 hotSpot:NSMakePoint(12, 12)
173 case eCursor_col_resize:
174 return [nsMacCursor cursorWithImageNamed:@"colResize"
175 hotSpot:NSMakePoint(12, 12)
178 case eCursor_row_resize:
179 return [nsMacCursor cursorWithImageNamed:@"rowResize"
180 hotSpot:NSMakePoint(12, 12)
183 return [nsMacCursor cursorWithCursor:[NSCursor arrowCursor] type:aCursor];
186 NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
190 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
192 if ((self = [super init])) {
193 mCursors = [[NSMutableDictionary alloc] initWithCapacity:25];
197 NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
200 - (nsresult)setCursor:(enum nsCursor)aCursor {
201 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
203 nsCursor oldType = [mCurrentMacCursor type];
204 [self setMacCursor:[self getCursor:aCursor]];
206 // if a custom cursor was previously set, release sCursorImgContainer
207 if (oldType == sCustomCursor) {
208 NS_IF_RELEASE(sCursorImgContainer);
212 NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
215 - (nsresult)setMacCursor:(nsMacCursor*)aMacCursor {
216 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
218 // Some plugins mess with our cursors and set a cursor that even
219 // [NSCursor currentCursor] doesn't know about. In case that happens, just
221 [[NSCursor currentCursor] set];
223 nsCursor oldType = [mCurrentMacCursor type];
224 nsCursor newType = [aMacCursor type];
225 if (oldType != newType) {
226 if (newType == eCursor_none) {
228 } else if (oldType == eCursor_none) {
233 if (mCurrentMacCursor != aMacCursor || ![mCurrentMacCursor isSet]) {
235 [mCurrentMacCursor unset];
237 [mCurrentMacCursor release];
238 mCurrentMacCursor = aMacCursor;
243 NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
246 - (nsresult)setCursorWithImage:(imgIContainer*)aCursorImage
247 hotSpotX:(uint32_t)aHotspotX
248 hotSpotY:(uint32_t)aHotspotY
249 scaleFactor:(CGFloat)scaleFactor {
250 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
251 // As the user moves the mouse, this gets called repeatedly with the same aCursorImage
252 if (sCursorImgContainer == aCursorImage && sCursorScaleFactor == scaleFactor &&
254 [self setMacCursor:mCurrentMacCursor];
258 int32_t width = 0, height = 0;
259 aCursorImage->GetWidth(&width);
260 aCursorImage->GetHeight(&height);
261 // prevent DoS attacks
262 if (width > 128 || height > 128) {
266 NSImage* cursorImage;
267 nsresult rv = nsCocoaUtils::CreateNSImageFromImageContainer(
268 aCursorImage, imgIContainer::FRAME_FIRST, &cursorImage, scaleFactor);
269 if (NS_FAILED(rv) || !cursorImage) {
270 return NS_ERROR_FAILURE;
273 // if the hotspot is nonsensical, make it 0,0
274 aHotspotX = (aHotspotX > (uint32_t)width - 1) ? 0 : aHotspotX;
275 aHotspotY = (aHotspotY > (uint32_t)height - 1) ? 0 : aHotspotY;
277 NSPoint hotSpot = ::NSMakePoint(aHotspotX, aHotspotY);
278 [self setMacCursor:[nsMacCursor cursorWithCursor:[[NSCursor alloc] initWithImage:cursorImage
280 type:sCustomCursor]];
281 [cursorImage release];
283 NS_IF_RELEASE(sCursorImgContainer);
284 sCursorImgContainer = aCursorImage;
285 sCursorScaleFactor = scaleFactor;
286 NS_ADDREF(sCursorImgContainer);
290 NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
293 - (nsMacCursor*)getCursor:(enum nsCursor)aCursor {
294 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
296 nsMacCursor* result = [mCursors objectForKey:[NSNumber numberWithInt:aCursor]];
298 result = [nsCursorManager createCursor:aCursor];
299 [mCursors setObject:result forKey:[NSNumber numberWithInt:aCursor]];
303 NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
307 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
309 [mCurrentMacCursor unset];
310 [mCurrentMacCursor release];
312 NS_IF_RELEASE(sCursorImgContainer);
315 NS_OBJC_END_TRY_ABORT_BLOCK;