Home | History | Annotate | Download | only in glue
      1 // Copyright (c) 2011 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 #include <Carbon/Carbon.h>
      9 
     10 #include "app/mac/nsimage_cache.h"
     11 #include "base/logging.h"
     12 #include "base/mac/scoped_cftyperef.h"
     13 #include "third_party/WebKit/Source/WebKit/chromium/public/WebCursorInfo.h"
     14 #include "third_party/WebKit/Source/WebKit/chromium/public/WebImage.h"
     15 #include "third_party/WebKit/Source/WebKit/chromium/public/WebSize.h"
     16 
     17 using WebKit::WebCursorInfo;
     18 using WebKit::WebImage;
     19 using WebKit::WebSize;
     20 
     21 namespace {
     22 
     23 // TODO: This image fetch can (and probably should) be serviced by the resource
     24 // resource bundle instead of going through the image cache.
     25 NSCursor* LoadCursor(const char* name, int x, int y) {
     26   NSString* file_name = [NSString stringWithUTF8String:name];
     27   DCHECK(file_name);
     28   NSImage* cursor_image = app::mac::GetCachedImageWithName(file_name);
     29   DCHECK(cursor_image);
     30   return [[[NSCursor alloc] initWithImage:cursor_image
     31                                   hotSpot:NSMakePoint(x, y)] autorelease];
     32 }
     33 
     34 CGImageRef CreateCGImageFromCustomData(const std::vector<char>& custom_data,
     35                                        const gfx::Size& custom_size) {
     36   base::mac::ScopedCFTypeRef<CGColorSpaceRef> cg_color(
     37       CGColorSpaceCreateDeviceRGB());
     38   // This is safe since we're not going to draw into the context we're creating.
     39   void* data = const_cast<char*>(&custom_data[0]);
     40   // The settings here match SetCustomData() below; keep in sync.
     41   base::mac::ScopedCFTypeRef<CGContextRef> context(
     42       CGBitmapContextCreate(data,
     43                             custom_size.width(),
     44                             custom_size.height(),
     45                             8,
     46                             custom_size.width()*4,
     47                             cg_color.get(),
     48                             kCGImageAlphaPremultipliedLast |
     49                             kCGBitmapByteOrder32Big));
     50   return CGBitmapContextCreateImage(context.get());
     51 }
     52 
     53 NSCursor* CreateCustomCursor(const std::vector<char>& custom_data,
     54                              const gfx::Size& custom_size,
     55                              const gfx::Point& hotspot) {
     56   // CG throws a cocoa exception if we try to create an empty image, which
     57   // results in an infinite loop.  This CHECK ensures that we crash instead.
     58   CHECK(!custom_data.empty());
     59 
     60   base::mac::ScopedCFTypeRef<CGImageRef> cg_image(
     61       CreateCGImageFromCustomData(custom_data, custom_size));
     62 
     63   NSBitmapImageRep* ns_bitmap =
     64       [[NSBitmapImageRep alloc] initWithCGImage:cg_image.get()];
     65   NSImage* cursor_image = [[NSImage alloc] init];
     66   DCHECK(cursor_image);
     67   [cursor_image addRepresentation:ns_bitmap];
     68   [ns_bitmap release];
     69 
     70   NSCursor* cursor = [[NSCursor alloc] initWithImage:cursor_image
     71                                              hotSpot:NSMakePoint(hotspot.x(),
     72                                                                  hotspot.y())];
     73   [cursor_image release];
     74 
     75   return [cursor autorelease];
     76 }
     77 
     78 }  // namespace
     79 
     80 // We're matching Safari's cursor choices; see platform/mac/CursorMac.mm
     81 NSCursor* WebCursor::GetCursor() const {
     82   switch (type_) {
     83     case WebCursorInfo::TypePointer:
     84       return [NSCursor arrowCursor];
     85     case WebCursorInfo::TypeCross:
     86       return LoadCursor("crossHairCursor", 11, 11);
     87     case WebCursorInfo::TypeHand:
     88       return LoadCursor("linkCursor", 6, 1);
     89     case WebCursorInfo::TypeIBeam:
     90       return [NSCursor IBeamCursor];
     91     case WebCursorInfo::TypeWait:
     92       return LoadCursor("waitCursor", 7, 7);
     93     case WebCursorInfo::TypeHelp:
     94       return LoadCursor("helpCursor", 8, 8);
     95     case WebCursorInfo::TypeEastResize:
     96     case WebCursorInfo::TypeEastPanning:
     97       return LoadCursor("eastResizeCursor", 14, 7);
     98     case WebCursorInfo::TypeNorthResize:
     99     case WebCursorInfo::TypeNorthPanning:
    100       return LoadCursor("northResizeCursor", 7, 1);
    101     case WebCursorInfo::TypeNorthEastResize:
    102     case WebCursorInfo::TypeNorthEastPanning:
    103       return LoadCursor("northEastResizeCursor", 14, 1);
    104     case WebCursorInfo::TypeNorthWestResize:
    105     case WebCursorInfo::TypeNorthWestPanning:
    106       return LoadCursor("northWestResizeCursor", 0, 0);
    107     case WebCursorInfo::TypeSouthResize:
    108     case WebCursorInfo::TypeSouthPanning:
    109       return LoadCursor("southResizeCursor", 7, 14);
    110     case WebCursorInfo::TypeSouthEastResize:
    111     case WebCursorInfo::TypeSouthEastPanning:
    112       return LoadCursor("southEastResizeCursor", 14, 14);
    113     case WebCursorInfo::TypeSouthWestResize:
    114     case WebCursorInfo::TypeSouthWestPanning:
    115       return LoadCursor("southWestResizeCursor", 1, 14);
    116     case WebCursorInfo::TypeWestResize:
    117     case WebCursorInfo::TypeWestPanning:
    118       return LoadCursor("westResizeCursor", 1, 7);
    119     case WebCursorInfo::TypeNorthSouthResize:
    120       return LoadCursor("northSouthResizeCursor", 7, 7);
    121     case WebCursorInfo::TypeEastWestResize:
    122       return LoadCursor("eastWestResizeCursor", 7, 7);
    123     case WebCursorInfo::TypeNorthEastSouthWestResize:
    124       return LoadCursor("northEastSouthWestResizeCursor", 7, 7);
    125     case WebCursorInfo::TypeNorthWestSouthEastResize:
    126       return LoadCursor("northWestSouthEastResizeCursor", 7, 7);
    127     case WebCursorInfo::TypeColumnResize:
    128       return [NSCursor resizeLeftRightCursor];
    129     case WebCursorInfo::TypeRowResize:
    130       return [NSCursor resizeUpDownCursor];
    131     case WebCursorInfo::TypeMiddlePanning:
    132     case WebCursorInfo::TypeMove:
    133       return LoadCursor("moveCursor", 7, 7);
    134     case WebCursorInfo::TypeVerticalText:
    135       return LoadCursor("verticalTextCursor", 7, 7);
    136     case WebCursorInfo::TypeCell:
    137       return LoadCursor("cellCursor", 7, 7);
    138     case WebCursorInfo::TypeContextMenu:
    139       return LoadCursor("contextMenuCursor", 3, 2);
    140     case WebCursorInfo::TypeAlias:
    141       return LoadCursor("aliasCursor", 11, 3);
    142     case WebCursorInfo::TypeProgress:
    143       return LoadCursor("progressCursor", 3, 2);
    144     case WebCursorInfo::TypeNoDrop:
    145       return LoadCursor("noDropCursor", 3, 1);
    146     case WebCursorInfo::TypeCopy:
    147       return LoadCursor("copyCursor", 3, 2);
    148     case WebCursorInfo::TypeNone:
    149       return LoadCursor("noneCursor", 7, 7);
    150     case WebCursorInfo::TypeNotAllowed:
    151       return LoadCursor("notAllowedCursor", 11, 11);
    152     case WebCursorInfo::TypeZoomIn:
    153       return LoadCursor("zoomInCursor", 7, 7);
    154     case WebCursorInfo::TypeZoomOut:
    155       return LoadCursor("zoomOutCursor", 7, 7);
    156     case WebCursorInfo::TypeGrab:
    157       return [NSCursor openHandCursor];
    158     case WebCursorInfo::TypeGrabbing:
    159       return [NSCursor closedHandCursor];
    160     case WebCursorInfo::TypeCustom:
    161       return CreateCustomCursor(custom_data_, custom_size_, hotspot_);
    162   }
    163   NOTREACHED();
    164   return nil;
    165 }
    166 
    167 gfx::NativeCursor WebCursor::GetNativeCursor() {
    168   return GetCursor();
    169 }
    170 
    171 void WebCursor::InitFromThemeCursor(ThemeCursor cursor) {
    172   WebKit::WebCursorInfo cursor_info;
    173 
    174   switch (cursor) {
    175     case kThemeArrowCursor:
    176       cursor_info.type = WebCursorInfo::TypePointer;
    177       break;
    178     case kThemeCopyArrowCursor:
    179       cursor_info.type = WebCursorInfo::TypeCopy;
    180       break;
    181     case kThemeAliasArrowCursor:
    182       cursor_info.type = WebCursorInfo::TypeAlias;
    183       break;
    184     case kThemeContextualMenuArrowCursor:
    185       cursor_info.type = WebCursorInfo::TypeContextMenu;
    186       break;
    187     case kThemeIBeamCursor:
    188       cursor_info.type = WebCursorInfo::TypeIBeam;
    189       break;
    190     case kThemeCrossCursor:
    191     case kThemePlusCursor:
    192       cursor_info.type = WebCursorInfo::TypeCross;
    193       break;
    194     case kThemeWatchCursor:
    195     case kThemeSpinningCursor:
    196       cursor_info.type = WebCursorInfo::TypeWait;
    197       break;
    198     case kThemeClosedHandCursor:
    199       cursor_info.type = WebCursorInfo::TypeGrabbing;
    200       break;
    201     case kThemeOpenHandCursor:
    202       cursor_info.type = WebCursorInfo::TypeGrab;
    203       break;
    204     case kThemePointingHandCursor:
    205     case kThemeCountingUpHandCursor:
    206     case kThemeCountingDownHandCursor:
    207     case kThemeCountingUpAndDownHandCursor:
    208       cursor_info.type = WebCursorInfo::TypeHand;
    209       break;
    210     case kThemeResizeLeftCursor:
    211       cursor_info.type = WebCursorInfo::TypeWestResize;
    212       break;
    213     case kThemeResizeRightCursor:
    214       cursor_info.type = WebCursorInfo::TypeEastResize;
    215       break;
    216     case kThemeResizeLeftRightCursor:
    217       cursor_info.type = WebCursorInfo::TypeEastWestResize;
    218       break;
    219     case kThemeNotAllowedCursor:
    220       cursor_info.type = WebCursorInfo::TypeNotAllowed;
    221       break;
    222     case kThemeResizeUpCursor:
    223       cursor_info.type = WebCursorInfo::TypeNorthResize;
    224       break;
    225     case kThemeResizeDownCursor:
    226       cursor_info.type = WebCursorInfo::TypeSouthResize;
    227       break;
    228     case kThemeResizeUpDownCursor:
    229       cursor_info.type = WebCursorInfo::TypeNorthSouthResize;
    230       break;
    231     case kThemePoofCursor:  // *shrug*
    232     default:
    233       cursor_info.type = WebCursorInfo::TypePointer;
    234       break;
    235   }
    236 
    237   InitFromCursorInfo(cursor_info);
    238 }
    239 
    240 void WebCursor::InitFromCursor(const Cursor* cursor) {
    241   // This conversion isn't perfect (in particular, the inversion effect of
    242   // data==1, mask==0 won't work). Not planning on fixing it.
    243 
    244   gfx::Size custom_size(16, 16);
    245   std::vector<char> raw_data;
    246   for (int row = 0; row < 16; ++row) {
    247     unsigned short data = cursor->data[row];
    248     unsigned short mask = cursor->mask[row];
    249 
    250     // The Core Endian flipper callback for 'CURS' doesn't flip Bits16 as if it
    251     // were a short (which it is), so we flip it here.
    252     data = ((data << 8) & 0xFF00) | ((data >> 8) & 0x00FF);
    253     mask = ((mask << 8) & 0xFF00) | ((mask >> 8) & 0x00FF);
    254 
    255     for (int bit = 0; bit < 16; ++bit) {
    256       if (data & 0x8000) {
    257         raw_data.push_back(0x00);
    258         raw_data.push_back(0x00);
    259         raw_data.push_back(0x00);
    260       } else {
    261         raw_data.push_back(0xFF);
    262         raw_data.push_back(0xFF);
    263         raw_data.push_back(0xFF);
    264       }
    265       if (mask & 0x8000)
    266         raw_data.push_back(0xFF);
    267       else
    268         raw_data.push_back(0x00);
    269       data <<= 1;
    270       mask <<= 1;
    271     }
    272   }
    273 
    274   base::mac::ScopedCFTypeRef<CGImageRef> cg_image(
    275       CreateCGImageFromCustomData(raw_data, custom_size));
    276 
    277   WebKit::WebCursorInfo cursor_info;
    278   cursor_info.type = WebCursorInfo::TypeCustom;
    279   cursor_info.hotSpot = WebKit::WebPoint(cursor->hotSpot.h, cursor->hotSpot.v);
    280   cursor_info.customImage = cg_image.get();
    281 
    282   InitFromCursorInfo(cursor_info);
    283 }
    284 
    285 void WebCursor::InitFromNSCursor(NSCursor* cursor) {
    286   WebKit::WebCursorInfo cursor_info;
    287 
    288   if ([cursor isEqual:[NSCursor arrowCursor]]) {
    289     cursor_info.type = WebCursorInfo::TypePointer;
    290   } else if ([cursor isEqual:[NSCursor IBeamCursor]]) {
    291     cursor_info.type = WebCursorInfo::TypeIBeam;
    292   } else if ([cursor isEqual:[NSCursor crosshairCursor]]) {
    293     cursor_info.type = WebCursorInfo::TypeCross;
    294   } else if ([cursor isEqual:[NSCursor pointingHandCursor]]) {
    295     cursor_info.type = WebCursorInfo::TypeHand;
    296   } else if ([cursor isEqual:[NSCursor resizeLeftCursor]]) {
    297     cursor_info.type = WebCursorInfo::TypeWestResize;
    298   } else if ([cursor isEqual:[NSCursor resizeRightCursor]]) {
    299     cursor_info.type = WebCursorInfo::TypeEastResize;
    300   } else if ([cursor isEqual:[NSCursor resizeLeftRightCursor]]) {
    301     cursor_info.type = WebCursorInfo::TypeEastWestResize;
    302   } else if ([cursor isEqual:[NSCursor resizeUpCursor]]) {
    303     cursor_info.type = WebCursorInfo::TypeNorthResize;
    304   } else if ([cursor isEqual:[NSCursor resizeDownCursor]]) {
    305     cursor_info.type = WebCursorInfo::TypeSouthResize;
    306   } else if ([cursor isEqual:[NSCursor resizeUpDownCursor]]) {
    307     cursor_info.type = WebCursorInfo::TypeNorthSouthResize;
    308   } else if ([cursor isEqual:[NSCursor openHandCursor]]) {
    309     cursor_info.type = WebCursorInfo::TypeGrab;
    310   } else if ([cursor isEqual:[NSCursor closedHandCursor]]) {
    311     cursor_info.type = WebCursorInfo::TypeGrabbing;
    312   } else {
    313     // Also handles the [NSCursor disappearingItemCursor] case. Quick-and-dirty
    314     // image conversion; TODO(avi): do better.
    315     CGImageRef cg_image = nil;
    316     NSImage* image = [cursor image];
    317     for (id rep in [image representations]) {
    318       if ([rep isKindOfClass:[NSBitmapImageRep class]]) {
    319         cg_image = [rep CGImage];
    320         break;
    321       }
    322     }
    323 
    324     if (cg_image) {
    325       cursor_info.type = WebCursorInfo::TypeCustom;
    326       NSPoint hot_spot = [cursor hotSpot];
    327       cursor_info.hotSpot = WebKit::WebPoint(hot_spot.x, hot_spot.y);
    328       cursor_info.customImage = cg_image;
    329     } else {
    330       cursor_info.type = WebCursorInfo::TypePointer;
    331     }
    332   }
    333 
    334   InitFromCursorInfo(cursor_info);
    335 }
    336 
    337 void WebCursor::SetCustomData(const WebImage& image) {
    338   if (image.isNull())
    339     return;
    340 
    341   base::mac::ScopedCFTypeRef<CGColorSpaceRef> cg_color(
    342       CGColorSpaceCreateDeviceRGB());
    343 
    344   const WebSize& image_dimensions = image.size();
    345   int image_width = image_dimensions.width;
    346   int image_height = image_dimensions.height;
    347 
    348   size_t size = image_height * image_width * 4;
    349   custom_data_.resize(size);
    350   custom_size_.set_width(image_width);
    351   custom_size_.set_height(image_height);
    352 
    353   // These settings match up with the code in CreateCustomCursor() above; keep
    354   // them in sync.
    355   // TODO(avi): test to ensure that the flags here are correct for RGBA
    356   base::mac::ScopedCFTypeRef<CGContextRef> context(
    357       CGBitmapContextCreate(&custom_data_[0],
    358                             image_width,
    359                             image_height,
    360                             8,
    361                             image_width * 4,
    362                             cg_color.get(),
    363                             kCGImageAlphaPremultipliedLast |
    364                             kCGBitmapByteOrder32Big));
    365   CGRect rect = CGRectMake(0, 0, image_width, image_height);
    366   CGContextDrawImage(context.get(), rect, image.getCGImageRef());
    367 }
    368 
    369 void WebCursor::ImageFromCustomData(WebImage* image) const {
    370   if (custom_data_.empty())
    371     return;
    372 
    373   base::mac::ScopedCFTypeRef<CGImageRef> cg_image(
    374       CreateCGImageFromCustomData(custom_data_, custom_size_));
    375   *image = cg_image.get();
    376 }
    377 
    378 void WebCursor::InitPlatformData() {
    379   return;
    380 }
    381 
    382 bool WebCursor::SerializePlatformData(Pickle* pickle) const {
    383   return true;
    384 }
    385 
    386 bool WebCursor::DeserializePlatformData(const Pickle* pickle, void** iter) {
    387   return true;
    388 }
    389 
    390 bool WebCursor::IsPlatformDataEqual(const WebCursor& other) const {
    391   return true;
    392 }
    393 
    394 void WebCursor::CleanupPlatformData() {
    395   return;
    396 }
    397 
    398 void WebCursor::CopyPlatformData(const WebCursor& other) {
    399   return;
    400 }
    401