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