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 "content/public/common/content_client.h"
13 #include "grit/webkit_resources.h"
14 #include "skia/ext/skia_utils_mac.h"
15 #include "third_party/WebKit/public/platform/WebCursorInfo.h"
16 #include "third_party/WebKit/public/platform/WebSize.h"
17 #include "ui/gfx/image/image.h"
18 #include "ui/gfx/point_conversions.h"
19 #include "ui/gfx/size_conversions.h"
22 using blink::WebCursorInfo;
25 // Declare symbols that are part of the 10.7 SDK.
26 #if !defined(MAC_OS_X_VERSION_10_7) || \
27 MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7
29 @interface NSCursor (LionSDKDeclarations)
30 + (NSCursor*)IBeamCursorForVerticalLayout;
33 #endif // MAC_OS_X_VERSION_10_7
35 // Private interface to CoreCursor, as of Mac OS X 10.7. This is essentially the
36 // implementation of WKCursor in WebKitSystemInterface.
42 kOperationNotAllowedCursor = 3,
43 kBusyButClickableCursor = 4,
45 kClosedHandCursor = 11,
47 kPointingHandCursor = 13,
48 kCountingUpHandCursor = 14,
49 kCountingDownHandCursor = 15,
50 kCountingUpAndDownHandCursor = 16,
51 kResizeLeftCursor = 17,
52 kResizeRightCursor = 18,
53 kResizeLeftRightCursor = 19,
54 kCrosshairCursor = 20,
56 kResizeDownCursor = 22,
57 kResizeUpDownCursor = 23,
58 kContextualMenuCursor = 24,
59 kDisappearingItemCursor = 25,
60 kVerticalIBeamCursor = 26,
61 kResizeEastCursor = 27,
62 kResizeEastWestCursor = 28,
63 kResizeNortheastCursor = 29,
64 kResizeNortheastSouthwestCursor = 30,
65 kResizeNorthCursor = 31,
66 kResizeNorthSouthCursor = 32,
67 kResizeNorthwestCursor = 33,
68 kResizeNorthwestSoutheastCursor = 34,
69 kResizeSoutheastCursor = 35,
70 kResizeSouthCursor = 36,
71 kResizeSouthwestCursor = 37,
72 kResizeWestCursor = 38,
74 kHelpCursor = 40, // Present on >= 10.7.3.
75 kCellCursor = 41, // Present on >= 10.7.3.
76 kZoomInCursor = 42, // Present on >= 10.7.3.
77 kZoomOutCursor = 43 // Present on >= 10.7.3.
79 typedef long long CrCoreCursorType;
81 @interface CrCoreCursor : NSCursor {
83 CrCoreCursorType type_;
86 + (id)cursorWithType:(CrCoreCursorType)type;
87 - (id)initWithType:(CrCoreCursorType)type;
88 - (CrCoreCursorType)_coreCursorType;
92 @implementation CrCoreCursor
94 + (id)cursorWithType:(CrCoreCursorType)type {
95 NSCursor* cursor = [[CrCoreCursor alloc] initWithType:type];
97 return [cursor autorelease];
103 - (id)initWithType:(CrCoreCursorType)type {
104 if ((self = [super init])) {
110 - (CrCoreCursorType)_coreCursorType {
118 NSCursor* LoadCursor(int resource_id, int hotspot_x, int hotspot_y) {
119 const gfx::Image& cursor_image =
120 content::GetContentClient()->GetNativeImageNamed(resource_id);
121 DCHECK(!cursor_image.IsEmpty());
122 return [[[NSCursor alloc] initWithImage:cursor_image.ToNSImage()
123 hotSpot:NSMakePoint(hotspot_x,
124 hotspot_y)] autorelease];
127 // Gets a specified cursor from CoreCursor, falling back to loading it from the
128 // image cache if CoreCursor cannot provide it.
129 NSCursor* GetCoreCursorWithFallback(CrCoreCursorType type,
133 if (base::mac::IsOSLionOrLater()) {
134 NSCursor* cursor = [CrCoreCursor cursorWithType:type];
139 return LoadCursor(resource_id, hotspot_x, hotspot_y);
142 NSCursor* CreateCustomCursor(const std::vector<char>& custom_data,
143 const gfx::Size& custom_size,
145 const gfx::Point& hotspot) {
146 // If the data is missing, leave the backing transparent.
148 size_t data_size = 0;
149 if (!custom_data.empty()) {
150 // This is safe since we're not going to draw into the context we're
152 data = const_cast<char*>(&custom_data[0]);
153 data_size = custom_data.size();
156 // If the size is empty, use a 1x1 transparent image.
157 gfx::Size size = custom_size;
158 if (size.IsEmpty()) {
164 bitmap.setConfig(SkBitmap::kARGB_8888_Config, size.width(), size.height());
165 bitmap.allocPixels();
167 memcpy(bitmap.getAddr32(0, 0), data, data_size);
169 bitmap.eraseARGB(0, 0, 0, 0);
171 // Convert from pixels to view units.
172 if (custom_scale == 0)
174 NSSize dip_size = NSSizeFromCGSize(gfx::ToFlooredSize(
175 gfx::ScaleSize(custom_size, 1 / custom_scale)).ToCGSize());
176 NSPoint dip_hotspot = NSPointFromCGPoint(gfx::ToFlooredPoint(
177 gfx::ScalePoint(hotspot, 1 / custom_scale)).ToCGPoint());
179 // Both the image and its representation need to have the same size for
180 // cursors to appear in high resolution on retina displays. Note that the
181 // size of a representation is not the same as pixelsWide or pixelsHigh.
182 NSImage* cursor_image = gfx::SkBitmapToNSImage(bitmap);
183 [cursor_image setSize:dip_size];
184 [[[cursor_image representations] objectAtIndex:0] setSize:dip_size];
186 NSCursor* cursor = [[NSCursor alloc] initWithImage:cursor_image
187 hotSpot:dip_hotspot];
189 return [cursor autorelease];
196 // Match Safari's cursor choices; see platform/mac/CursorMac.mm .
197 gfx::NativeCursor WebCursor::GetNativeCursor() {
199 case WebCursorInfo::TypePointer:
200 return [NSCursor arrowCursor];
201 case WebCursorInfo::TypeCross:
202 return [NSCursor crosshairCursor];
203 case WebCursorInfo::TypeHand:
204 // If >= 10.7, the pointingHandCursor has a shadow so use it. Otherwise
205 // use the custom one.
206 if (base::mac::IsOSLionOrLater())
207 return [NSCursor pointingHandCursor];
209 return LoadCursor(IDR_LINK_CURSOR, 6, 1);
210 case WebCursorInfo::TypeIBeam:
211 return [NSCursor IBeamCursor];
212 case WebCursorInfo::TypeWait:
213 return GetCoreCursorWithFallback(kBusyButClickableCursor,
214 IDR_WAIT_CURSOR, 7, 7);
215 case WebCursorInfo::TypeHelp:
216 return GetCoreCursorWithFallback(kHelpCursor,
217 IDR_HELP_CURSOR, 8, 8);
218 case WebCursorInfo::TypeEastResize:
219 case WebCursorInfo::TypeEastPanning:
220 return GetCoreCursorWithFallback(kResizeEastCursor,
221 IDR_EAST_RESIZE_CURSOR, 14, 7);
222 case WebCursorInfo::TypeNorthResize:
223 case WebCursorInfo::TypeNorthPanning:
224 return GetCoreCursorWithFallback(kResizeNorthCursor,
225 IDR_NORTH_RESIZE_CURSOR, 7, 1);
226 case WebCursorInfo::TypeNorthEastResize:
227 case WebCursorInfo::TypeNorthEastPanning:
228 return GetCoreCursorWithFallback(kResizeNortheastCursor,
229 IDR_NORTHEAST_RESIZE_CURSOR, 14, 1);
230 case WebCursorInfo::TypeNorthWestResize:
231 case WebCursorInfo::TypeNorthWestPanning:
232 return GetCoreCursorWithFallback(kResizeNorthwestCursor,
233 IDR_NORTHWEST_RESIZE_CURSOR, 0, 0);
234 case WebCursorInfo::TypeSouthResize:
235 case WebCursorInfo::TypeSouthPanning:
236 return GetCoreCursorWithFallback(kResizeSouthCursor,
237 IDR_SOUTH_RESIZE_CURSOR, 7, 14);
238 case WebCursorInfo::TypeSouthEastResize:
239 case WebCursorInfo::TypeSouthEastPanning:
240 return GetCoreCursorWithFallback(kResizeSoutheastCursor,
241 IDR_SOUTHEAST_RESIZE_CURSOR, 14, 14);
242 case WebCursorInfo::TypeSouthWestResize:
243 case WebCursorInfo::TypeSouthWestPanning:
244 return GetCoreCursorWithFallback(kResizeSouthwestCursor,
245 IDR_SOUTHWEST_RESIZE_CURSOR, 1, 14);
246 case WebCursorInfo::TypeWestResize:
247 case WebCursorInfo::TypeWestPanning:
248 return GetCoreCursorWithFallback(kResizeWestCursor,
249 IDR_WEST_RESIZE_CURSOR, 1, 7);
250 case WebCursorInfo::TypeNorthSouthResize:
251 return GetCoreCursorWithFallback(kResizeNorthSouthCursor,
252 IDR_NORTHSOUTH_RESIZE_CURSOR, 7, 7);
253 case WebCursorInfo::TypeEastWestResize:
254 return GetCoreCursorWithFallback(kResizeEastWestCursor,
255 IDR_EASTWEST_RESIZE_CURSOR, 7, 7);
256 case WebCursorInfo::TypeNorthEastSouthWestResize:
257 return GetCoreCursorWithFallback(kResizeNortheastSouthwestCursor,
258 IDR_NORTHEASTSOUTHWEST_RESIZE_CURSOR,
260 case WebCursorInfo::TypeNorthWestSouthEastResize:
261 return GetCoreCursorWithFallback(kResizeNorthwestSoutheastCursor,
262 IDR_NORTHWESTSOUTHEAST_RESIZE_CURSOR,
264 case WebCursorInfo::TypeColumnResize:
265 return [NSCursor resizeLeftRightCursor];
266 case WebCursorInfo::TypeRowResize:
267 return [NSCursor resizeUpDownCursor];
268 case WebCursorInfo::TypeMiddlePanning:
269 case WebCursorInfo::TypeMove:
270 return GetCoreCursorWithFallback(kMoveCursor,
271 IDR_MOVE_CURSOR, 7, 7);
272 case WebCursorInfo::TypeVerticalText:
273 // IBeamCursorForVerticalLayout is >= 10.7.
274 if ([NSCursor respondsToSelector:@selector(IBeamCursorForVerticalLayout)])
275 return [NSCursor IBeamCursorForVerticalLayout];
277 return LoadCursor(IDR_VERTICALTEXT_CURSOR, 7, 7);
278 case WebCursorInfo::TypeCell:
279 return GetCoreCursorWithFallback(kCellCursor,
280 IDR_CELL_CURSOR, 7, 7);
281 case WebCursorInfo::TypeContextMenu:
282 return [NSCursor contextualMenuCursor];
283 case WebCursorInfo::TypeAlias:
284 return GetCoreCursorWithFallback(kMakeAliasCursor,
285 IDR_ALIAS_CURSOR, 11, 3);
286 case WebCursorInfo::TypeProgress:
287 return GetCoreCursorWithFallback(kBusyButClickableCursor,
288 IDR_PROGRESS_CURSOR, 3, 2);
289 case WebCursorInfo::TypeNoDrop:
290 case WebCursorInfo::TypeNotAllowed:
291 return [NSCursor operationNotAllowedCursor];
292 case WebCursorInfo::TypeCopy:
293 return [NSCursor dragCopyCursor];
294 case WebCursorInfo::TypeNone:
295 return LoadCursor(IDR_NONE_CURSOR, 7, 7);
296 case WebCursorInfo::TypeZoomIn:
297 return GetCoreCursorWithFallback(kZoomInCursor,
298 IDR_ZOOMIN_CURSOR, 7, 7);
299 case WebCursorInfo::TypeZoomOut:
300 return GetCoreCursorWithFallback(kZoomOutCursor,
301 IDR_ZOOMOUT_CURSOR, 7, 7);
302 case WebCursorInfo::TypeGrab:
303 return [NSCursor openHandCursor];
304 case WebCursorInfo::TypeGrabbing:
305 return [NSCursor closedHandCursor];
306 case WebCursorInfo::TypeCustom:
307 return CreateCustomCursor(
308 custom_data_, custom_size_, custom_scale_, hotspot_);
314 void WebCursor::InitFromNSCursor(NSCursor* cursor) {
315 CursorInfo cursor_info;
317 if ([cursor isEqual:[NSCursor arrowCursor]]) {
318 cursor_info.type = WebCursorInfo::TypePointer;
319 } else if ([cursor isEqual:[NSCursor IBeamCursor]]) {
320 cursor_info.type = WebCursorInfo::TypeIBeam;
321 } else if ([cursor isEqual:[NSCursor crosshairCursor]]) {
322 cursor_info.type = WebCursorInfo::TypeCross;
323 } else if ([cursor isEqual:[NSCursor pointingHandCursor]]) {
324 cursor_info.type = WebCursorInfo::TypeHand;
325 } else if ([cursor isEqual:[NSCursor resizeLeftCursor]]) {
326 cursor_info.type = WebCursorInfo::TypeWestResize;
327 } else if ([cursor isEqual:[NSCursor resizeRightCursor]]) {
328 cursor_info.type = WebCursorInfo::TypeEastResize;
329 } else if ([cursor isEqual:[NSCursor resizeLeftRightCursor]]) {
330 cursor_info.type = WebCursorInfo::TypeEastWestResize;
331 } else if ([cursor isEqual:[NSCursor resizeUpCursor]]) {
332 cursor_info.type = WebCursorInfo::TypeNorthResize;
333 } else if ([cursor isEqual:[NSCursor resizeDownCursor]]) {
334 cursor_info.type = WebCursorInfo::TypeSouthResize;
335 } else if ([cursor isEqual:[NSCursor resizeUpDownCursor]]) {
336 cursor_info.type = WebCursorInfo::TypeNorthSouthResize;
337 } else if ([cursor isEqual:[NSCursor openHandCursor]]) {
338 cursor_info.type = WebCursorInfo::TypeGrab;
339 } else if ([cursor isEqual:[NSCursor closedHandCursor]]) {
340 cursor_info.type = WebCursorInfo::TypeGrabbing;
341 } else if ([cursor isEqual:[NSCursor operationNotAllowedCursor]]) {
342 cursor_info.type = WebCursorInfo::TypeNotAllowed;
343 } else if ([cursor isEqual:[NSCursor dragCopyCursor]]) {
344 cursor_info.type = WebCursorInfo::TypeCopy;
345 } else if ([cursor isEqual:[NSCursor contextualMenuCursor]]) {
346 cursor_info.type = WebCursorInfo::TypeContextMenu;
348 [NSCursor respondsToSelector:@selector(IBeamCursorForVerticalLayout)] &&
349 [cursor isEqual:[NSCursor IBeamCursorForVerticalLayout]]) {
350 cursor_info.type = WebCursorInfo::TypeVerticalText;
352 // Also handles the [NSCursor disappearingItemCursor] case. Quick-and-dirty
353 // image conversion; TODO(avi): do better.
354 CGImageRef cg_image = nil;
355 NSImage* image = [cursor image];
356 for (id rep in [image representations]) {
357 if ([rep isKindOfClass:[NSBitmapImageRep class]]) {
358 cg_image = [rep CGImage];
364 cursor_info.type = WebCursorInfo::TypeCustom;
365 NSPoint hot_spot = [cursor hotSpot];
366 cursor_info.hotspot = gfx::Point(hot_spot.x, hot_spot.y);
367 cursor_info.custom_image = gfx::CGImageToSkBitmap(cg_image);
369 cursor_info.type = WebCursorInfo::TypePointer;
373 InitFromCursorInfo(cursor_info);
376 void WebCursor::InitPlatformData() {
380 bool WebCursor::SerializePlatformData(Pickle* pickle) const {
384 bool WebCursor::DeserializePlatformData(PickleIterator* iter) {
388 bool WebCursor::IsPlatformDataEqual(const WebCursor& other) const {
392 void WebCursor::CleanupPlatformData() {
396 void WebCursor::CopyPlatformData(const WebCursor& other) {
400 } // namespace content