Remove WebCoreSystemInterface
[WebKit-https.git] / Source / WebCore / platform / mac / CursorMac.mm
1 /*
2  * Copyright (C) 2004-2017 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
24  */
25
26 #import "config.h"
27 #import "Cursor.h"
28
29 #import <objc/runtime.h>
30 #import <pal/spi/mac/HIServicesSPI.h>
31 #import <wtf/BlockObjCExceptions.h>
32 #import <wtf/StdLibExtras.h>
33
34 @interface WebCoreCursorBundle : NSObject { }
35 @end
36
37 @implementation WebCoreCursorBundle
38 @end
39
40 namespace WebCore {
41     
42 static NSCursor *busyButClickableNSCursor;
43 static NSCursor *makeAliasNSCursor;
44 static NSCursor *moveNSCursor;
45 static NSCursor *resizeEastNSCursor;
46 static NSCursor *resizeEastWestNSCursor;
47 static NSCursor *resizeNorthNSCursor;
48 static NSCursor *resizeNorthSouthNSCursor;
49 static NSCursor *resizeNortheastNSCursor;
50 static NSCursor *resizeNortheastSouthwestNSCursor;
51 static NSCursor *resizeNorthwestNSCursor;
52 static NSCursor *resizeNorthwestSoutheastNSCursor;
53 static NSCursor *resizeSouthNSCursor;
54 static NSCursor *resizeSoutheastNSCursor;
55 static NSCursor *resizeSouthwestNSCursor;
56 static NSCursor *resizeWestNSCursor;
57
58 static NSCursor *cellNSCursor;
59 static NSCursor *helpNSCursor;
60 static NSCursor *zoomInNSCursor;
61 static NSCursor *zoomOutNSCursor;
62
63 static NSInteger WKCoreCursor_coreCursorType(id self, SEL)
64 {
65     if (self == busyButClickableNSCursor)
66         return kCoreCursorBusyButClickable;
67     if (self == makeAliasNSCursor)
68         return kCoreCursorMakeAlias;
69     if (self == moveNSCursor)
70         return kCoreCursorWindowMove;
71     if (self == resizeEastNSCursor)
72         return kCoreCursorWindowResizeEast;
73     if (self == resizeEastWestNSCursor)
74         return kCoreCursorWindowResizeEastWest;
75     if (self == resizeNorthNSCursor)
76         return kCoreCursorWindowResizeNorth;
77     if (self == resizeNorthSouthNSCursor)
78         return kCoreCursorWindowResizeNorthSouth;
79     if (self == resizeNortheastNSCursor)
80         return kCoreCursorWindowResizeNorthEast;
81     if (self == resizeNortheastSouthwestNSCursor)
82         return kCoreCursorWindowResizeNorthEastSouthWest;
83     if (self == resizeNorthwestNSCursor)
84         return kCoreCursorWindowResizeNorthWest;
85     if (self == resizeNorthwestSoutheastNSCursor)
86         return kCoreCursorWindowResizeNorthWestSouthEast;
87     if (self == resizeSouthNSCursor)
88         return kCoreCursorWindowResizeSouth;
89     if (self == resizeSoutheastNSCursor)
90         return kCoreCursorWindowResizeSouthEast;
91     if (self == resizeSouthwestNSCursor)
92         return kCoreCursorWindowResizeSouthWest;
93     if (self == resizeWestNSCursor)
94         return kCoreCursorWindowResizeWest;
95     if (self == cellNSCursor)
96         return kCoreCursorCell;
97     if (self == helpNSCursor)
98         return kCoreCursorHelp;
99     if (self == zoomInNSCursor)
100         return kCoreCursorZoomIn;
101     if (self == zoomOutNSCursor)
102         return kCoreCursorZoomOut;
103     
104     return NSNotFound;
105 }
106
107 static Class createCoreCursorClass()
108 {
109     Class coreCursorClass = objc_allocateClassPair([NSCursor class], "WKCoreCursor", 0);
110     SEL coreCursorType = NSSelectorFromString(@"_coreCursorType");
111     class_addMethod(coreCursorClass, coreCursorType, (IMP)WKCoreCursor_coreCursorType, method_getTypeEncoding(class_getInstanceMethod([NSCursor class], coreCursorType)));
112     objc_registerClassPair(coreCursorClass);
113     return coreCursorClass;
114 }
115
116 static Class coreCursorClass()
117 {
118     Class coreCursorClass = objc_lookUpClass("WKCoreCursor");
119     if (!coreCursorClass)
120         coreCursorClass = createCoreCursorClass();
121     return coreCursorClass;
122 }
123
124 static NSCursor *cursor(const char *name)
125 {
126     NSCursor **slot = 0;
127     
128     if (!strcmp(name, "BusyButClickable"))
129         slot = &busyButClickableNSCursor;
130     else if (!strcmp(name, "MakeAlias"))
131         slot = &makeAliasNSCursor;
132     else if (!strcmp(name, "Move"))
133         slot = &moveNSCursor;
134     else if (!strcmp(name, "ResizeEast"))
135         slot = &resizeEastNSCursor;
136     else if (!strcmp(name, "ResizeEastWest"))
137         slot = &resizeEastWestNSCursor;
138     else if (!strcmp(name, "ResizeNorth"))
139         slot = &resizeNorthNSCursor;
140     else if (!strcmp(name, "ResizeNorthSouth"))
141         slot = &resizeNorthSouthNSCursor;
142     else if (!strcmp(name, "ResizeNortheast"))
143         slot = &resizeNortheastNSCursor;
144     else if (!strcmp(name, "ResizeNortheastSouthwest"))
145         slot = &resizeNortheastSouthwestNSCursor;
146     else if (!strcmp(name, "ResizeNorthwest"))
147         slot = &resizeNorthwestNSCursor;
148     else if (!strcmp(name, "ResizeNorthwestSoutheast"))
149         slot = &resizeNorthwestSoutheastNSCursor;
150     else if (!strcmp(name, "ResizeSouth"))
151         slot = &resizeSouthNSCursor;
152     else if (!strcmp(name, "ResizeSoutheast"))
153         slot = &resizeSoutheastNSCursor;
154     else if (!strcmp(name, "ResizeSouthwest"))
155         slot = &resizeSouthwestNSCursor;
156     else if (!strcmp(name, "ResizeWest"))
157         slot = &resizeWestNSCursor;
158     else if (!strcmp(name, "Cell"))
159         slot = &cellNSCursor;
160     else if (!strcmp(name, "Help"))
161         slot = &helpNSCursor;
162     else if (!strcmp(name, "ZoomIn"))
163         slot = &zoomInNSCursor;
164     else if (!strcmp(name, "ZoomOut"))
165         slot = &zoomOutNSCursor;
166     
167     if (!slot)
168         return nil;
169     
170     if (!*slot)
171         *slot = [[coreCursorClass() alloc] init];
172     return *slot;
173 }
174
175 // Simple NSCursor calls shouldn't need protection,
176 // but creating a cursor with a bad image might throw.
177
178 #if ENABLE(MOUSE_CURSOR_SCALE)
179 static RetainPtr<NSCursor> createCustomCursor(Image* image, const IntPoint& hotSpot, float scale)
180 #else
181 static RetainPtr<NSCursor> createCustomCursor(Image* image, const IntPoint& hotSpot)
182 #endif
183 {
184     // FIXME: The cursor won't animate.  Not sure if that's a big deal.
185     auto nsImage = image->snapshotNSImage();
186     if (!nsImage)
187         return nullptr;
188     BEGIN_BLOCK_OBJC_EXCEPTIONS;
189
190 #if ENABLE(MOUSE_CURSOR_SCALE)
191     NSSize size = NSMakeSize(image->width() / scale, image->height() / scale);
192     NSSize expandedSize = NSMakeSize(ceil(size.width), ceil(size.height));
193
194     // Pad the image with transparent pixels so it has an integer boundary.
195     if (size.width != expandedSize.width || size.height != expandedSize.height) {
196         RetainPtr<NSImage> expandedImage = adoptNS([[NSImage alloc] initWithSize:expandedSize]);
197         NSRect toRect = NSMakeRect(0, expandedSize.height - size.height, size.width, size.height);
198         NSRect fromRect = NSMakeRect(0, 0, image->width(), image->height());
199
200         [expandedImage lockFocus];
201         [nsImage drawInRect:toRect fromRect:fromRect operation:NSCompositingOperationSourceOver fraction:1];
202         [expandedImage unlockFocus];
203
204         return adoptNS([[NSCursor alloc] initWithImage:expandedImage.get() hotSpot:hotSpot]);
205     }
206
207     // Scale the image and its representation to match retina resolution.
208     [nsImage setSize:expandedSize];
209     [[[nsImage representations] objectAtIndex:0] setSize:expandedSize];
210 #endif
211
212     return adoptNS([[NSCursor alloc] initWithImage:nsImage.get() hotSpot:hotSpot]);
213     END_BLOCK_OBJC_EXCEPTIONS;
214     return nullptr;
215 }
216
217 void Cursor::ensurePlatformCursor() const
218 {
219     if (m_platformCursor)
220         return;
221
222     switch (m_type) {
223     case Cursor::Pointer:
224         m_platformCursor = [NSCursor arrowCursor];
225         break;
226
227     case Cursor::Cross:
228         m_platformCursor = [NSCursor crosshairCursor];
229         break;
230
231     case Cursor::Hand:
232         m_platformCursor = [NSCursor pointingHandCursor];
233         break;
234
235     case Cursor::IBeam:
236         m_platformCursor = [NSCursor IBeamCursor];
237         break;
238
239     case Cursor::Wait:
240         m_platformCursor = cursor("BusyButClickable");
241         break;
242
243     case Cursor::Help:
244         m_platformCursor = cursor("Help");
245         break;
246
247     case Cursor::Move:
248     case Cursor::MiddlePanning:
249         m_platformCursor = cursor("Move");
250         break;
251
252     case Cursor::EastResize:
253     case Cursor::EastPanning:
254         m_platformCursor = cursor("ResizeEast");
255         break;
256
257     case Cursor::NorthResize:
258     case Cursor::NorthPanning:
259         m_platformCursor = cursor("ResizeNorth");
260         break;
261
262     case Cursor::NorthEastResize:
263     case Cursor::NorthEastPanning:
264         m_platformCursor = cursor("ResizeNortheast");
265         break;
266
267     case Cursor::NorthWestResize:
268     case Cursor::NorthWestPanning:
269         m_platformCursor = cursor("ResizeNorthwest");
270         break;
271
272     case Cursor::SouthResize:
273     case Cursor::SouthPanning:
274         m_platformCursor = cursor("ResizeSouth");
275         break;
276
277     case Cursor::SouthEastResize:
278     case Cursor::SouthEastPanning:
279         m_platformCursor = cursor("ResizeSoutheast");
280         break;
281
282     case Cursor::SouthWestResize:
283     case Cursor::SouthWestPanning:
284         m_platformCursor = cursor("ResizeSouthwest");
285         break;
286
287     case Cursor::WestResize:
288     case Cursor::WestPanning:
289         m_platformCursor = cursor("ResizeWest");
290         break;
291
292     case Cursor::NorthSouthResize:
293         m_platformCursor = cursor("ResizeNorthSouth");
294         break;
295
296     case Cursor::EastWestResize:
297         m_platformCursor = cursor("ResizeEastWest");
298         break;
299
300     case Cursor::NorthEastSouthWestResize:
301         m_platformCursor = cursor("ResizeNortheastSouthwest");
302         break;
303
304     case Cursor::NorthWestSouthEastResize:
305         m_platformCursor = cursor("ResizeNorthwestSoutheast");
306         break;
307
308     case Cursor::ColumnResize:
309         m_platformCursor = [NSCursor resizeLeftRightCursor];
310         break;
311
312     case Cursor::RowResize:
313         m_platformCursor = [NSCursor resizeUpDownCursor];
314         break;
315
316     case Cursor::VerticalText:
317         m_platformCursor = [NSCursor IBeamCursorForVerticalLayout];
318         break;
319
320     case Cursor::Cell:
321         m_platformCursor = cursor("Cell");
322         break;
323
324     case Cursor::ContextMenu:
325         m_platformCursor = [NSCursor contextualMenuCursor];
326         break;
327
328     case Cursor::Alias:
329         m_platformCursor = cursor("MakeAlias");
330         break;
331
332     case Cursor::Progress:
333         m_platformCursor = cursor("BusyButClickable");
334         break;
335
336     case Cursor::NoDrop:
337         m_platformCursor = [NSCursor operationNotAllowedCursor];
338         break;
339
340     case Cursor::Copy:
341         m_platformCursor = [NSCursor dragCopyCursor];
342         break;
343
344     case Cursor::None:
345         m_platformCursor = adoptNS([[NSCursor alloc] initWithImage:adoptNS([[NSImage alloc] initWithSize:NSMakeSize(1, 1)]).get() hotSpot:NSZeroPoint]);
346         break;
347
348     case Cursor::NotAllowed:
349         m_platformCursor = [NSCursor operationNotAllowedCursor];
350         break;
351
352     case Cursor::ZoomIn:
353         m_platformCursor = cursor("ZoomIn");
354         break;
355
356     case Cursor::ZoomOut:
357         m_platformCursor = cursor("ZoomOut");
358         break;
359
360     case Cursor::Grab:
361         m_platformCursor = [NSCursor openHandCursor];
362         break;
363
364     case Cursor::Grabbing:
365         m_platformCursor = [NSCursor closedHandCursor];
366         break;
367
368     case Cursor::Custom:
369 #if ENABLE(MOUSE_CURSOR_SCALE)
370         m_platformCursor = createCustomCursor(m_image.get(), m_hotSpot, m_imageScaleFactor);
371 #else
372         m_platformCursor = createCustomCursor(m_image.get(), m_hotSpot);
373 #endif
374         break;
375     }
376 }
377
378 NSCursor *Cursor::platformCursor() const
379 {
380     ensurePlatformCursor();
381     return m_platformCursor.get();
382 }
383
384 } // namespace WebCore