Bug 1673311 [wpt PR 26282] - Fieldset NG: Percentage heights for content elements...
[gecko.git] / widget / cocoa / nsCursorManager.mm
blobe27641160dd62940b1be7c4abd9360e1d60893ae
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 {
52   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
54   if (!gInstance) {
55     gInstance = [[nsCursorManager alloc] init];
56   }
57   return gInstance;
59   NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
62 + (void)dispose {
63   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
65   [gInstance release];
66   gInstance = nil;
68   NS_OBJC_END_TRY_ABORT_BLOCK;
71 + (nsMacCursor*)createCursor:(enum nsCursor)aCursor {
72   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
74   switch (aCursor) {
75     SEL cursorSelector;
76     case eCursor_standard:
77       return [nsMacCursor cursorWithCursor:[NSCursor arrowCursor] type:aCursor];
78     case eCursor_wait:
79     case eCursor_spinning: {
80       return [nsMacCursor cursorWithCursor:[NSCursor busyButClickableCursor] type:aCursor];
81     }
82     case eCursor_select:
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];
88     case eCursor_move:
89       return [nsMacCursor cursorWithImageNamed:@"move" hotSpot:NSMakePoint(12, 12) type:aCursor];
90     case eCursor_help:
91       return [nsMacCursor cursorWithImageNamed:@"help" hotSpot:NSMakePoint(12, 12) type:aCursor];
92     case eCursor_copy:
93       cursorSelector = @selector(dragCopyCursor);
94       return [nsMacCursor cursorWithCursor:[NSCursor respondsToSelector:cursorSelector]
95                                                ? [NSCursor performSelector:cursorSelector]
96                                                : [NSCursor arrowCursor]
97                                       type:aCursor];
98     case eCursor_alias:
99       cursorSelector = @selector(dragLinkCursor);
100       return [nsMacCursor cursorWithCursor:[NSCursor respondsToSelector:cursorSelector]
101                                                ? [NSCursor performSelector:cursorSelector]
102                                                : [NSCursor arrowCursor]
103                                       type:aCursor];
104     case eCursor_context_menu:
105       cursorSelector = @selector(contextualMenuCursor);
106       return [nsMacCursor cursorWithCursor:[NSCursor respondsToSelector:cursorSelector]
107                                                ? [NSCursor performSelector:cursorSelector]
108                                                : [NSCursor arrowCursor]
109                                       type:aCursor];
110     case eCursor_cell:
111       return [nsMacCursor cursorWithImageNamed:@"cell" hotSpot:NSMakePoint(12, 12) type:aCursor];
112     case eCursor_grab:
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]
130                                       type:aCursor];
131     // Resize Cursors:
132     // North
133     case eCursor_n_resize:
134       return [nsMacCursor cursorWithCursor:[NSCursor resizeUpCursor] type:aCursor];
135     // North East
136     case eCursor_ne_resize:
137       return [nsMacCursor cursorWithImageNamed:@"sizeNE" hotSpot:NSMakePoint(12, 11) type:aCursor];
138     // East
139     case eCursor_e_resize:
140       return [nsMacCursor cursorWithCursor:[NSCursor resizeRightCursor] type:aCursor];
141     // South East
142     case eCursor_se_resize:
143       return [nsMacCursor cursorWithImageNamed:@"sizeSE" hotSpot:NSMakePoint(12, 12) type:aCursor];
144     // South
145     case eCursor_s_resize:
146       return [nsMacCursor cursorWithCursor:[NSCursor resizeDownCursor] type:aCursor];
147     // South West
148     case eCursor_sw_resize:
149       return [nsMacCursor cursorWithImageNamed:@"sizeSW" hotSpot:NSMakePoint(10, 12) type:aCursor];
150     // West
151     case eCursor_w_resize:
152       return [nsMacCursor cursorWithCursor:[NSCursor resizeLeftCursor] type:aCursor];
153     // North West
154     case eCursor_nw_resize:
155       return [nsMacCursor cursorWithImageNamed:@"sizeNW" hotSpot:NSMakePoint(11, 11) type:aCursor];
156     // North & South
157     case eCursor_ns_resize:
158       return [nsMacCursor cursorWithCursor:[NSCursor resizeUpDownCursor] type:aCursor];
159     // East & West
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)
166                                           type:aCursor];
167     // North West & South East
168     case eCursor_nwse_resize:
169       return [nsMacCursor cursorWithImageNamed:@"sizeNWSE"
170                                        hotSpot:NSMakePoint(12, 12)
171                                           type:aCursor];
172     // Column Resize
173     case eCursor_col_resize:
174       return [nsMacCursor cursorWithImageNamed:@"colResize"
175                                        hotSpot:NSMakePoint(12, 12)
176                                           type:aCursor];
177     // Row Resize
178     case eCursor_row_resize:
179       return [nsMacCursor cursorWithImageNamed:@"rowResize"
180                                        hotSpot:NSMakePoint(12, 12)
181                                           type:aCursor];
182     default:
183       return [nsMacCursor cursorWithCursor:[NSCursor arrowCursor] type:aCursor];
184   }
186   NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
189 - (id)init {
190   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
192   if ((self = [super init])) {
193     mCursors = [[NSMutableDictionary alloc] initWithCapacity:25];
194   }
195   return self;
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);
209   }
210   return NS_OK;
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
220   // reset the state.
221   [[NSCursor currentCursor] set];
223   nsCursor oldType = [mCurrentMacCursor type];
224   nsCursor newType = [aMacCursor type];
225   if (oldType != newType) {
226     if (newType == eCursor_none) {
227       [NSCursor hide];
228     } else if (oldType == eCursor_none) {
229       [NSCursor unhide];
230     }
231   }
233   if (mCurrentMacCursor != aMacCursor || ![mCurrentMacCursor isSet]) {
234     [aMacCursor retain];
235     [mCurrentMacCursor unset];
236     [aMacCursor set];
237     [mCurrentMacCursor release];
238     mCurrentMacCursor = aMacCursor;
239   }
241   return NS_OK;
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 &&
253       mCurrentMacCursor) {
254     [self setMacCursor:mCurrentMacCursor];
255     return NS_OK;
256   }
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) {
263     return NS_OK;
264   }
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;
271   }
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
279                                                                            hotSpot:hotSpot]
280                                               type:sCustomCursor]];
281   [cursorImage release];
283   NS_IF_RELEASE(sCursorImgContainer);
284   sCursorImgContainer = aCursorImage;
285   sCursorScaleFactor = scaleFactor;
286   NS_ADDREF(sCursorImgContainer);
288   return NS_OK;
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]];
297   if (!result) {
298     result = [nsCursorManager createCursor:aCursor];
299     [mCursors setObject:result forKey:[NSNumber numberWithInt:aCursor]];
300   }
301   return result;
303   NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
306 - (void)dealloc {
307   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
309   [mCurrentMacCursor unset];
310   [mCurrentMacCursor release];
311   [mCursors release];
312   NS_IF_RELEASE(sCursorImgContainer);
313   [super dealloc];
315   NS_OBJC_END_TRY_ABORT_BLOCK;
318 @end