зеркало из https://github.com/mozilla/gecko-dev.git
Bug 621117 - Support native cursor manipulation from OOP plugins on OS X. r=josh,bsmedberg a=sheriff
This commit is contained in:
Родитель
cc7b927b54
Коммит
cf976ee5ab
|
@ -901,6 +901,17 @@ pref("dom.ipc.plugins.enabled.x86_64", true);
|
|||
pref("dom.ipc.plugins.enabled", true);
|
||||
#endif
|
||||
|
||||
// This pref governs whether we attempt to work around problems caused by
|
||||
// plugins using OS calls to manipulate the cursor while running out-of-
|
||||
// process. These workarounds all involve intercepting (hooking) certain
|
||||
// OS calls in the plugin process, then arranging to make certain OS calls
|
||||
// in the browser process. Eventually plugins will be required to use the
|
||||
// NPAPI to manipulate the cursor, and these workarounds will be removed.
|
||||
// See bug 621117.
|
||||
#ifdef XP_MACOSX
|
||||
pref("dom.ipc.plugins.nativeCursorSupport", true);
|
||||
#endif
|
||||
|
||||
#ifdef XP_WIN
|
||||
pref("browser.taskbar.previews.enable", false);
|
||||
pref("browser.taskbar.previews.max", 20);
|
||||
|
|
|
@ -58,6 +58,7 @@
|
|||
#endif
|
||||
#ifdef XP_MACOSX
|
||||
@BINPATH@/@MOZ_CHILD_PROCESS_NAME@.app/
|
||||
@BINPATH@/@DLL_PREFIX@plugin_child_interpose@DLL_SUFFIX@
|
||||
#else
|
||||
@BINPATH@/@MOZ_CHILD_PROCESS_NAME@
|
||||
#endif
|
||||
|
|
|
@ -135,10 +135,18 @@ EXPORTS_mozilla/plugins += \
|
|||
endif
|
||||
|
||||
ifeq (cocoa,$(MOZ_WIDGET_TOOLKIT))
|
||||
DIRS += \
|
||||
interpose \
|
||||
$(NULL)
|
||||
|
||||
CMMSRCS += \
|
||||
PluginUtilsOSX.mm \
|
||||
PluginInterposeOSX.mm \
|
||||
$(NULL)
|
||||
|
||||
EXPORTS_mozilla/plugins += \
|
||||
PluginInterposeOSX.h \
|
||||
$(NULL)
|
||||
endif
|
||||
|
||||
LOCAL_INCLUDES = \
|
||||
|
|
|
@ -47,6 +47,7 @@ using NPError;
|
|||
using NPNVariable;
|
||||
using base::FileDescriptor;
|
||||
using mozilla::plugins::NativeThreadId;
|
||||
using mac_plugin_interposing::NSCursorInfo;
|
||||
|
||||
namespace mozilla {
|
||||
namespace plugins {
|
||||
|
@ -135,6 +136,13 @@ parent:
|
|||
int32_t aX, int32_t aY,
|
||||
size_t aWidth, size_t aHeight);
|
||||
async PluginHideWindow(uint32_t aWindowId);
|
||||
|
||||
// OS X Specific calls to allow the plugin to manage the cursor.
|
||||
async SetCursor(NSCursorInfo cursorInfo);
|
||||
async ShowCursor(bool show);
|
||||
async PushCursor(NSCursorInfo cursorInfo);
|
||||
async PopCursor();
|
||||
sync GetNativeCursorsSupported() returns (bool supported);
|
||||
};
|
||||
|
||||
} // namespace plugins
|
||||
|
|
|
@ -27,12 +27,84 @@
|
|||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#ifndef DOM_PLUGINS_IPC_PLUGININTERPOSEOSX_H
|
||||
#define DOM_PLUGINS_IPC_PLUGININTERPOSEOSX_H
|
||||
|
||||
#include "base/basictypes.h"
|
||||
#include "nsPoint.h"
|
||||
|
||||
// Make this includable from non-Objective-C code.
|
||||
#ifndef __OBJC__
|
||||
class NSCursor;
|
||||
#else
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#endif
|
||||
|
||||
namespace mac_plugin_interposing {
|
||||
|
||||
// Class used to serialize NSCursor objects over IPC between processes.
|
||||
class NSCursorInfo {
|
||||
public:
|
||||
enum Type {
|
||||
TypeCustom,
|
||||
TypeArrow,
|
||||
TypeClosedHand,
|
||||
TypeContextualMenu, // Only supported on OS X 10.6 and up
|
||||
TypeCrosshair,
|
||||
TypeDisappearingItem,
|
||||
TypeDragCopy, // Only supported on OS X 10.6 and up
|
||||
TypeDragLink, // Only supported on OS X 10.6 and up
|
||||
TypeIBeam,
|
||||
TypeNotAllowed, // Only supported on OS X 10.6 and up
|
||||
TypeOpenHand,
|
||||
TypePointingHand,
|
||||
TypeResizeDown,
|
||||
TypeResizeLeft,
|
||||
TypeResizeLeftRight,
|
||||
TypeResizeRight,
|
||||
TypeResizeUp,
|
||||
TypeResizeUpDown,
|
||||
TypeTransparent // Special type
|
||||
};
|
||||
|
||||
NSCursorInfo();
|
||||
NSCursorInfo(NSCursor* aCursor);
|
||||
NSCursorInfo(const Cursor* aCursor);
|
||||
~NSCursorInfo();
|
||||
|
||||
NSCursor* GetNSCursor() const;
|
||||
Type GetType() const;
|
||||
const char* GetTypeName() const;
|
||||
nsPoint GetHotSpot() const;
|
||||
uint8_t* GetCustomImageData() const;
|
||||
uint32_t GetCustomImageDataLength() const;
|
||||
|
||||
void SetType(Type aType);
|
||||
void SetHotSpot(nsPoint aHotSpot);
|
||||
void SetCustomImageData(uint8_t* aData, uint32_t aDataLength);
|
||||
|
||||
static PRBool GetNativeCursorsSupported();
|
||||
|
||||
private:
|
||||
NSCursor* GetTransparentCursor() const;
|
||||
|
||||
Type mType;
|
||||
// The hot spot's coordinate system is the cursor's coordinate system, and
|
||||
// has an upper-left origin (in both Cocoa and pre-Cocoa systems).
|
||||
nsPoint mHotSpot;
|
||||
uint8_t* mCustomImageData;
|
||||
uint32_t mCustomImageDataLength;
|
||||
static int32_t mNativeCursorsSupported;
|
||||
};
|
||||
|
||||
namespace parent {
|
||||
|
||||
void OnPluginShowWindow(uint32_t window_id, CGRect window_bounds, bool modal);
|
||||
void OnPluginHideWindow(uint32_t window_id, pid_t aPluginPid);
|
||||
void OnSetCursor(const NSCursorInfo& cursorInfo);
|
||||
void OnShowCursor(bool show);
|
||||
void OnPushCursor(const NSCursorInfo& cursorInfo);
|
||||
void OnPopCursor();
|
||||
|
||||
}
|
||||
|
||||
|
@ -43,3 +115,5 @@ void SetUpCocoaInterposing();
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
#endif /* DOM_PLUGINS_IPC_PLUGININTERPOSEOSX_H */
|
||||
|
|
|
@ -32,13 +32,534 @@
|
|||
#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 mozilla::plugins::PluginModuleChild;
|
||||
using mozilla::plugins::AssertPluginThread;
|
||||
|
||||
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;
|
||||
|
||||
PRBool isTransparent = PR_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 = PR_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);
|
||||
}
|
||||
}
|
||||
|
||||
moz_free(bitmap);
|
||||
}
|
||||
|
||||
NSCursorInfo::~NSCursorInfo()
|
||||
{
|
||||
if (mCustomImageData) {
|
||||
moz_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];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
moz_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) {
|
||||
moz_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.
|
||||
PRBool NSCursorInfo::GetNativeCursorsSupported()
|
||||
{
|
||||
if (mNativeCursorsSupported == -1) {
|
||||
AssertPluginThread();
|
||||
PluginModuleChild *pmc = PluginModuleChild::current();
|
||||
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 {
|
||||
|
||||
|
@ -114,6 +635,36 @@ void OnPluginHideWindow(uint32_t window_id, pid_t aPluginPid) {
|
|||
}
|
||||
}
|
||||
|
||||
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];
|
||||
}
|
||||
|
||||
} // parent
|
||||
} // namespace mac_plugin_interposing
|
||||
|
||||
|
@ -153,6 +704,42 @@ void NotifyBrowserOfPluginHideWindow(uint32_t window_id, CGRect bounds) {
|
|||
pmc->PluginHideWindow(window_id);
|
||||
}
|
||||
|
||||
void NotifyBrowserOfSetCursor(NSCursorInfo& aCursorInfo)
|
||||
{
|
||||
AssertPluginThread();
|
||||
PluginModuleChild *pmc = PluginModuleChild::current();
|
||||
if (pmc) {
|
||||
pmc->SetCursor(aCursorInfo);
|
||||
}
|
||||
}
|
||||
|
||||
void NotifyBrowserOfShowCursor(bool show)
|
||||
{
|
||||
AssertPluginThread();
|
||||
PluginModuleChild *pmc = PluginModuleChild::current();
|
||||
if (pmc) {
|
||||
pmc->ShowCursor(show);
|
||||
}
|
||||
}
|
||||
|
||||
void NotifyBrowserOfPushCursor(NSCursorInfo& aCursorInfo)
|
||||
{
|
||||
AssertPluginThread();
|
||||
PluginModuleChild *pmc = PluginModuleChild::current();
|
||||
if (pmc) {
|
||||
pmc->PushCursor(aCursorInfo);
|
||||
}
|
||||
}
|
||||
|
||||
void NotifyBrowserOfPopCursor()
|
||||
{
|
||||
AssertPluginThread();
|
||||
PluginModuleChild *pmc = PluginModuleChild::current();
|
||||
if (pmc) {
|
||||
pmc->PopCursor();
|
||||
}
|
||||
}
|
||||
|
||||
struct WindowInfo {
|
||||
uint32_t window_id;
|
||||
CGRect bounds;
|
||||
|
@ -188,10 +775,55 @@ static void OnPluginWindowShown(const WindowInfo& window_info, BOOL is_modal) {
|
|||
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;
|
||||
}
|
||||
|
||||
} // child
|
||||
} // namespace mac_plugin_interposing
|
||||
|
||||
using mac_plugin_interposing::child::WindowInfo;
|
||||
using namespace mac_plugin_interposing::child;
|
||||
|
||||
@interface NSWindow (PluginInterposing)
|
||||
- (void)pluginInterpose_orderOut:(id)sender;
|
||||
|
@ -247,6 +879,130 @@ using mac_plugin_interposing::child::WindowInfo;
|
|||
|
||||
@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,
|
||||
|
@ -286,8 +1042,118 @@ void SetUpCocoaInterposing() {
|
|||
|
||||
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();
|
||||
}
|
||||
|
|
|
@ -56,6 +56,12 @@
|
|||
#ifdef MOZ_CRASHREPORTER
|
||||
# include "nsExceptionHandler.h"
|
||||
#endif
|
||||
#ifdef XP_MACOSX
|
||||
#include "PluginInterposeOSX.h"
|
||||
#else
|
||||
namespace mac_plugin_interposing { class NSCursorInfo { }; }
|
||||
#endif
|
||||
using mac_plugin_interposing::NSCursorInfo;
|
||||
|
||||
namespace mozilla {
|
||||
namespace plugins {
|
||||
|
@ -538,6 +544,100 @@ struct ParamTraits<NPNSString*>
|
|||
};
|
||||
#endif
|
||||
|
||||
#ifdef XP_MACOSX
|
||||
template <>
|
||||
struct ParamTraits<NSCursorInfo>
|
||||
{
|
||||
typedef NSCursorInfo paramType;
|
||||
|
||||
static void Write(Message* aMsg, const paramType& aParam)
|
||||
{
|
||||
NSCursorInfo::Type type = aParam.GetType();
|
||||
|
||||
aMsg->WriteInt(type);
|
||||
|
||||
nsPoint hotSpot = aParam.GetHotSpot();
|
||||
WriteParam(aMsg, hotSpot.x);
|
||||
WriteParam(aMsg, hotSpot.y);
|
||||
|
||||
uint32_t dataLength = aParam.GetCustomImageDataLength();
|
||||
WriteParam(aMsg, dataLength);
|
||||
if (dataLength == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t* buffer = (uint8_t*)moz_xmalloc(dataLength);
|
||||
memcpy(buffer, aParam.GetCustomImageData(), dataLength);
|
||||
aMsg->WriteBytes(buffer, dataLength);
|
||||
free(buffer);
|
||||
}
|
||||
|
||||
static bool Read(const Message* aMsg, void** aIter, paramType* aResult)
|
||||
{
|
||||
NSCursorInfo::Type type;
|
||||
if (!aMsg->ReadInt(aIter, (int*)&type)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nscoord hotSpotX, hotSpotY;
|
||||
if (!ReadParam(aMsg, aIter, &hotSpotX) ||
|
||||
!ReadParam(aMsg, aIter, &hotSpotY)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t dataLength;
|
||||
if (!ReadParam(aMsg, aIter, &dataLength)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t* data = NULL;
|
||||
if (dataLength != 0) {
|
||||
if (!aMsg->ReadBytes(aIter, (const char**)&data, dataLength) || !data) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
aResult->SetType(type);
|
||||
aResult->SetHotSpot(nsPoint(hotSpotX, hotSpotY));
|
||||
aResult->SetCustomImageData(data, dataLength);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void Log(const paramType& aParam, std::wstring* aLog)
|
||||
{
|
||||
const char* typeName = aParam.GetTypeName();
|
||||
nsPoint hotSpot = aParam.GetHotSpot();
|
||||
int hotSpotX, hotSpotY;
|
||||
#ifdef NS_COORD_IS_FLOAT
|
||||
hotSpotX = rint(hotSpot.x);
|
||||
hotSpotY = rint(hotSpot.y);
|
||||
#else
|
||||
hotSpotX = hotSpot.x;
|
||||
hotSpotY = hotSpot.y;
|
||||
#endif
|
||||
uint32_t dataLength = aParam.GetCustomImageDataLength();
|
||||
uint8_t* data = aParam.GetCustomImageData();
|
||||
|
||||
aLog->append(StringPrintf(L"[%s, (%i %i), %u, %p]",
|
||||
typeName, hotSpotX, hotSpotY, dataLength, data));
|
||||
}
|
||||
};
|
||||
#else
|
||||
template<>
|
||||
struct ParamTraits<NSCursorInfo>
|
||||
{
|
||||
typedef NSCursorInfo paramType;
|
||||
static void Write(Message* aMsg, const paramType& aParam) {
|
||||
NS_RUNTIMEABORT("NSCursorInfo isn't meaningful on this platform");
|
||||
}
|
||||
static bool Read(const Message* aMsg, void** aIter, paramType* aResult) {
|
||||
NS_RUNTIMEABORT("NSCursorInfo isn't meaningful on this platform");
|
||||
return false;
|
||||
}
|
||||
};
|
||||
#endif // #ifdef XP_MACOSX
|
||||
|
||||
template <>
|
||||
struct ParamTraits<NPVariant>
|
||||
{
|
||||
|
|
|
@ -55,6 +55,10 @@
|
|||
#include "nsTHashtable.h"
|
||||
#include "nsHashKeys.h"
|
||||
|
||||
#ifdef OS_MACOSX
|
||||
#include "PluginInterposeOSX.h"
|
||||
#endif
|
||||
|
||||
#include "mozilla/plugins/PPluginModuleChild.h"
|
||||
#include "mozilla/plugins/PluginInstanceChild.h"
|
||||
#include "mozilla/plugins/PluginIdentifierChild.h"
|
||||
|
@ -224,6 +228,28 @@ public:
|
|||
void PluginHideWindow(uint32_t window_id) {
|
||||
SendPluginHideWindow(window_id);
|
||||
}
|
||||
|
||||
void SetCursor(NSCursorInfo& cursorInfo) {
|
||||
SendSetCursor(cursorInfo);
|
||||
}
|
||||
|
||||
void ShowCursor(bool show) {
|
||||
SendShowCursor(show);
|
||||
}
|
||||
|
||||
void PushCursor(NSCursorInfo& cursorInfo) {
|
||||
SendPushCursor(cursorInfo);
|
||||
}
|
||||
|
||||
void PopCursor() {
|
||||
SendPopCursor();
|
||||
}
|
||||
|
||||
bool GetNativeCursorsSupported() {
|
||||
bool supported = false;
|
||||
SendGetNativeCursorsSupported(&supported);
|
||||
return supported;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Quirks mode support for various plugin mime types
|
||||
|
|
|
@ -41,6 +41,8 @@
|
|||
#elif XP_MACOSX
|
||||
#include "PluginUtilsOSX.h"
|
||||
#include "PluginInterposeOSX.h"
|
||||
#include "nsIPrefService.h"
|
||||
#include "nsIPrefBranch.h"
|
||||
#endif
|
||||
#ifdef MOZ_WIDGET_QT
|
||||
#include <QtCore/QCoreApplication>
|
||||
|
@ -1048,6 +1050,84 @@ PluginModuleParent::RecvPluginHideWindow(const uint32_t& aWindowId)
|
|||
#endif
|
||||
}
|
||||
|
||||
bool
|
||||
PluginModuleParent::RecvSetCursor(const NSCursorInfo& aCursorInfo)
|
||||
{
|
||||
PLUGIN_LOG_DEBUG(("%s", FULLFUNCTION));
|
||||
#if defined(XP_MACOSX)
|
||||
mac_plugin_interposing::parent::OnSetCursor(aCursorInfo);
|
||||
return true;
|
||||
#else
|
||||
NS_NOTREACHED(
|
||||
"PluginInstanceParent::RecvSetCursor not implemented!");
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool
|
||||
PluginModuleParent::RecvShowCursor(const bool& aShow)
|
||||
{
|
||||
PLUGIN_LOG_DEBUG(("%s", FULLFUNCTION));
|
||||
#if defined(XP_MACOSX)
|
||||
mac_plugin_interposing::parent::OnShowCursor(aShow);
|
||||
return true;
|
||||
#else
|
||||
NS_NOTREACHED(
|
||||
"PluginInstanceParent::RecvShowCursor not implemented!");
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool
|
||||
PluginModuleParent::RecvPushCursor(const NSCursorInfo& aCursorInfo)
|
||||
{
|
||||
PLUGIN_LOG_DEBUG(("%s", FULLFUNCTION));
|
||||
#if defined(XP_MACOSX)
|
||||
mac_plugin_interposing::parent::OnPushCursor(aCursorInfo);
|
||||
return true;
|
||||
#else
|
||||
NS_NOTREACHED(
|
||||
"PluginInstanceParent::RecvPushCursor not implemented!");
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool
|
||||
PluginModuleParent::RecvPopCursor()
|
||||
{
|
||||
PLUGIN_LOG_DEBUG(("%s", FULLFUNCTION));
|
||||
#if defined(XP_MACOSX)
|
||||
mac_plugin_interposing::parent::OnPopCursor();
|
||||
return true;
|
||||
#else
|
||||
NS_NOTREACHED(
|
||||
"PluginInstanceParent::RecvPopCursor not implemented!");
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool
|
||||
PluginModuleParent::RecvGetNativeCursorsSupported(bool* supported)
|
||||
{
|
||||
PLUGIN_LOG_DEBUG(("%s", FULLFUNCTION));
|
||||
#if defined(XP_MACOSX)
|
||||
PRBool nativeCursorsSupported = PR_FALSE;
|
||||
nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
|
||||
if (prefs) {
|
||||
if (NS_FAILED(prefs->GetBoolPref("dom.ipc.plugins.nativeCursorSupport",
|
||||
&nativeCursorsSupported))) {
|
||||
nativeCursorsSupported = PR_FALSE;
|
||||
}
|
||||
}
|
||||
*supported = nativeCursorsSupported;
|
||||
return true;
|
||||
#else
|
||||
NS_NOTREACHED(
|
||||
"PluginInstanceParent::RecvGetNativeCursorSupportLevel not implemented!");
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef OS_MACOSX
|
||||
#define DEFAULT_REFRESH_MS 20 // CoreAnimation: 50 FPS
|
||||
|
||||
|
|
|
@ -188,6 +188,21 @@ protected:
|
|||
NS_OVERRIDE virtual bool
|
||||
RecvPluginHideWindow(const uint32_t& aWindowId);
|
||||
|
||||
NS_OVERRIDE virtual bool
|
||||
RecvSetCursor(const NSCursorInfo& aCursorInfo);
|
||||
|
||||
NS_OVERRIDE virtual bool
|
||||
RecvShowCursor(const bool& aShow);
|
||||
|
||||
NS_OVERRIDE virtual bool
|
||||
RecvPushCursor(const NSCursorInfo& aCursorInfo);
|
||||
|
||||
NS_OVERRIDE virtual bool
|
||||
RecvPopCursor();
|
||||
|
||||
NS_OVERRIDE virtual bool
|
||||
RecvGetNativeCursorsSupported(bool* supported);
|
||||
|
||||
static PluginInstanceParent* InstCast(NPP instance);
|
||||
static BrowserStreamParent* StreamCast(NPP instance, NPStream* s);
|
||||
|
||||
|
|
|
@ -73,6 +73,42 @@ namespace plugins {
|
|||
bool
|
||||
PluginProcessChild::Init()
|
||||
{
|
||||
#if defined(XP_MACOSX)
|
||||
// Remove the trigger for "dyld interposing" that we added in
|
||||
// GeckoChildProcessHost::PerformAsyncLaunchInternal(), in the host
|
||||
// process just before we were launched. Dyld interposing will still
|
||||
// happen in our process (the plugin child process). But we don't want
|
||||
// it to happen in any processes that the plugin might launch from our
|
||||
// process.
|
||||
nsCString interpose(PR_GetEnv("DYLD_INSERT_LIBRARIES"));
|
||||
if (!interpose.IsEmpty()) {
|
||||
// If we added the path to libplugin_child_interpose.dylib to an
|
||||
// existing DYLD_INSERT_LIBRARIES, we appended it to the end, after a
|
||||
// ":" path seperator.
|
||||
PRInt32 lastSeparatorPos = interpose.RFind(":");
|
||||
PRInt32 lastTriggerPos = interpose.RFind("libplugin_child_interpose.dylib");
|
||||
PRBool needsReset = PR_FALSE;
|
||||
if (lastTriggerPos != -1) {
|
||||
if (lastSeparatorPos == -1) {
|
||||
interpose.Truncate();
|
||||
needsReset = PR_TRUE;
|
||||
} else if (lastTriggerPos > lastSeparatorPos) {
|
||||
interpose.SetLength(lastSeparatorPos);
|
||||
needsReset = PR_TRUE;
|
||||
}
|
||||
}
|
||||
if (needsReset) {
|
||||
nsCString setInterpose("DYLD_INSERT_LIBRARIES=");
|
||||
if (!interpose.IsEmpty()) {
|
||||
setInterpose.Append(interpose);
|
||||
}
|
||||
// Values passed to PR_SetEnv() must be seperately allocated.
|
||||
char* setInterposePtr = strdup(PromiseFlatCString(setInterpose).get());
|
||||
PR_SetEnv(setInterposePtr);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef XP_WIN
|
||||
// Drag-and-drop needs OleInitialize to be called, and Silverlight depends
|
||||
// on the host calling CoInitialize (which is called by OleInitialize).
|
||||
|
|
|
@ -72,6 +72,14 @@ NPError mozilla::plugins::PluginUtilsOSX::ShowCocoaContextMenu(void* aMenu, int
|
|||
{
|
||||
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
|
||||
|
||||
// Set the native cursor to the OS default (an arrow) before displaying the
|
||||
// context menu. Otherwise (if the plugin has changed the cursor) it may
|
||||
// stay as the plugin has set it -- which means it may be invisible. We
|
||||
// need to do this because we display the context menu without making the
|
||||
// plugin process the foreground process. If we did, the cursor would
|
||||
// change to an arrow cursor automatically -- as it does in Chrome.
|
||||
[[NSCursor arrowCursor] set];
|
||||
|
||||
// Create a timer to process browser events while waiting
|
||||
// on the menu. This prevents the browser from hanging
|
||||
// during the lifetime of the menu.
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public License Version
|
||||
# 1.1 (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
# http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS IS" basis,
|
||||
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
# for the specific language governing rights and limitations under the
|
||||
# License.
|
||||
#
|
||||
# The Original Code is Mozilla core build scripts.
|
||||
#
|
||||
# The Initial Developer of the Original Code is
|
||||
# The Mozilla Foundation
|
||||
#
|
||||
# Portions created by the Initial Developer are Copyright (C) 2011
|
||||
# the Mozilla Foundation <http://www.mozilla.org/>. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s):
|
||||
# Steven Michaud <smichaud@pobox.com>
|
||||
#
|
||||
# Alternatively, the contents of this file may be used under the terms of
|
||||
# either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
# in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
# of those above. If you wish to allow use of your version of this file only
|
||||
# under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
# use your version of this file under the terms of the MPL, indicate your
|
||||
# decision by deleting the provisions above and replace them with the notice
|
||||
# and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
# the provisions above, a recipient may use your version of this file under
|
||||
# the terms of any one of the MPL, the GPL or the LGPL.
|
||||
#
|
||||
# ***** END LICENSE BLOCK *****
|
||||
|
||||
DEPTH = ../../../..
|
||||
topsrcdir = @top_srcdir@
|
||||
srcdir = @srcdir@
|
||||
VPATH = @srcdir@
|
||||
|
||||
include $(DEPTH)/config/autoconf.mk
|
||||
|
||||
LIBRARY_NAME = plugin_child_interpose
|
||||
FORCE_SHARED_LIB = 1
|
||||
DIST_INSTALL = 1
|
||||
|
||||
CMMSRCS = $(LIBRARY_NAME).mm
|
||||
|
||||
EXTRA_DSO_LDOPTS += \
|
||||
-framework Carbon \
|
||||
$(NULL)
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
|
@ -0,0 +1,150 @@
|
|||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is mozilla.org code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Mozilla Corporation
|
||||
*
|
||||
* Portions created by the Initial Developer are Copyright (C) 2011
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Steven Michaud <smichaud@pobox.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either of the GNU General Public License Version 2 or later (the "GPL"),
|
||||
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
// Use "dyld interposing" to hook methods imported from other libraries in the
|
||||
// plugin child process. The basic technique is described at
|
||||
// http://books.google.com/books?id=K8vUkpOXhN4C&pg=PA73&lpg=PA73&dq=__interpose&source=bl&ots=OJnnXZYpZC&sig=o7I3lXvoduUi13SrPfOON7o3do4&hl=en&ei=AoehS9brCYGQNrvsmeUM&sa=X&oi=book_result&ct=result&resnum=6&ved=0CBsQ6AEwBQ#v=onepage&q=__interpose&f=false.
|
||||
// The idea of doing it for the plugin child process comes from Chromium code,
|
||||
// particularly from plugin_carbon_interpose_mac.cc
|
||||
// (http://codesearch.google.com/codesearch/p?hl=en#OAMlx_jo-ck/src/chrome/browser/plugin_carbon_interpose_mac.cc&q=nscursor&exact_package=chromium&d=1&l=168)
|
||||
// and from PluginProcessHost::Init() in plugin_process_host.cc
|
||||
// (http://codesearch.google.com/codesearch/p?hl=en#OAMlx_jo-ck/src/content/browser/plugin_process_host.cc&q=nscursor&exact_package=chromium&d=1&l=222).
|
||||
|
||||
// These hooks are needed to make certain OS calls work from the child process
|
||||
// (a background process) that would normally only work when called in the
|
||||
// parent process (the foreground process). They allow us to serialize
|
||||
// information from the child process to the parent process, so that the same
|
||||
// (or equivalent) calls can be made from the parent process.
|
||||
|
||||
// This file lives in a seperate module (libplugin_child_interpose.dylib),
|
||||
// which will get loaded by the OS before any other modules when the plugin
|
||||
// child process is launched (from GeckoChildProcessHost::
|
||||
// PerformAsyncLaunchInternal()). For this reason it shouldn't link in other
|
||||
// browser modules when loaded. Instead it should use dlsym() to load
|
||||
// pointers to the methods it wants to call in other modules.
|
||||
|
||||
#if !defined(__LP64__)
|
||||
|
||||
#include <dlfcn.h>
|
||||
#import <Carbon/Carbon.h>
|
||||
|
||||
BOOL (*OnSetThemeCursorPtr) (ThemeCursor) = NULL;
|
||||
BOOL (*OnSetCursorPtr) (const Cursor*) = NULL;
|
||||
BOOL (*OnHideCursorPtr) () = NULL;
|
||||
BOOL (*OnShowCursorPtr) () = NULL;
|
||||
|
||||
static BOOL loadXULPtrs()
|
||||
{
|
||||
if (!OnSetThemeCursorPtr) {
|
||||
// mac_plugin_interposing_child_OnSetThemeCursor(ThemeCursor cursor) is in
|
||||
// PluginInterposeOSX.mm
|
||||
OnSetThemeCursorPtr = (BOOL(*)(ThemeCursor))
|
||||
dlsym(RTLD_DEFAULT, "mac_plugin_interposing_child_OnSetThemeCursor");
|
||||
}
|
||||
if (!OnSetCursorPtr) {
|
||||
// mac_plugin_interposing_child_OnSetCursor(const Cursor* cursor) is in
|
||||
// PluginInterposeOSX.mm
|
||||
OnSetCursorPtr = (BOOL(*)(const Cursor*))
|
||||
dlsym(RTLD_DEFAULT, "mac_plugin_interposing_child_OnSetCursor");
|
||||
}
|
||||
if (!OnHideCursorPtr) {
|
||||
// mac_plugin_interposing_child_OnHideCursor() is in PluginInterposeOSX.mm
|
||||
OnHideCursorPtr = (BOOL(*)())
|
||||
dlsym(RTLD_DEFAULT, "mac_plugin_interposing_child_OnHideCursor");
|
||||
}
|
||||
if (!OnShowCursorPtr) {
|
||||
// mac_plugin_interposing_child_OnShowCursor() is in PluginInterposeOSX.mm
|
||||
OnShowCursorPtr = (BOOL(*)())
|
||||
dlsym(RTLD_DEFAULT, "mac_plugin_interposing_child_OnShowCursor");
|
||||
}
|
||||
return (OnSetCursorPtr && OnSetThemeCursorPtr && OnHideCursorPtr && OnShowCursorPtr);
|
||||
}
|
||||
|
||||
static OSStatus MacPluginChildSetThemeCursor(ThemeCursor cursor)
|
||||
{
|
||||
if (loadXULPtrs()) {
|
||||
OnSetThemeCursorPtr(cursor);
|
||||
}
|
||||
return ::SetThemeCursor(cursor);
|
||||
}
|
||||
|
||||
static void MacPluginChildSetCursor(const Cursor* cursor)
|
||||
{
|
||||
if (loadXULPtrs()) {
|
||||
OnSetCursorPtr(cursor);
|
||||
}
|
||||
::SetCursor(cursor);
|
||||
}
|
||||
|
||||
static CGError MacPluginChildCGDisplayHideCursor(CGDirectDisplayID display)
|
||||
{
|
||||
if (loadXULPtrs()) {
|
||||
OnHideCursorPtr();
|
||||
}
|
||||
return ::CGDisplayHideCursor(display);
|
||||
}
|
||||
|
||||
static CGError MacPluginChildCGDisplayShowCursor(CGDirectDisplayID display)
|
||||
{
|
||||
if (loadXULPtrs()) {
|
||||
OnShowCursorPtr();
|
||||
}
|
||||
return ::CGDisplayShowCursor(display);
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
||||
struct interpose_substitution {
|
||||
const void* replacement;
|
||||
const void* original;
|
||||
};
|
||||
|
||||
#define INTERPOSE_FUNCTION(function) \
|
||||
{ reinterpret_cast<const void*>(MacPluginChild##function), \
|
||||
reinterpret_cast<const void*>(function) }
|
||||
|
||||
__attribute__((used)) static const interpose_substitution substitutions[]
|
||||
__attribute__((section("__DATA, __interpose"))) = {
|
||||
INTERPOSE_FUNCTION(SetThemeCursor),
|
||||
INTERPOSE_FUNCTION(SetCursor),
|
||||
INTERPOSE_FUNCTION(CGDisplayHideCursor),
|
||||
INTERPOSE_FUNCTION(CGDisplayShowCursor),
|
||||
};
|
||||
|
||||
#endif // !__LP64__
|
|
@ -434,6 +434,25 @@ GeckoChildProcessHost::PerformAsyncLaunchInternal(std::vector<std::string>& aExt
|
|||
newEnvVars["LD_LIBRARY_PATH"] = path.get();
|
||||
#elif OS_MACOSX
|
||||
newEnvVars["DYLD_LIBRARY_PATH"] = path.get();
|
||||
// XXX DYLD_INSERT_LIBRARIES should only be set when launching a plugin
|
||||
// process, and has no effect on other subprocesses (the hooks in
|
||||
// libplugin_child_interpose.dylib become noops). But currently it
|
||||
// gets set when launching any kind of subprocess.
|
||||
//
|
||||
// Trigger "dyld interposing" for the dylib that contains
|
||||
// plugin_child_interpose.mm. This allows us to hook OS calls in the
|
||||
// plugin process (ones that don't work correctly in a background
|
||||
// process). Don't break any other "dyld interposing" that has already
|
||||
// been set up by whatever may have launched the browser.
|
||||
const char* prevInterpose = PR_GetEnv("DYLD_INSERT_LIBRARIES");
|
||||
nsCString interpose;
|
||||
if (prevInterpose) {
|
||||
interpose.Assign(prevInterpose);
|
||||
interpose.AppendLiteral(":");
|
||||
}
|
||||
interpose.Append(path.get());
|
||||
interpose.AppendLiteral("/libplugin_child_interpose.dylib");
|
||||
newEnvVars["DYLD_INSERT_LIBRARIES"] = interpose.get();
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
|
Загрузка…
Ссылка в новой задаче