Updating trunk VERSION from 2139.0 to 2140.0
[chromium-blink-merge.git] / content / common / cursors / webcursor_mac.mm
blobc0a7885c6ef30e4199695a4edf97653060b3898f
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "content/common/cursors/webcursor.h"
7 #import <AppKit/AppKit.h>
9 #include "base/logging.h"
10 #include "base/mac/mac_util.h"
11 #include "base/mac/scoped_cftyperef.h"
12 #include "base/mac/sdk_forward_declarations.h"
13 #include "content/app/resources/grit/content_resources.h"
14 #include "content/public/common/content_client.h"
15 #include "skia/ext/skia_utils_mac.h"
16 #include "third_party/WebKit/public/platform/WebCursorInfo.h"
17 #include "third_party/WebKit/public/platform/WebSize.h"
18 #include "ui/gfx/image/image.h"
19 #include "ui/gfx/point_conversions.h"
20 #include "ui/gfx/size_conversions.h"
22 using blink::WebCursorInfo;
23 using blink::WebSize;
25 // Private interface to CoreCursor, as of Mac OS X 10.7. This is essentially the
26 // implementation of WKCursor in WebKitSystemInterface.
28 enum {
29   kArrowCursor = 0,
30   kIBeamCursor = 1,
31   kMakeAliasCursor = 2,
32   kOperationNotAllowedCursor = 3,
33   kBusyButClickableCursor = 4,
34   kCopyCursor = 5,
35   kClosedHandCursor = 11,
36   kOpenHandCursor = 12,
37   kPointingHandCursor = 13,
38   kCountingUpHandCursor = 14,
39   kCountingDownHandCursor = 15,
40   kCountingUpAndDownHandCursor = 16,
41   kResizeLeftCursor = 17,
42   kResizeRightCursor = 18,
43   kResizeLeftRightCursor = 19,
44   kCrosshairCursor = 20,
45   kResizeUpCursor = 21,
46   kResizeDownCursor = 22,
47   kResizeUpDownCursor = 23,
48   kContextualMenuCursor = 24,
49   kDisappearingItemCursor = 25,
50   kVerticalIBeamCursor = 26,
51   kResizeEastCursor = 27,
52   kResizeEastWestCursor = 28,
53   kResizeNortheastCursor = 29,
54   kResizeNortheastSouthwestCursor = 30,
55   kResizeNorthCursor = 31,
56   kResizeNorthSouthCursor = 32,
57   kResizeNorthwestCursor = 33,
58   kResizeNorthwestSoutheastCursor = 34,
59   kResizeSoutheastCursor = 35,
60   kResizeSouthCursor = 36,
61   kResizeSouthwestCursor = 37,
62   kResizeWestCursor = 38,
63   kMoveCursor = 39,
64   kHelpCursor = 40,  // Present on >= 10.7.3.
65   kCellCursor = 41,  // Present on >= 10.7.3.
66   kZoomInCursor = 42,  // Present on >= 10.7.3.
67   kZoomOutCursor = 43  // Present on >= 10.7.3.
69 typedef long long CrCoreCursorType;
71 @interface CrCoreCursor : NSCursor {
72  @private
73   CrCoreCursorType type_;
76 + (id)cursorWithType:(CrCoreCursorType)type;
77 - (id)initWithType:(CrCoreCursorType)type;
78 - (CrCoreCursorType)_coreCursorType;
80 @end
82 @implementation CrCoreCursor
84 + (id)cursorWithType:(CrCoreCursorType)type {
85   NSCursor* cursor = [[CrCoreCursor alloc] initWithType:type];
86   if ([cursor image])
87     return [cursor autorelease];
89   [cursor release];
90   return nil;
93 - (id)initWithType:(CrCoreCursorType)type {
94   if ((self = [super init])) {
95     type_ = type;
96   }
97   return self;
100 - (CrCoreCursorType)_coreCursorType {
101   return type_;
104 @end
106 namespace {
108 NSCursor* LoadCursor(int resource_id, int hotspot_x, int hotspot_y) {
109   const gfx::Image& cursor_image =
110       content::GetContentClient()->GetNativeImageNamed(resource_id);
111   DCHECK(!cursor_image.IsEmpty());
112   return [[[NSCursor alloc] initWithImage:cursor_image.ToNSImage()
113                                   hotSpot:NSMakePoint(hotspot_x,
114                                                       hotspot_y)] autorelease];
117 // Gets a specified cursor from CoreCursor, falling back to loading it from the
118 // image cache if CoreCursor cannot provide it.
119 NSCursor* GetCoreCursorWithFallback(CrCoreCursorType type,
120                                     int resource_id,
121                                     int hotspot_x,
122                                     int hotspot_y) {
123   if (base::mac::IsOSLionOrLater()) {
124     NSCursor* cursor = [CrCoreCursor cursorWithType:type];
125     if (cursor)
126       return cursor;
127   }
129   return LoadCursor(resource_id, hotspot_x, hotspot_y);
132 NSCursor* CreateCustomCursor(const std::vector<char>& custom_data,
133                              const gfx::Size& custom_size,
134                              float custom_scale,
135                              const gfx::Point& hotspot) {
136   // If the data is missing, leave the backing transparent.
137   void* data = NULL;
138   size_t data_size = 0;
139   if (!custom_data.empty()) {
140     // This is safe since we're not going to draw into the context we're
141     // creating.
142     data = const_cast<char*>(&custom_data[0]);
143     data_size = custom_data.size();
144   }
146   // If the size is empty, use a 1x1 transparent image.
147   gfx::Size size = custom_size;
148   if (size.IsEmpty()) {
149     size.SetSize(1, 1);
150     data = NULL;
151   }
153   SkBitmap bitmap;
154   if (bitmap.allocN32Pixels(size.width(), size.height()) && data)
155     memcpy(bitmap.getAddr32(0, 0), data, data_size);
156   else
157     bitmap.eraseARGB(0, 0, 0, 0);
159   // Convert from pixels to view units.
160   if (custom_scale == 0)
161     custom_scale = 1;
162   NSSize dip_size = NSSizeFromCGSize(gfx::ToFlooredSize(
163       gfx::ScaleSize(custom_size, 1 / custom_scale)).ToCGSize());
164   NSPoint dip_hotspot = NSPointFromCGPoint(gfx::ToFlooredPoint(
165       gfx::ScalePoint(hotspot, 1 / custom_scale)).ToCGPoint());
167   // Both the image and its representation need to have the same size for
168   // cursors to appear in high resolution on retina displays. Note that the
169   // size of a representation is not the same as pixelsWide or pixelsHigh.
170   NSImage* cursor_image = gfx::SkBitmapToNSImage(bitmap);
171   [cursor_image setSize:dip_size];
172   [[[cursor_image representations] objectAtIndex:0] setSize:dip_size];
174   NSCursor* cursor = [[NSCursor alloc] initWithImage:cursor_image
175                                              hotSpot:dip_hotspot];
177   return [cursor autorelease];
180 }  // namespace
182 namespace content {
184 // Match Safari's cursor choices; see platform/mac/CursorMac.mm .
185 gfx::NativeCursor WebCursor::GetNativeCursor() {
186   switch (type_) {
187     case WebCursorInfo::TypePointer:
188       return [NSCursor arrowCursor];
189     case WebCursorInfo::TypeCross:
190       return [NSCursor crosshairCursor];
191     case WebCursorInfo::TypeHand:
192       // If >= 10.7, the pointingHandCursor has a shadow so use it. Otherwise
193       // use the custom one.
194       if (base::mac::IsOSLionOrLater())
195         return [NSCursor pointingHandCursor];
196       else
197         return LoadCursor(IDR_LINK_CURSOR, 6, 1);
198     case WebCursorInfo::TypeIBeam:
199       return [NSCursor IBeamCursor];
200     case WebCursorInfo::TypeWait:
201       return GetCoreCursorWithFallback(kBusyButClickableCursor,
202                                        IDR_WAIT_CURSOR, 7, 7);
203     case WebCursorInfo::TypeHelp:
204       return GetCoreCursorWithFallback(kHelpCursor,
205                                        IDR_HELP_CURSOR, 8, 8);
206     case WebCursorInfo::TypeEastResize:
207     case WebCursorInfo::TypeEastPanning:
208       return GetCoreCursorWithFallback(kResizeEastCursor,
209                                        IDR_EAST_RESIZE_CURSOR, 14, 7);
210     case WebCursorInfo::TypeNorthResize:
211     case WebCursorInfo::TypeNorthPanning:
212       return GetCoreCursorWithFallback(kResizeNorthCursor,
213                                        IDR_NORTH_RESIZE_CURSOR, 7, 1);
214     case WebCursorInfo::TypeNorthEastResize:
215     case WebCursorInfo::TypeNorthEastPanning:
216       return GetCoreCursorWithFallback(kResizeNortheastCursor,
217                                        IDR_NORTHEAST_RESIZE_CURSOR, 14, 1);
218     case WebCursorInfo::TypeNorthWestResize:
219     case WebCursorInfo::TypeNorthWestPanning:
220       return GetCoreCursorWithFallback(kResizeNorthwestCursor,
221                                        IDR_NORTHWEST_RESIZE_CURSOR, 0, 0);
222     case WebCursorInfo::TypeSouthResize:
223     case WebCursorInfo::TypeSouthPanning:
224       return GetCoreCursorWithFallback(kResizeSouthCursor,
225                                        IDR_SOUTH_RESIZE_CURSOR, 7, 14);
226     case WebCursorInfo::TypeSouthEastResize:
227     case WebCursorInfo::TypeSouthEastPanning:
228       return GetCoreCursorWithFallback(kResizeSoutheastCursor,
229                                        IDR_SOUTHEAST_RESIZE_CURSOR, 14, 14);
230     case WebCursorInfo::TypeSouthWestResize:
231     case WebCursorInfo::TypeSouthWestPanning:
232       return GetCoreCursorWithFallback(kResizeSouthwestCursor,
233                                        IDR_SOUTHWEST_RESIZE_CURSOR, 1, 14);
234     case WebCursorInfo::TypeWestResize:
235     case WebCursorInfo::TypeWestPanning:
236       return GetCoreCursorWithFallback(kResizeWestCursor,
237                                        IDR_WEST_RESIZE_CURSOR, 1, 7);
238     case WebCursorInfo::TypeNorthSouthResize:
239       return GetCoreCursorWithFallback(kResizeNorthSouthCursor,
240                                        IDR_NORTHSOUTH_RESIZE_CURSOR, 7, 7);
241     case WebCursorInfo::TypeEastWestResize:
242       return GetCoreCursorWithFallback(kResizeEastWestCursor,
243                                        IDR_EASTWEST_RESIZE_CURSOR, 7, 7);
244     case WebCursorInfo::TypeNorthEastSouthWestResize:
245       return GetCoreCursorWithFallback(kResizeNortheastSouthwestCursor,
246                                        IDR_NORTHEASTSOUTHWEST_RESIZE_CURSOR,
247                                        7, 7);
248     case WebCursorInfo::TypeNorthWestSouthEastResize:
249       return GetCoreCursorWithFallback(kResizeNorthwestSoutheastCursor,
250                                        IDR_NORTHWESTSOUTHEAST_RESIZE_CURSOR,
251                                        7, 7);
252     case WebCursorInfo::TypeColumnResize:
253       return [NSCursor resizeLeftRightCursor];
254     case WebCursorInfo::TypeRowResize:
255       return [NSCursor resizeUpDownCursor];
256     case WebCursorInfo::TypeMiddlePanning:
257     case WebCursorInfo::TypeMove:
258       return GetCoreCursorWithFallback(kMoveCursor,
259                                        IDR_MOVE_CURSOR, 7, 7);
260     case WebCursorInfo::TypeVerticalText:
261       // IBeamCursorForVerticalLayout is >= 10.7.
262       if ([NSCursor respondsToSelector:@selector(IBeamCursorForVerticalLayout)])
263         return [NSCursor IBeamCursorForVerticalLayout];
264       else
265         return LoadCursor(IDR_VERTICALTEXT_CURSOR, 7, 7);
266     case WebCursorInfo::TypeCell:
267       return GetCoreCursorWithFallback(kCellCursor,
268                                        IDR_CELL_CURSOR, 7, 7);
269     case WebCursorInfo::TypeContextMenu:
270       return [NSCursor contextualMenuCursor];
271     case WebCursorInfo::TypeAlias:
272       return GetCoreCursorWithFallback(kMakeAliasCursor,
273                                        IDR_ALIAS_CURSOR, 11, 3);
274     case WebCursorInfo::TypeProgress:
275       return GetCoreCursorWithFallback(kBusyButClickableCursor,
276                                        IDR_PROGRESS_CURSOR, 3, 2);
277     case WebCursorInfo::TypeNoDrop:
278     case WebCursorInfo::TypeNotAllowed:
279       return [NSCursor operationNotAllowedCursor];
280     case WebCursorInfo::TypeCopy:
281       return [NSCursor dragCopyCursor];
282     case WebCursorInfo::TypeNone:
283       return LoadCursor(IDR_NONE_CURSOR, 7, 7);
284     case WebCursorInfo::TypeZoomIn:
285       return GetCoreCursorWithFallback(kZoomInCursor,
286                                        IDR_ZOOMIN_CURSOR, 7, 7);
287     case WebCursorInfo::TypeZoomOut:
288       return GetCoreCursorWithFallback(kZoomOutCursor,
289                                        IDR_ZOOMOUT_CURSOR, 7, 7);
290     case WebCursorInfo::TypeGrab:
291       return [NSCursor openHandCursor];
292     case WebCursorInfo::TypeGrabbing:
293       return [NSCursor closedHandCursor];
294     case WebCursorInfo::TypeCustom:
295       return CreateCustomCursor(
296           custom_data_, custom_size_, custom_scale_, hotspot_);
297   }
298   NOTREACHED();
299   return nil;
302 void WebCursor::InitFromNSCursor(NSCursor* cursor) {
303   CursorInfo cursor_info;
305   if ([cursor isEqual:[NSCursor arrowCursor]]) {
306     cursor_info.type = WebCursorInfo::TypePointer;
307   } else if ([cursor isEqual:[NSCursor IBeamCursor]]) {
308     cursor_info.type = WebCursorInfo::TypeIBeam;
309   } else if ([cursor isEqual:[NSCursor crosshairCursor]]) {
310     cursor_info.type = WebCursorInfo::TypeCross;
311   } else if ([cursor isEqual:[NSCursor pointingHandCursor]]) {
312     cursor_info.type = WebCursorInfo::TypeHand;
313   } else if ([cursor isEqual:[NSCursor resizeLeftCursor]]) {
314     cursor_info.type = WebCursorInfo::TypeWestResize;
315   } else if ([cursor isEqual:[NSCursor resizeRightCursor]]) {
316     cursor_info.type = WebCursorInfo::TypeEastResize;
317   } else if ([cursor isEqual:[NSCursor resizeLeftRightCursor]]) {
318     cursor_info.type = WebCursorInfo::TypeEastWestResize;
319   } else if ([cursor isEqual:[NSCursor resizeUpCursor]]) {
320     cursor_info.type = WebCursorInfo::TypeNorthResize;
321   } else if ([cursor isEqual:[NSCursor resizeDownCursor]]) {
322     cursor_info.type = WebCursorInfo::TypeSouthResize;
323   } else if ([cursor isEqual:[NSCursor resizeUpDownCursor]]) {
324     cursor_info.type = WebCursorInfo::TypeNorthSouthResize;
325   } else if ([cursor isEqual:[NSCursor openHandCursor]]) {
326     cursor_info.type = WebCursorInfo::TypeGrab;
327   } else if ([cursor isEqual:[NSCursor closedHandCursor]]) {
328     cursor_info.type = WebCursorInfo::TypeGrabbing;
329   } else if ([cursor isEqual:[NSCursor operationNotAllowedCursor]]) {
330     cursor_info.type = WebCursorInfo::TypeNotAllowed;
331   } else if ([cursor isEqual:[NSCursor dragCopyCursor]]) {
332     cursor_info.type = WebCursorInfo::TypeCopy;
333   } else if ([cursor isEqual:[NSCursor contextualMenuCursor]]) {
334     cursor_info.type = WebCursorInfo::TypeContextMenu;
335   } else if (
336       [NSCursor respondsToSelector:@selector(IBeamCursorForVerticalLayout)] &&
337       [cursor isEqual:[NSCursor IBeamCursorForVerticalLayout]]) {
338     cursor_info.type = WebCursorInfo::TypeVerticalText;
339   } else {
340     // Also handles the [NSCursor disappearingItemCursor] case. Quick-and-dirty
341     // image conversion; TODO(avi): do better.
342     CGImageRef cg_image = nil;
343     NSImage* image = [cursor image];
344     for (id rep in [image representations]) {
345       if ([rep isKindOfClass:[NSBitmapImageRep class]]) {
346         cg_image = [rep CGImage];
347         break;
348       }
349     }
351     if (cg_image) {
352       cursor_info.type = WebCursorInfo::TypeCustom;
353       NSPoint hot_spot = [cursor hotSpot];
354       cursor_info.hotspot = gfx::Point(hot_spot.x, hot_spot.y);
355       cursor_info.custom_image = gfx::CGImageToSkBitmap(cg_image);
356     } else {
357       cursor_info.type = WebCursorInfo::TypePointer;
358     }
359   }
361   InitFromCursorInfo(cursor_info);
364 void WebCursor::InitPlatformData() {
365   return;
368 bool WebCursor::SerializePlatformData(Pickle* pickle) const {
369   return true;
372 bool WebCursor::DeserializePlatformData(PickleIterator* iter) {
373   return true;
376 bool WebCursor::IsPlatformDataEqual(const WebCursor& other) const {
377   return true;
380 void WebCursor::CleanupPlatformData() {
381   return;
384 void WebCursor::CopyPlatformData(const WebCursor& other) {
385   return;
388 }  // namespace content