forked from mirrors/gecko-dev
		
	
		
			
				
	
	
		
			1158 lines
		
	
	
	
		
			34 KiB
		
	
	
	
		
			Text
		
	
	
	
	
	
			
		
		
	
	
			1158 lines
		
	
	
	
		
			34 KiB
		
	
	
	
		
			Text
		
	
	
	
	
	
| /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 | |
| // vim:set ts=2 sts=2 sw=2 et cin:
 | |
| // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
 | |
| //
 | |
| // Redistribution and use in source and binary forms, with or without
 | |
| // modification, are permitted provided that the following conditions are
 | |
| // met:
 | |
| //
 | |
| //    * Redistributions of source code must retain the above copyright
 | |
| // notice, this list of conditions and the following disclaimer.
 | |
| //    * Redistributions in binary form must reproduce the above
 | |
| // copyright notice, this list of conditions and the following disclaimer
 | |
| // in the documentation and/or other materials provided with the
 | |
| // distribution.
 | |
| //    * Neither the name of Google Inc. nor the names of its
 | |
| // contributors may be used to endorse or promote products derived from
 | |
| // this software without specific prior written permission.
 | |
| //
 | |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | |
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | |
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 | |
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 | |
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | |
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 | |
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 | |
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | |
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | |
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 | |
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | |
| 
 | |
| #include "base/basictypes.h"
 | |
| #include "nsCocoaUtils.h"
 | |
| #include "PluginModuleChild.h"
 | |
| #include "nsDebug.h"
 | |
| #include "PluginInterposeOSX.h"
 | |
| #include <set>
 | |
| #import <AppKit/AppKit.h>
 | |
| #import <objc/runtime.h>
 | |
| #import <Carbon/Carbon.h>
 | |
| 
 | |
| using namespace mozilla::plugins;
 | |
| 
 | |
| namespace mac_plugin_interposing {
 | |
| 
 | |
| int32_t NSCursorInfo::mNativeCursorsSupported = -1;
 | |
| 
 | |
| // This constructor may be called from the browser process or the plugin
 | |
| // process.
 | |
| NSCursorInfo::NSCursorInfo()
 | |
|   : mType(TypeArrow)
 | |
|   , mHotSpot(nsPoint(0, 0))
 | |
|   , mCustomImageData(NULL)
 | |
|   , mCustomImageDataLength(0)
 | |
| {
 | |
| }
 | |
| 
 | |
| NSCursorInfo::NSCursorInfo(NSCursor* aCursor)
 | |
|   : mType(TypeArrow)
 | |
|   , mHotSpot(nsPoint(0, 0))
 | |
|   , mCustomImageData(NULL)
 | |
|   , mCustomImageDataLength(0)
 | |
| {
 | |
|   // This constructor is only ever called from the plugin process, so the
 | |
|   // following is safe.
 | |
|   if (!GetNativeCursorsSupported()) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   NSPoint hotSpotCocoa = [aCursor hotSpot];
 | |
|   mHotSpot = nsPoint(hotSpotCocoa.x, hotSpotCocoa.y);
 | |
| 
 | |
|   Class nsCursorClass = [NSCursor class];
 | |
|   if ([aCursor isEqual:[NSCursor arrowCursor]]) {
 | |
|     mType = TypeArrow;
 | |
|   } else if ([aCursor isEqual:[NSCursor closedHandCursor]]) {
 | |
|     mType = TypeClosedHand;
 | |
|   } else if ([aCursor isEqual:[NSCursor crosshairCursor]]) {
 | |
|     mType = TypeCrosshair;
 | |
|   } else if ([aCursor isEqual:[NSCursor disappearingItemCursor]]) {
 | |
|     mType = TypeDisappearingItem;
 | |
|   } else if ([aCursor isEqual:[NSCursor IBeamCursor]]) {
 | |
|     mType = TypeIBeam;
 | |
|   } else if ([aCursor isEqual:[NSCursor openHandCursor]]) {
 | |
|     mType = TypeOpenHand;
 | |
|   } else if ([aCursor isEqual:[NSCursor pointingHandCursor]]) {
 | |
|     mType = TypePointingHand;
 | |
|   } else if ([aCursor isEqual:[NSCursor resizeDownCursor]]) {
 | |
|     mType = TypeResizeDown;
 | |
|   } else if ([aCursor isEqual:[NSCursor resizeLeftCursor]]) {
 | |
|     mType = TypeResizeLeft;
 | |
|   } else if ([aCursor isEqual:[NSCursor resizeLeftRightCursor]]) {
 | |
|     mType = TypeResizeLeftRight;
 | |
|   } else if ([aCursor isEqual:[NSCursor resizeRightCursor]]) {
 | |
|     mType = TypeResizeRight;
 | |
|   } else if ([aCursor isEqual:[NSCursor resizeUpCursor]]) {
 | |
|     mType = TypeResizeUp;
 | |
|   } else if ([aCursor isEqual:[NSCursor resizeUpDownCursor]]) {
 | |
|     mType = TypeResizeUpDown;
 | |
|   // The following cursor types are only supported on OS X 10.6 and up.
 | |
|   } else if ([nsCursorClass respondsToSelector:@selector(contextualMenuCursor)] &&
 | |
|              [aCursor isEqual:[nsCursorClass performSelector:@selector(contextualMenuCursor)]]) {
 | |
|     mType = TypeContextualMenu;
 | |
|   } else if ([nsCursorClass respondsToSelector:@selector(dragCopyCursor)] &&
 | |
|              [aCursor isEqual:[nsCursorClass performSelector:@selector(dragCopyCursor)]]) {
 | |
|     mType = TypeDragCopy;
 | |
|   } else if ([nsCursorClass respondsToSelector:@selector(dragLinkCursor)] &&
 | |
|              [aCursor isEqual:[nsCursorClass performSelector:@selector(dragLinkCursor)]]) {
 | |
|     mType = TypeDragLink;
 | |
|   } else if ([nsCursorClass respondsToSelector:@selector(operationNotAllowedCursor)] &&
 | |
|              [aCursor isEqual:[nsCursorClass performSelector:@selector(operationNotAllowedCursor)]]) {
 | |
|     mType = TypeNotAllowed;
 | |
|   } else {
 | |
|     NSImage* image = [aCursor image];
 | |
|     NSArray* reps = image ? [image representations] : nil;
 | |
|     NSUInteger repsCount = reps ? [reps count] : 0;
 | |
|     if (!repsCount) {
 | |
|       // If we have a custom cursor with no image representations, assume we
 | |
|       // need a transparent cursor.
 | |
|       mType = TypeTransparent;
 | |
|     } else {
 | |
|       CGImageRef cgImage = nil;
 | |
|       // XXX We don't know how to deal with a cursor that doesn't have a
 | |
|       //     bitmap image representation.  For now we fall back to an arrow
 | |
|       //     cursor.
 | |
|       for (NSUInteger i = 0; i < repsCount; ++i) {
 | |
|         id rep = [reps objectAtIndex:i];
 | |
|         if ([rep isKindOfClass:[NSBitmapImageRep class]]) {
 | |
|           cgImage = [(NSBitmapImageRep*)rep CGImage];
 | |
|           break;
 | |
|         }
 | |
|       }
 | |
|       if (cgImage) {
 | |
|         CFMutableDataRef data = ::CFDataCreateMutable(kCFAllocatorDefault, 0);
 | |
|         if (data) {
 | |
|           CGImageDestinationRef dest = ::CGImageDestinationCreateWithData(data,
 | |
|                                                                           kUTTypePNG,
 | |
|                                                                           1,
 | |
|                                                                           NULL);
 | |
|           if (dest) {
 | |
|             ::CGImageDestinationAddImage(dest, cgImage, NULL);
 | |
|             if (::CGImageDestinationFinalize(dest)) {
 | |
|               uint32_t dataLength = (uint32_t) ::CFDataGetLength(data);
 | |
|               mCustomImageData = (uint8_t*) moz_xmalloc(dataLength);
 | |
|               ::CFDataGetBytes(data, ::CFRangeMake(0, dataLength), mCustomImageData);
 | |
|               mCustomImageDataLength = dataLength;
 | |
|               mType = TypeCustom;
 | |
|             }
 | |
|             ::CFRelease(dest);
 | |
|           }
 | |
|           ::CFRelease(data);
 | |
|         }
 | |
|       }
 | |
|       if (!mCustomImageData) {
 | |
|         mType = TypeArrow;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| NSCursorInfo::NSCursorInfo(const Cursor* aCursor)
 | |
|   : mType(TypeArrow)
 | |
|   , mHotSpot(nsPoint(0, 0))
 | |
|   , mCustomImageData(NULL)
 | |
|   , mCustomImageDataLength(0)
 | |
| {
 | |
|   // This constructor is only ever called from the plugin process, so the
 | |
|   // following is safe.
 | |
|   if (!GetNativeCursorsSupported()) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   mHotSpot = nsPoint(aCursor->hotSpot.h, aCursor->hotSpot.v);
 | |
| 
 | |
|   int width = 16, height = 16;
 | |
|   int bytesPerPixel = 4;
 | |
|   int rowBytes = width * bytesPerPixel;
 | |
|   int bitmapSize = height * rowBytes;
 | |
| 
 | |
|   bool isTransparent = true;
 | |
| 
 | |
|   uint8_t* bitmap = (uint8_t*) moz_xmalloc(bitmapSize);
 | |
|   // The way we create 'bitmap' is largely "borrowed" from Chrome's
 | |
|   // WebCursor::InitFromCursor().
 | |
|   for (int y = 0; y < height; ++y) {
 | |
|     unsigned short data = aCursor->data[y];
 | |
|     unsigned short mask = aCursor->mask[y];
 | |
|     // Change 'data' and 'mask' from big-endian to little-endian, but output
 | |
|     // big-endian data below.
 | |
|     data = ((data << 8) & 0xFF00) | ((data >> 8) & 0x00FF);
 | |
|     mask = ((mask << 8) & 0xFF00) | ((mask >> 8) & 0x00FF);
 | |
|     // It'd be nice to use a gray-scale bitmap.  But
 | |
|     // CGBitmapContextCreateImage() (used below) won't work with one that also
 | |
|     // has alpha values.
 | |
|     for (int x = 0; x < width; ++x) {
 | |
|       int offset = (y * rowBytes) + (x * bytesPerPixel);
 | |
|       // Color value
 | |
|       if (data & 0x8000) {
 | |
|         bitmap[offset]     = 0x0;
 | |
|         bitmap[offset + 1] = 0x0;
 | |
|         bitmap[offset + 2] = 0x0;
 | |
|       } else {
 | |
|         bitmap[offset]     = 0xFF;
 | |
|         bitmap[offset + 1] = 0xFF;
 | |
|         bitmap[offset + 2] = 0xFF;
 | |
|       }
 | |
|       // Mask value
 | |
|       if (mask & 0x8000) {
 | |
|         bitmap[offset + 3] = 0xFF;
 | |
|         isTransparent = false;
 | |
|       } else {
 | |
|         bitmap[offset + 3] = 0x0;
 | |
|       }
 | |
|       data <<= 1;
 | |
|       mask <<= 1;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (isTransparent) {
 | |
|     // If aCursor is transparent, we don't need to serialize custom cursor
 | |
|     // data over IPC.
 | |
|     mType = TypeTransparent;
 | |
|   } else {
 | |
|     CGColorSpaceRef color = ::CGColorSpaceCreateDeviceRGB();
 | |
|     if (color) {
 | |
|       CGContextRef context =
 | |
|         ::CGBitmapContextCreate(bitmap,
 | |
|                                 width,
 | |
|                                 height,
 | |
|                                 8,
 | |
|                                 rowBytes,
 | |
|                                 color,
 | |
|                                 kCGImageAlphaPremultipliedLast |
 | |
|                                   kCGBitmapByteOrder32Big);
 | |
|       if (context) {
 | |
|         CGImageRef image = ::CGBitmapContextCreateImage(context);
 | |
|         if (image) {
 | |
|           ::CFMutableDataRef data = ::CFDataCreateMutable(kCFAllocatorDefault, 0);
 | |
|           if (data) {
 | |
|             CGImageDestinationRef dest =
 | |
|               ::CGImageDestinationCreateWithData(data,
 | |
|                                                  kUTTypePNG,
 | |
|                                                  1,
 | |
|                                                  NULL);
 | |
|             if (dest) {
 | |
|               ::CGImageDestinationAddImage(dest, image, NULL);
 | |
|               if (::CGImageDestinationFinalize(dest)) {
 | |
|                 uint32_t dataLength = (uint32_t) ::CFDataGetLength(data);
 | |
|                 mCustomImageData = (uint8_t*) moz_xmalloc(dataLength);
 | |
|                 ::CFDataGetBytes(data,
 | |
|                                  ::CFRangeMake(0, dataLength),
 | |
|                                  mCustomImageData);
 | |
|                 mCustomImageDataLength = dataLength;
 | |
|                 mType = TypeCustom;
 | |
|               }
 | |
|               ::CFRelease(dest);
 | |
|             }
 | |
|             ::CFRelease(data);
 | |
|           }
 | |
|           ::CGImageRelease(image);
 | |
|         }
 | |
|         ::CGContextRelease(context);
 | |
|       }
 | |
|       ::CGColorSpaceRelease(color);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   free(bitmap);
 | |
| }
 | |
| 
 | |
| NSCursorInfo::~NSCursorInfo()
 | |
| {
 | |
|   if (mCustomImageData) {
 | |
|     free(mCustomImageData);
 | |
|   }
 | |
| }
 | |
| 
 | |
| NSCursor* NSCursorInfo::GetNSCursor() const
 | |
| {
 | |
|   NSCursor* retval = nil;
 | |
| 
 | |
|   Class nsCursorClass = [NSCursor class];
 | |
|   switch(mType) {
 | |
|     case TypeArrow:
 | |
|       retval = [NSCursor arrowCursor];
 | |
|       break;
 | |
|     case TypeClosedHand:
 | |
|       retval = [NSCursor closedHandCursor];
 | |
|       break;
 | |
|     case TypeCrosshair:
 | |
|       retval = [NSCursor crosshairCursor];
 | |
|       break;
 | |
|     case TypeDisappearingItem:
 | |
|       retval = [NSCursor disappearingItemCursor];
 | |
|       break;
 | |
|     case TypeIBeam:
 | |
|       retval = [NSCursor IBeamCursor];
 | |
|       break;
 | |
|     case TypeOpenHand:
 | |
|       retval = [NSCursor openHandCursor];
 | |
|       break;
 | |
|     case TypePointingHand:
 | |
|       retval = [NSCursor pointingHandCursor];
 | |
|       break;
 | |
|     case TypeResizeDown:
 | |
|       retval = [NSCursor resizeDownCursor];
 | |
|       break;
 | |
|     case TypeResizeLeft:
 | |
|       retval = [NSCursor resizeLeftCursor];
 | |
|       break;
 | |
|     case TypeResizeLeftRight:
 | |
|       retval = [NSCursor resizeLeftRightCursor];
 | |
|       break;
 | |
|     case TypeResizeRight:
 | |
|       retval = [NSCursor resizeRightCursor];
 | |
|       break;
 | |
|     case TypeResizeUp:
 | |
|       retval = [NSCursor resizeUpCursor];
 | |
|       break;
 | |
|     case TypeResizeUpDown:
 | |
|       retval = [NSCursor resizeUpDownCursor];
 | |
|       break;
 | |
|     // The following four cursor types are only supported on OS X 10.6 and up.
 | |
|     case TypeContextualMenu: {
 | |
|       if ([nsCursorClass respondsToSelector:@selector(contextualMenuCursor)]) {
 | |
|         retval = [nsCursorClass performSelector:@selector(contextualMenuCursor)];
 | |
|       }
 | |
|       break;
 | |
|     }
 | |
|     case TypeDragCopy: {
 | |
|       if ([nsCursorClass respondsToSelector:@selector(dragCopyCursor)]) {
 | |
|         retval = [nsCursorClass performSelector:@selector(dragCopyCursor)];
 | |
|       }
 | |
|       break;
 | |
|     }
 | |
|     case TypeDragLink: {
 | |
|       if ([nsCursorClass respondsToSelector:@selector(dragLinkCursor)]) {
 | |
|         retval = [nsCursorClass performSelector:@selector(dragLinkCursor)];
 | |
|       }
 | |
|       break;
 | |
|     }
 | |
|     case TypeNotAllowed: {
 | |
|       if ([nsCursorClass respondsToSelector:@selector(operationNotAllowedCursor)]) {
 | |
|         retval = [nsCursorClass performSelector:@selector(operationNotAllowedCursor)];
 | |
|       }
 | |
|       break;
 | |
|     }
 | |
|     case TypeTransparent:
 | |
|       retval = GetTransparentCursor();
 | |
|       break;
 | |
|     default:
 | |
|       break;
 | |
|   }
 | |
| 
 | |
|   if (!retval && mCustomImageData && mCustomImageDataLength) {
 | |
|     CGDataProviderRef provider = ::CGDataProviderCreateWithData(NULL,
 | |
|                                                                 (const void*)mCustomImageData,
 | |
|                                                                 mCustomImageDataLength,
 | |
|                                                                 NULL);
 | |
|     if (provider) {
 | |
|       CGImageRef cgImage = ::CGImageCreateWithPNGDataProvider(provider,
 | |
|                                                               NULL,
 | |
|                                                               false,
 | |
|                                                               kCGRenderingIntentDefault);
 | |
|       if (cgImage) {
 | |
|         NSBitmapImageRep* rep = [[NSBitmapImageRep alloc] initWithCGImage:cgImage];
 | |
|         if (rep) {
 | |
|           NSImage* image = [[NSImage alloc] init];
 | |
|           if (image) {
 | |
|             [image addRepresentation:rep];
 | |
|             retval = [[[NSCursor alloc] initWithImage:image 
 | |
|                                               hotSpot:NSMakePoint(mHotSpot.x, mHotSpot.y)]
 | |
|                       autorelease];
 | |
|             [image release];
 | |
|           }
 | |
|           [rep release];
 | |
|         }
 | |
|         ::CGImageRelease(cgImage);
 | |
|       }
 | |
|       ::CFRelease(provider);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   // Fall back to an arrow cursor if need be.
 | |
|   if (!retval) {
 | |
|     retval = [NSCursor arrowCursor];
 | |
|   }
 | |
| 
 | |
|   return retval;
 | |
| }
 | |
| 
 | |
| // Get a transparent cursor with the appropriate hot spot.  We need one if
 | |
| // (for example) we have a custom cursor with no image data.
 | |
| NSCursor* NSCursorInfo::GetTransparentCursor() const
 | |
| {
 | |
|   NSCursor* retval = nil;
 | |
| 
 | |
|   int width = 16, height = 16;
 | |
|   int bytesPerPixel = 2;
 | |
|   int rowBytes = width * bytesPerPixel;
 | |
|   int dataSize = height * rowBytes;
 | |
| 
 | |
|   uint8_t* data = (uint8_t*) moz_xmalloc(dataSize);
 | |
|   for (int y = 0; y < height; ++y) {
 | |
|     for (int x = 0; x < width; ++x) {
 | |
|       int offset = (y * rowBytes) + (x * bytesPerPixel);
 | |
|       data[offset] = 0x7E;  // Arbitrary gray-scale value
 | |
|       data[offset + 1] = 0; // Alpha value to make us transparent
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   NSBitmapImageRep* imageRep =
 | |
|     [[[NSBitmapImageRep alloc] initWithBitmapDataPlanes:nil
 | |
|                                              pixelsWide:width
 | |
|                                              pixelsHigh:height
 | |
|                                           bitsPerSample:8
 | |
|                                         samplesPerPixel:2
 | |
|                                                hasAlpha:YES
 | |
|                                                isPlanar:NO
 | |
|                                          colorSpaceName:NSCalibratedWhiteColorSpace
 | |
|                                             bytesPerRow:rowBytes
 | |
|                                            bitsPerPixel:16]
 | |
|      autorelease];
 | |
|   if (imageRep) {
 | |
|     uint8_t* repDataPtr = [imageRep bitmapData];
 | |
|     if (repDataPtr) {
 | |
|       memcpy(repDataPtr, data, dataSize);
 | |
|       NSImage *image =
 | |
|         [[[NSImage alloc] initWithSize:NSMakeSize(width, height)]
 | |
|          autorelease];
 | |
|       if (image) {
 | |
|         [image addRepresentation:imageRep];
 | |
|         retval =
 | |
|           [[[NSCursor alloc] initWithImage:image
 | |
|                                    hotSpot:NSMakePoint(mHotSpot.x, mHotSpot.y)]
 | |
|            autorelease];
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   free(data);
 | |
| 
 | |
|   // Fall back to an arrow cursor if (for some reason) the above code failed.
 | |
|   if (!retval) {
 | |
|     retval = [NSCursor arrowCursor];
 | |
|   }
 | |
| 
 | |
|   return retval;
 | |
| }
 | |
| 
 | |
| NSCursorInfo::Type NSCursorInfo::GetType() const
 | |
| {
 | |
|   return mType;
 | |
| }
 | |
| 
 | |
| const char* NSCursorInfo::GetTypeName() const
 | |
| {
 | |
|   switch(mType) {
 | |
|     case TypeCustom:
 | |
|       return "TypeCustom";
 | |
|     case TypeArrow:
 | |
|       return "TypeArrow";
 | |
|     case TypeClosedHand:
 | |
|       return "TypeClosedHand";
 | |
|     case TypeContextualMenu:
 | |
|       return "TypeContextualMenu";
 | |
|     case TypeCrosshair:
 | |
|       return "TypeCrosshair";
 | |
|     case TypeDisappearingItem:
 | |
|       return "TypeDisappearingItem";
 | |
|     case TypeDragCopy:
 | |
|       return "TypeDragCopy";
 | |
|     case TypeDragLink:
 | |
|       return "TypeDragLink";
 | |
|     case TypeIBeam:
 | |
|       return "TypeIBeam";
 | |
|     case TypeNotAllowed:
 | |
|       return "TypeNotAllowed";
 | |
|     case TypeOpenHand:
 | |
|       return "TypeOpenHand";
 | |
|     case TypePointingHand:
 | |
|       return "TypePointingHand";
 | |
|     case TypeResizeDown:
 | |
|       return "TypeResizeDown";
 | |
|     case TypeResizeLeft:
 | |
|       return "TypeResizeLeft";
 | |
|     case TypeResizeLeftRight:
 | |
|       return "TypeResizeLeftRight";
 | |
|     case TypeResizeRight:
 | |
|       return "TypeResizeRight";
 | |
|     case TypeResizeUp:
 | |
|       return "TypeResizeUp";
 | |
|     case TypeResizeUpDown:
 | |
|       return "TypeResizeUpDown";
 | |
|     case TypeTransparent:
 | |
|       return "TypeTransparent";
 | |
|     default:
 | |
|       break;
 | |
|   }
 | |
|   return "TypeUnknown";
 | |
| }
 | |
| 
 | |
| nsPoint NSCursorInfo::GetHotSpot() const
 | |
| {
 | |
|   return mHotSpot;
 | |
| }
 | |
| 
 | |
| uint8_t* NSCursorInfo::GetCustomImageData() const
 | |
| {
 | |
|   return mCustomImageData;
 | |
| }
 | |
| 
 | |
| uint32_t NSCursorInfo::GetCustomImageDataLength() const
 | |
| {
 | |
|   return mCustomImageDataLength;
 | |
| }
 | |
| 
 | |
| void NSCursorInfo::SetType(Type aType)
 | |
| {
 | |
|   mType = aType;
 | |
| }
 | |
| 
 | |
| void NSCursorInfo::SetHotSpot(nsPoint aHotSpot)
 | |
| {
 | |
|   mHotSpot = aHotSpot;
 | |
| }
 | |
| 
 | |
| void NSCursorInfo::SetCustomImageData(uint8_t* aData, uint32_t aDataLength)
 | |
| {
 | |
|   if (mCustomImageData) {
 | |
|     free(mCustomImageData);
 | |
|   }
 | |
|   if (aDataLength) {
 | |
|     mCustomImageData = (uint8_t*) moz_xmalloc(aDataLength);
 | |
|     memcpy(mCustomImageData, aData, aDataLength);
 | |
|   } else {
 | |
|     mCustomImageData = NULL;
 | |
|   }
 | |
|   mCustomImageDataLength = aDataLength;
 | |
| }
 | |
| 
 | |
| // This should never be called from the browser process -- only from the
 | |
| // plugin process.
 | |
| bool NSCursorInfo::GetNativeCursorsSupported()
 | |
| {
 | |
|   if (mNativeCursorsSupported == -1) {
 | |
|     ENSURE_PLUGIN_THREAD(false);
 | |
|     PluginModuleChild *pmc = PluginModuleChild::GetChrome();
 | |
|     if (pmc) {
 | |
|       bool result = pmc->GetNativeCursorsSupported();
 | |
|       if (result) {
 | |
|         mNativeCursorsSupported = 1;
 | |
|       } else {
 | |
|         mNativeCursorsSupported = 0;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
|   return (mNativeCursorsSupported == 1);
 | |
| }
 | |
| 
 | |
| } // namespace mac_plugin_interposing
 | |
| 
 | |
| namespace mac_plugin_interposing {
 | |
| namespace parent {
 | |
| 
 | |
| // Tracks plugin windows currently visible.
 | |
| std::set<uint32_t> plugin_visible_windows_set_;
 | |
| // Tracks full screen windows currently visible.
 | |
| std::set<uint32_t> plugin_fullscreen_windows_set_;
 | |
| // Tracks modal windows currently visible.
 | |
| std::set<uint32_t> plugin_modal_windows_set_;
 | |
| 
 | |
| void OnPluginShowWindow(uint32_t window_id,
 | |
|                         CGRect window_bounds,
 | |
|                         bool modal) {
 | |
|   plugin_visible_windows_set_.insert(window_id);
 | |
| 
 | |
|   if (modal)
 | |
|     plugin_modal_windows_set_.insert(window_id);
 | |
| 
 | |
|   CGRect main_display_bounds = ::CGDisplayBounds(CGMainDisplayID());
 | |
| 
 | |
|   if (CGRectEqualToRect(window_bounds, main_display_bounds) &&
 | |
|       (plugin_fullscreen_windows_set_.find(window_id) ==
 | |
|        plugin_fullscreen_windows_set_.end())) {
 | |
|     plugin_fullscreen_windows_set_.insert(window_id);
 | |
| 
 | |
|     nsCocoaUtils::HideOSChromeOnScreen(true);
 | |
|   }
 | |
| }
 | |
| 
 | |
| static void ActivateProcess(pid_t pid) {
 | |
|   ProcessSerialNumber process;
 | |
|   OSStatus status = ::GetProcessForPID(pid, &process);
 | |
| 
 | |
|   if (status == noErr) {
 | |
|     SetFrontProcess(&process);
 | |
|   } else {
 | |
|     NS_WARNING("Unable to get process for pid.");
 | |
|   }
 | |
| }
 | |
| 
 | |
| // Must be called on the UI thread.
 | |
| // If plugin_pid is -1, the browser will be the active process on return,
 | |
| // otherwise that process will be given focus back before this function returns.
 | |
| static void ReleasePluginFullScreen(pid_t plugin_pid) {
 | |
|   // Releasing full screen only works if we are the frontmost process; grab
 | |
|   // focus, but give it back to the plugin process if requested.
 | |
|   ActivateProcess(base::GetCurrentProcId());
 | |
| 
 | |
|   nsCocoaUtils::HideOSChromeOnScreen(false);
 | |
| 
 | |
|   if (plugin_pid != -1) {
 | |
|     ActivateProcess(plugin_pid);
 | |
|   }
 | |
| }
 | |
| 
 | |
| void OnPluginHideWindow(uint32_t window_id, pid_t aPluginPid) {
 | |
|   bool had_windows = !plugin_visible_windows_set_.empty();
 | |
|   plugin_visible_windows_set_.erase(window_id);
 | |
|   bool browser_needs_activation = had_windows &&
 | |
|       plugin_visible_windows_set_.empty();
 | |
| 
 | |
|   plugin_modal_windows_set_.erase(window_id);
 | |
|   if (plugin_fullscreen_windows_set_.find(window_id) !=
 | |
|       plugin_fullscreen_windows_set_.end()) {
 | |
|     plugin_fullscreen_windows_set_.erase(window_id);
 | |
|     pid_t plugin_pid = browser_needs_activation ? -1 : aPluginPid;
 | |
|     browser_needs_activation = false;
 | |
|     ReleasePluginFullScreen(plugin_pid);
 | |
|   }
 | |
| 
 | |
|   if (browser_needs_activation) {
 | |
|     ActivateProcess(getpid());
 | |
|   }
 | |
| }
 | |
| 
 | |
| void OnSetCursor(const NSCursorInfo& cursorInfo)
 | |
| {
 | |
|   NSCursor* aCursor = cursorInfo.GetNSCursor();
 | |
|   if (aCursor) {
 | |
|     [aCursor set];
 | |
|   }
 | |
| }
 | |
| 
 | |
| void OnShowCursor(bool show)
 | |
| {
 | |
|   if (show) {
 | |
|     [NSCursor unhide];
 | |
|   } else {
 | |
|     [NSCursor hide];
 | |
|   }
 | |
| }
 | |
| 
 | |
| void OnPushCursor(const NSCursorInfo& cursorInfo)
 | |
| {
 | |
|   NSCursor* aCursor = cursorInfo.GetNSCursor();
 | |
|   if (aCursor) {
 | |
|     [aCursor push];
 | |
|   }
 | |
| }
 | |
| 
 | |
| void OnPopCursor()
 | |
| {
 | |
|   [NSCursor pop];
 | |
| }
 | |
| 
 | |
| } // namespace parent
 | |
| } // namespace mac_plugin_interposing
 | |
| 
 | |
| namespace mac_plugin_interposing {
 | |
| namespace child {
 | |
| 
 | |
| // TODO(stuartmorgan): Make this an IPC to order the plugin process above the
 | |
| // browser process only if the browser is current frontmost.
 | |
| void FocusPluginProcess() {
 | |
|   ProcessSerialNumber this_process, front_process;
 | |
|   if ((GetCurrentProcess(&this_process) != noErr) ||
 | |
|       (GetFrontProcess(&front_process) != noErr)) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   Boolean matched = false;
 | |
|   if ((SameProcess(&this_process, &front_process, &matched) == noErr) &&
 | |
|       !matched) {
 | |
|     SetFrontProcess(&this_process);
 | |
|   }
 | |
| }
 | |
| 
 | |
| void NotifyBrowserOfPluginShowWindow(uint32_t window_id, CGRect bounds,
 | |
|                                      bool modal) {
 | |
|   ENSURE_PLUGIN_THREAD_VOID();
 | |
| 
 | |
|   PluginModuleChild *pmc = PluginModuleChild::GetChrome();
 | |
|   if (pmc)
 | |
|     pmc->PluginShowWindow(window_id, modal, bounds);
 | |
| }
 | |
| 
 | |
| void NotifyBrowserOfPluginHideWindow(uint32_t window_id, CGRect bounds) {
 | |
|   ENSURE_PLUGIN_THREAD_VOID();
 | |
| 
 | |
|   PluginModuleChild *pmc = PluginModuleChild::GetChrome();
 | |
|   if (pmc)
 | |
|     pmc->PluginHideWindow(window_id);
 | |
| }
 | |
| 
 | |
| void NotifyBrowserOfSetCursor(NSCursorInfo& aCursorInfo)
 | |
| {
 | |
|   ENSURE_PLUGIN_THREAD_VOID();
 | |
|   PluginModuleChild *pmc = PluginModuleChild::GetChrome();
 | |
|   if (pmc) {
 | |
|     pmc->SetCursor(aCursorInfo);
 | |
|   }
 | |
| }
 | |
| 
 | |
| void NotifyBrowserOfShowCursor(bool show)
 | |
| {
 | |
|   ENSURE_PLUGIN_THREAD_VOID();
 | |
|   PluginModuleChild *pmc = PluginModuleChild::GetChrome();
 | |
|   if (pmc) {
 | |
|     pmc->ShowCursor(show);
 | |
|   }
 | |
| }
 | |
| 
 | |
| void NotifyBrowserOfPushCursor(NSCursorInfo& aCursorInfo)
 | |
| {
 | |
|   ENSURE_PLUGIN_THREAD_VOID();
 | |
|   PluginModuleChild *pmc = PluginModuleChild::GetChrome();
 | |
|   if (pmc) {
 | |
|     pmc->PushCursor(aCursorInfo);
 | |
|   }
 | |
| }
 | |
| 
 | |
| void NotifyBrowserOfPopCursor()
 | |
| {
 | |
|   ENSURE_PLUGIN_THREAD_VOID();
 | |
|   PluginModuleChild *pmc = PluginModuleChild::GetChrome();
 | |
|   if (pmc) {
 | |
|     pmc->PopCursor();
 | |
|   }
 | |
| }
 | |
| 
 | |
| struct WindowInfo {
 | |
|   uint32_t window_id;
 | |
|   CGRect bounds;
 | |
|   explicit WindowInfo(NSWindow* aWindow) {
 | |
|     NSInteger window_num = [aWindow windowNumber];
 | |
|     window_id = window_num > 0 ? window_num : 0;
 | |
|     bounds = NSRectToCGRect([aWindow frame]);
 | |
|   }
 | |
| };
 | |
| 
 | |
| static void OnPluginWindowClosed(const WindowInfo& window_info) {
 | |
|   if (window_info.window_id == 0)
 | |
|     return;
 | |
|   mac_plugin_interposing::child::NotifyBrowserOfPluginHideWindow(window_info.window_id,
 | |
|                                                                  window_info.bounds);
 | |
| }
 | |
| 
 | |
| static void OnPluginWindowShown(const WindowInfo& window_info, BOOL is_modal) {
 | |
|   // The window id is 0 if it has never been shown (including while it is the
 | |
|   // process of being shown for the first time); when that happens, we'll catch
 | |
|   // it in _setWindowNumber instead.
 | |
|   static BOOL s_pending_display_is_modal = NO;
 | |
|   if (window_info.window_id == 0) {
 | |
|     if (is_modal)
 | |
|       s_pending_display_is_modal = YES;
 | |
|     return;
 | |
|   }
 | |
|   if (s_pending_display_is_modal) {
 | |
|     is_modal = YES;
 | |
|     s_pending_display_is_modal = NO;
 | |
|   }
 | |
|   mac_plugin_interposing::child::NotifyBrowserOfPluginShowWindow(
 | |
|     window_info.window_id, window_info.bounds, is_modal);
 | |
| }
 | |
| 
 | |
| static BOOL OnSetCursor(NSCursorInfo &aInfo)
 | |
| {
 | |
|   if (NSCursorInfo::GetNativeCursorsSupported()) {
 | |
|     NotifyBrowserOfSetCursor(aInfo);
 | |
|     return YES;
 | |
|   }
 | |
|   return NO;
 | |
| }
 | |
| 
 | |
| static BOOL OnHideCursor()
 | |
| {
 | |
|   if (NSCursorInfo::GetNativeCursorsSupported()) {
 | |
|     NotifyBrowserOfShowCursor(NO);
 | |
|     return YES;
 | |
|   }
 | |
|   return NO;
 | |
| }
 | |
| 
 | |
| static BOOL OnUnhideCursor()
 | |
| {
 | |
|   if (NSCursorInfo::GetNativeCursorsSupported()) {
 | |
|     NotifyBrowserOfShowCursor(YES);
 | |
|     return YES;
 | |
|   }
 | |
|   return NO;
 | |
| }
 | |
| 
 | |
| static BOOL OnPushCursor(NSCursorInfo &aInfo)
 | |
| {
 | |
|   if (NSCursorInfo::GetNativeCursorsSupported()) {
 | |
|     NotifyBrowserOfPushCursor(aInfo);
 | |
|     return YES;
 | |
|   }
 | |
|   return NO;
 | |
| }
 | |
| 
 | |
| static BOOL OnPopCursor()
 | |
| {
 | |
|   if (NSCursorInfo::GetNativeCursorsSupported()) {
 | |
|     NotifyBrowserOfPopCursor();
 | |
|     return YES;
 | |
|   }
 | |
|   return NO;
 | |
| }
 | |
| 
 | |
| } // namespace child
 | |
| } // namespace mac_plugin_interposing
 | |
| 
 | |
| using namespace mac_plugin_interposing::child;
 | |
| 
 | |
| @interface NSWindow (PluginInterposing)
 | |
| - (void)pluginInterpose_orderOut:(id)sender;
 | |
| - (void)pluginInterpose_orderFront:(id)sender;
 | |
| - (void)pluginInterpose_makeKeyAndOrderFront:(id)sender;
 | |
| - (void)pluginInterpose_setWindowNumber:(NSInteger)num;
 | |
| @end
 | |
| 
 | |
| @implementation NSWindow (PluginInterposing)
 | |
| 
 | |
| - (void)pluginInterpose_orderOut:(id)sender {
 | |
|   WindowInfo window_info(self);
 | |
|   [self pluginInterpose_orderOut:sender];
 | |
|   OnPluginWindowClosed(window_info);
 | |
| }
 | |
| 
 | |
| - (void)pluginInterpose_orderFront:(id)sender {
 | |
|   mac_plugin_interposing::child::FocusPluginProcess();
 | |
|   [self pluginInterpose_orderFront:sender];
 | |
|   OnPluginWindowShown(WindowInfo(self), NO);
 | |
| }
 | |
| 
 | |
| - (void)pluginInterpose_makeKeyAndOrderFront:(id)sender {
 | |
|   mac_plugin_interposing::child::FocusPluginProcess();
 | |
|   [self pluginInterpose_makeKeyAndOrderFront:sender];
 | |
|   OnPluginWindowShown(WindowInfo(self), NO);
 | |
| }
 | |
| 
 | |
| - (void)pluginInterpose_setWindowNumber:(NSInteger)num {
 | |
|   if (num > 0)
 | |
|     mac_plugin_interposing::child::FocusPluginProcess();
 | |
|   [self pluginInterpose_setWindowNumber:num];
 | |
|   if (num > 0)
 | |
|     OnPluginWindowShown(WindowInfo(self), NO);
 | |
| }
 | |
| 
 | |
| @end
 | |
| 
 | |
| @interface NSApplication (PluginInterposing)
 | |
| - (NSInteger)pluginInterpose_runModalForWindow:(NSWindow*)window;
 | |
| @end
 | |
| 
 | |
| @implementation NSApplication (PluginInterposing)
 | |
| 
 | |
| - (NSInteger)pluginInterpose_runModalForWindow:(NSWindow*)window {
 | |
|   mac_plugin_interposing::child::FocusPluginProcess();
 | |
|   // This is out-of-order relative to the other calls, but runModalForWindow:
 | |
|   // won't return until the window closes, and the order only matters for
 | |
|   // full-screen windows.
 | |
|   OnPluginWindowShown(WindowInfo(window), YES);
 | |
|   return [self pluginInterpose_runModalForWindow:window];
 | |
| }
 | |
| 
 | |
| @end
 | |
| 
 | |
| // Hook commands to manipulate the current cursor, so that they can be passed
 | |
| // from the child process to the parent process.  These commands have no
 | |
| // effect unless they're performed in the parent process.
 | |
| @interface NSCursor (PluginInterposing)
 | |
| - (void)pluginInterpose_set;
 | |
| - (void)pluginInterpose_push;
 | |
| - (void)pluginInterpose_pop;
 | |
| + (NSCursor*)pluginInterpose_currentCursor;
 | |
| + (void)pluginInterpose_hide;
 | |
| + (void)pluginInterpose_unhide;
 | |
| + (void)pluginInterpose_pop;
 | |
| @end
 | |
| 
 | |
| // Cache the results of [NSCursor set], [NSCursor push] and [NSCursor pop].
 | |
| // The last element is always the current cursor.
 | |
| static NSMutableArray* gCursorStack = nil;
 | |
| 
 | |
| static BOOL initCursorStack()
 | |
| {
 | |
|   if (!gCursorStack) {
 | |
|     gCursorStack = [[NSMutableArray arrayWithCapacity:5] retain];
 | |
|   }
 | |
|   return (gCursorStack != NULL);
 | |
| }
 | |
| 
 | |
| static NSCursor* currentCursorFromCache()
 | |
| {
 | |
|   if (!initCursorStack())
 | |
|     return nil;
 | |
|   return (NSCursor*) [gCursorStack lastObject];
 | |
| }
 | |
| 
 | |
| static void setCursorInCache(NSCursor* aCursor)
 | |
| {
 | |
|   if (!initCursorStack() || !aCursor)
 | |
|     return;
 | |
|   NSUInteger count = [gCursorStack count];
 | |
|   if (count) {
 | |
|     [gCursorStack replaceObjectAtIndex:count - 1 withObject:aCursor];
 | |
|   } else {
 | |
|     [gCursorStack addObject:aCursor];
 | |
|   }
 | |
| }
 | |
| 
 | |
| static void pushCursorInCache(NSCursor* aCursor)
 | |
| {
 | |
|   if (!initCursorStack() || !aCursor)
 | |
|     return;
 | |
|   [gCursorStack addObject:aCursor];
 | |
| }
 | |
| 
 | |
| static void popCursorInCache()
 | |
| {
 | |
|   if (!initCursorStack())
 | |
|     return;
 | |
|   // Apple's doc on the +[NSCursor pop] method says:  "If the current cursor
 | |
|   // is the only cursor on the stack, this method does nothing."
 | |
|   if ([gCursorStack count] > 1) {
 | |
|     [gCursorStack removeLastObject];
 | |
|   }
 | |
| }
 | |
| 
 | |
| @implementation NSCursor (PluginInterposing)
 | |
| 
 | |
| - (void)pluginInterpose_set
 | |
| {
 | |
|   NSCursorInfo info(self);
 | |
|   OnSetCursor(info);
 | |
|   setCursorInCache(self);
 | |
|   [self pluginInterpose_set];
 | |
| }
 | |
| 
 | |
| - (void)pluginInterpose_push
 | |
| {
 | |
|   NSCursorInfo info(self);
 | |
|   OnPushCursor(info);
 | |
|   pushCursorInCache(self);
 | |
|   [self pluginInterpose_push];
 | |
| }
 | |
| 
 | |
| - (void)pluginInterpose_pop
 | |
| {
 | |
|   OnPopCursor();
 | |
|   popCursorInCache();
 | |
|   [self pluginInterpose_pop];
 | |
| }
 | |
| 
 | |
| // The currentCursor method always returns nil when running in a background
 | |
| // process.  But this may confuse plugins (notably Flash, see bug 621117).  So
 | |
| // if we get a nil return from the "call to super", we return a cursor that's
 | |
| // been cached by previous calls to set or push.  According to Apple's docs,
 | |
| // currentCursor "only returns the cursor set by your application using
 | |
| // NSCursor methods".  So we don't need to worry about changes to the cursor
 | |
| // made by other methods like SetThemeCursor().
 | |
| + (NSCursor*)pluginInterpose_currentCursor
 | |
| {
 | |
|   NSCursor* retval = [self pluginInterpose_currentCursor];
 | |
|   if (!retval) {
 | |
|     retval = currentCursorFromCache();
 | |
|   }
 | |
|   return retval;
 | |
| }
 | |
| 
 | |
| + (void)pluginInterpose_hide
 | |
| {
 | |
|   OnHideCursor();
 | |
|   [self pluginInterpose_hide];
 | |
| }
 | |
| 
 | |
| + (void)pluginInterpose_unhide
 | |
| {
 | |
|   OnUnhideCursor();
 | |
|   [self pluginInterpose_unhide];
 | |
| }
 | |
| 
 | |
| + (void)pluginInterpose_pop
 | |
| {
 | |
|   OnPopCursor();
 | |
|   popCursorInCache();
 | |
|   [self pluginInterpose_pop];
 | |
| }
 | |
| 
 | |
| @end
 | |
| 
 | |
| static void ExchangeMethods(Class target_class,
 | |
|                             BOOL class_method,
 | |
|                             SEL original,
 | |
|                             SEL replacement) {
 | |
|   Method m1;
 | |
|   Method m2;
 | |
|   if (class_method) {
 | |
|     m1 = class_getClassMethod(target_class, original);
 | |
|     m2 = class_getClassMethod(target_class, replacement);
 | |
|   } else {
 | |
|     m1 = class_getInstanceMethod(target_class, original);
 | |
|     m2 = class_getInstanceMethod(target_class, replacement);
 | |
|   }
 | |
| 
 | |
|   if (m1 == m2)
 | |
|     return;
 | |
| 
 | |
|   if (m1 && m2)
 | |
|     method_exchangeImplementations(m1, m2);
 | |
|   else
 | |
|     NS_NOTREACHED("Cocoa swizzling failed");
 | |
| }
 | |
| 
 | |
| namespace mac_plugin_interposing {
 | |
| namespace child {
 | |
| 
 | |
| void SetUpCocoaInterposing() {
 | |
|   Class nswindow_class = [NSWindow class];
 | |
|   ExchangeMethods(nswindow_class, NO, @selector(orderOut:),
 | |
|                   @selector(pluginInterpose_orderOut:));
 | |
|   ExchangeMethods(nswindow_class, NO, @selector(orderFront:),
 | |
|                   @selector(pluginInterpose_orderFront:));
 | |
|   ExchangeMethods(nswindow_class, NO, @selector(makeKeyAndOrderFront:),
 | |
|                   @selector(pluginInterpose_makeKeyAndOrderFront:));
 | |
|   ExchangeMethods(nswindow_class, NO, @selector(_setWindowNumber:),
 | |
|                   @selector(pluginInterpose_setWindowNumber:));
 | |
| 
 | |
|   ExchangeMethods([NSApplication class], NO, @selector(runModalForWindow:),
 | |
|                   @selector(pluginInterpose_runModalForWindow:));
 | |
| 
 | |
|   Class nscursor_class = [NSCursor class];
 | |
|   ExchangeMethods(nscursor_class, NO, @selector(set),
 | |
|                   @selector(pluginInterpose_set));
 | |
|   ExchangeMethods(nscursor_class, NO, @selector(push),
 | |
|                   @selector(pluginInterpose_push));
 | |
|   ExchangeMethods(nscursor_class, NO, @selector(pop),
 | |
|                   @selector(pluginInterpose_pop));
 | |
|   ExchangeMethods(nscursor_class, YES, @selector(currentCursor),
 | |
|                   @selector(pluginInterpose_currentCursor));
 | |
|   ExchangeMethods(nscursor_class, YES, @selector(hide),
 | |
|                   @selector(pluginInterpose_hide));
 | |
|   ExchangeMethods(nscursor_class, YES, @selector(unhide),
 | |
|                   @selector(pluginInterpose_unhide));
 | |
|   ExchangeMethods(nscursor_class, YES, @selector(pop),
 | |
|                   @selector(pluginInterpose_pop));
 | |
| }
 | |
| 
 | |
| }  // namespace child
 | |
| }  // namespace mac_plugin_interposing
 | |
| 
 | |
| // Called from plugin_child_interpose.mm, which hooks calls to
 | |
| // SetCursor() (the QuickDraw call) from the plugin child process.
 | |
| extern "C" NS_VISIBILITY_DEFAULT BOOL
 | |
| mac_plugin_interposing_child_OnSetCursor(const Cursor* cursor)
 | |
| {
 | |
|   NSCursorInfo info(cursor);
 | |
|   return OnSetCursor(info);
 | |
| }
 | |
| 
 | |
| // Called from plugin_child_interpose.mm, which hooks calls to
 | |
| // SetThemeCursor() (the Appearance Manager call) from the plugin child
 | |
| // process.
 | |
| extern "C" NS_VISIBILITY_DEFAULT BOOL
 | |
| mac_plugin_interposing_child_OnSetThemeCursor(ThemeCursor cursor)
 | |
| {
 | |
|   NSCursorInfo info;
 | |
|   switch (cursor) {
 | |
|     case kThemeArrowCursor:
 | |
|       info.SetType(NSCursorInfo::TypeArrow);
 | |
|       break;
 | |
|     case kThemeCopyArrowCursor:
 | |
|       info.SetType(NSCursorInfo::TypeDragCopy);
 | |
|       break;
 | |
|     case kThemeAliasArrowCursor:
 | |
|       info.SetType(NSCursorInfo::TypeDragLink);
 | |
|       break;
 | |
|     case kThemeContextualMenuArrowCursor:
 | |
|       info.SetType(NSCursorInfo::TypeContextualMenu);
 | |
|       break;
 | |
|     case kThemeIBeamCursor:
 | |
|       info.SetType(NSCursorInfo::TypeIBeam);
 | |
|       break;
 | |
|     case kThemeCrossCursor:
 | |
|     case kThemePlusCursor:
 | |
|       info.SetType(NSCursorInfo::TypeCrosshair);
 | |
|       break;
 | |
|     case kThemeWatchCursor:
 | |
|     case kThemeSpinningCursor:
 | |
|       info.SetType(NSCursorInfo::TypeArrow);
 | |
|       break;
 | |
|     case kThemeClosedHandCursor:
 | |
|       info.SetType(NSCursorInfo::TypeClosedHand);
 | |
|       break;
 | |
|     case kThemeOpenHandCursor:
 | |
|       info.SetType(NSCursorInfo::TypeOpenHand);
 | |
|       break;
 | |
|     case kThemePointingHandCursor:
 | |
|     case kThemeCountingUpHandCursor:
 | |
|     case kThemeCountingDownHandCursor:
 | |
|     case kThemeCountingUpAndDownHandCursor:
 | |
|       info.SetType(NSCursorInfo::TypePointingHand);
 | |
|       break;
 | |
|     case kThemeResizeLeftCursor:
 | |
|       info.SetType(NSCursorInfo::TypeResizeLeft);
 | |
|       break;
 | |
|     case kThemeResizeRightCursor:
 | |
|       info.SetType(NSCursorInfo::TypeResizeRight);
 | |
|       break;
 | |
|     case kThemeResizeLeftRightCursor:
 | |
|       info.SetType(NSCursorInfo::TypeResizeLeftRight);
 | |
|       break;
 | |
|     case kThemeNotAllowedCursor:
 | |
|       info.SetType(NSCursorInfo::TypeNotAllowed);
 | |
|       break;
 | |
|     case kThemeResizeUpCursor:
 | |
|       info.SetType(NSCursorInfo::TypeResizeUp);
 | |
|       break;
 | |
|     case kThemeResizeDownCursor:
 | |
|       info.SetType(NSCursorInfo::TypeResizeDown);
 | |
|       break;
 | |
|     case kThemeResizeUpDownCursor:
 | |
|       info.SetType(NSCursorInfo::TypeResizeUpDown);
 | |
|       break;
 | |
|     case kThemePoofCursor:
 | |
|       info.SetType(NSCursorInfo::TypeDisappearingItem);
 | |
|       break;
 | |
|     default:
 | |
|       info.SetType(NSCursorInfo::TypeArrow);
 | |
|       break;
 | |
|   }
 | |
|   return OnSetCursor(info);
 | |
| }
 | |
| 
 | |
| extern "C" NS_VISIBILITY_DEFAULT BOOL
 | |
| mac_plugin_interposing_child_OnHideCursor()
 | |
| {
 | |
|   return OnHideCursor();
 | |
| }
 | |
| 
 | |
| extern "C" NS_VISIBILITY_DEFAULT BOOL
 | |
| mac_plugin_interposing_child_OnShowCursor()
 | |
| {
 | |
|   return OnUnhideCursor();
 | |
| }
 | 
