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 sCurrentCursorScaleFactor = 0.0f;
13 MOZ_RUNINIT static nsIWidget::Cursor sCurrentCursor;
14 static constexpr nsCursor kCustomCursor = 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
24 the cursor cache, 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
33 cursor to change. The argument is retained and the old cursor is
35 @param aMacCursor the cursor to set
38 - (nsresult)setMacCursor:(nsMacCursor*)aMacCursor;
40 /*! @method createCursor:
41 @abstract Create a Mac native representation of a cursor.
42 @discussion Creates a version of the Mac native representation of this
44 @param aCursor the cursor to create
45 @result the Mac native implementation of the cursor
47 + (nsMacCursor*)createCursor:(enum nsCursor)aCursor;
51 @implementation nsCursorManager
53 + (nsCursorManager*)sharedInstance {
54 NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
57 gInstance = [[nsCursorManager alloc] init];
61 NS_OBJC_END_TRY_BLOCK_RETURN(nil);
65 NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
70 NS_OBJC_END_TRY_IGNORE_BLOCK;
73 + (nsMacCursor*)createCursor:(enum nsCursor)aCursor {
74 NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
77 case eCursor_standard:
78 return [nsMacCursor cursorWithCursor:[NSCursor arrowCursor] type:aCursor];
80 case eCursor_spinning: {
81 return [nsMacCursor cursorWithCursor:[NSCursor busyButClickableCursor]
85 return [nsMacCursor cursorWithCursor:[NSCursor IBeamCursor] type:aCursor];
86 case eCursor_hyperlink:
87 return [nsMacCursor cursorWithCursor:[NSCursor pointingHandCursor]
89 case eCursor_crosshair:
90 return [nsMacCursor cursorWithCursor:[NSCursor crosshairCursor]
93 return [nsMacCursor cursorWithImageNamed:@"move"
94 hotSpot:NSMakePoint(12, 12)
97 return [nsMacCursor cursorWithImageNamed:@"help"
98 hotSpot:NSMakePoint(12, 12)
101 SEL cursorSelector = @selector(dragCopyCursor);
103 cursorWithCursor:[NSCursor respondsToSelector:cursorSelector]
104 ? [NSCursor performSelector:cursorSelector]
105 : [NSCursor arrowCursor]
108 case eCursor_alias: {
109 SEL cursorSelector = @selector(dragLinkCursor);
111 cursorWithCursor:[NSCursor respondsToSelector:cursorSelector]
112 ? [NSCursor performSelector:cursorSelector]
113 : [NSCursor arrowCursor]
116 case eCursor_context_menu: {
117 SEL cursorSelector = @selector(contextualMenuCursor);
119 cursorWithCursor:[NSCursor respondsToSelector:cursorSelector]
120 ? [NSCursor performSelector:cursorSelector]
121 : [NSCursor arrowCursor]
125 return [nsMacCursor cursorWithImageNamed:@"cell"
126 hotSpot:NSMakePoint(12, 12)
129 return [nsMacCursor cursorWithCursor:[NSCursor openHandCursor]
131 case eCursor_grabbing:
132 return [nsMacCursor cursorWithCursor:[NSCursor closedHandCursor]
134 case eCursor_zoom_in:
135 return [nsMacCursor cursorWithImageNamed:@"zoomIn"
136 hotSpot:NSMakePoint(10, 10)
138 case eCursor_zoom_out:
139 return [nsMacCursor cursorWithImageNamed:@"zoomOut"
140 hotSpot:NSMakePoint(10, 10)
142 case eCursor_vertical_text:
143 return [nsMacCursor cursorWithImageNamed:@"vtIBeam"
144 hotSpot:NSMakePoint(12, 11)
146 case eCursor_all_scroll:
147 return [nsMacCursor cursorWithCursor:[NSCursor openHandCursor]
149 case eCursor_not_allowed:
150 case eCursor_no_drop: {
151 SEL cursorSelector = @selector(operationNotAllowedCursor);
153 cursorWithCursor:[NSCursor respondsToSelector:cursorSelector]
154 ? [NSCursor performSelector:cursorSelector]
155 : [NSCursor arrowCursor]
160 case eCursor_n_resize:
161 return [nsMacCursor cursorWithCursor:[NSCursor resizeUpCursor]
164 case eCursor_ne_resize:
165 return [nsMacCursor cursorWithImageNamed:@"sizeNE"
166 hotSpot:NSMakePoint(12, 11)
169 case eCursor_e_resize:
170 return [nsMacCursor cursorWithCursor:[NSCursor resizeRightCursor]
173 case eCursor_se_resize:
174 return [nsMacCursor cursorWithImageNamed:@"sizeSE"
175 hotSpot:NSMakePoint(12, 12)
178 case eCursor_s_resize:
179 return [nsMacCursor cursorWithCursor:[NSCursor resizeDownCursor]
182 case eCursor_sw_resize:
183 return [nsMacCursor cursorWithImageNamed:@"sizeSW"
184 hotSpot:NSMakePoint(10, 12)
187 case eCursor_w_resize:
188 return [nsMacCursor cursorWithCursor:[NSCursor resizeLeftCursor]
191 case eCursor_nw_resize:
192 return [nsMacCursor cursorWithImageNamed:@"sizeNW"
193 hotSpot:NSMakePoint(11, 11)
196 case eCursor_ns_resize:
197 return [nsMacCursor cursorWithCursor:[NSCursor resizeUpDownCursor]
200 case eCursor_ew_resize:
201 return [nsMacCursor cursorWithCursor:[NSCursor resizeLeftRightCursor]
203 // North East & South West
204 case eCursor_nesw_resize:
205 return [nsMacCursor cursorWithImageNamed:@"sizeNESW"
206 hotSpot:NSMakePoint(12, 12)
208 // North West & South East
209 case eCursor_nwse_resize:
210 return [nsMacCursor cursorWithImageNamed:@"sizeNWSE"
211 hotSpot:NSMakePoint(12, 12)
214 case eCursor_col_resize:
215 return [nsMacCursor cursorWithImageNamed:@"colResize"
216 hotSpot:NSMakePoint(12, 12)
219 case eCursor_row_resize:
220 return [nsMacCursor cursorWithImageNamed:@"rowResize"
221 hotSpot:NSMakePoint(12, 12)
224 return [nsMacCursor cursorWithCursor:[NSCursor arrowCursor] type:aCursor];
227 NS_OBJC_END_TRY_BLOCK_RETURN(nil);
231 NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
233 if ((self = [super init])) {
234 mCursors = [[NSMutableDictionary alloc] initWithCapacity:25];
238 NS_OBJC_END_TRY_BLOCK_RETURN(nil);
241 - (nsresult)setNonCustomCursor:(const nsIWidget::Cursor&)aCursor {
242 NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
244 [self setMacCursor:[self getCursor:aCursor.mDefaultCursor]];
246 sCurrentCursor = aCursor;
249 NS_OBJC_END_TRY_BLOCK_RETURN(NS_ERROR_FAILURE);
252 - (nsresult)setMacCursor:(nsMacCursor*)aMacCursor {
253 NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
255 nsCursor oldType = [mCurrentMacCursor type];
256 nsCursor newType = [aMacCursor type];
257 if (oldType != newType) {
258 if (newType == eCursor_none) {
260 } else if (oldType == eCursor_none) {
265 if (mCurrentMacCursor != aMacCursor || ![mCurrentMacCursor isSet]) {
267 [mCurrentMacCursor unset];
269 [mCurrentMacCursor release];
270 mCurrentMacCursor = aMacCursor;
275 NS_OBJC_END_TRY_BLOCK_RETURN(NS_ERROR_FAILURE);
278 - (nsresult)setCustomCursor:(const nsIWidget::Cursor&)aCursor
279 widgetScaleFactor:(CGFloat)scaleFactor
280 forceUpdate:(bool)aForceUpdate {
281 NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
283 // As the user moves the mouse, this gets called repeatedly with the same
285 if (!aForceUpdate && sCurrentCursor == aCursor &&
286 sCurrentCursorScaleFactor == scaleFactor && mCurrentMacCursor) {
287 // Native dragging can unset our cursor apparently (see bug 1739352).
288 if (MOZ_UNLIKELY(![mCurrentMacCursor isSet])) {
289 [mCurrentMacCursor set];
294 sCurrentCursor = aCursor;
295 sCurrentCursorScaleFactor = scaleFactor;
297 if (!aCursor.IsCustom()) {
298 return NS_ERROR_FAILURE;
301 nsIntSize size = nsIWidget::CustomCursorSize(aCursor);
302 // prevent DoS attacks
303 if (size.width > 128 || size.height > 128) {
304 return NS_ERROR_FAILURE;
307 const NSSize cocoaSize = NSMakeSize(size.width, size.height);
308 NSImage* cursorImage;
309 nsresult rv = nsCocoaUtils::CreateNSImageFromImageContainer(
310 aCursor.mContainer, imgIContainer::FRAME_FIRST, nullptr, cocoaSize,
311 &cursorImage, scaleFactor);
312 if (NS_FAILED(rv) || !cursorImage) {
313 return NS_ERROR_FAILURE;
316 [cursorImage setSize:cocoaSize];
317 [[[cursorImage representations] objectAtIndex:0] setSize:cocoaSize];
319 // if the hotspot is nonsensical, make it 0,0
321 aCursor.mHotspotX > (uint32_t(size.width) - 1) ? 0 : aCursor.mHotspotX;
323 aCursor.mHotspotY > (uint32_t(size.height) - 1) ? 0 : aCursor.mHotspotY;
324 NSPoint hotSpot = ::NSMakePoint(hotspotX, hotspotY);
325 [self setMacCursor:[nsMacCursor cursorWithCursor:[[NSCursor alloc]
326 initWithImage:cursorImage
328 type:kCustomCursor]];
329 [cursorImage release];
332 NS_OBJC_END_TRY_BLOCK_RETURN(NS_ERROR_FAILURE);
335 - (nsMacCursor*)getCursor:(enum nsCursor)aCursor {
336 NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
338 nsMacCursor* result =
339 [mCursors objectForKey:[NSNumber numberWithInt:aCursor]];
341 result = [nsCursorManager createCursor:aCursor];
342 [mCursors setObject:result forKey:[NSNumber numberWithInt:aCursor]];
346 NS_OBJC_END_TRY_BLOCK_RETURN(nil);
350 NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
352 [mCurrentMacCursor unset];
353 [mCurrentMacCursor release];
358 NS_OBJC_END_TRY_IGNORE_BLOCK;