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