Bug 621117 - Support native cursor manipulation from OOP plugins on OS X. r=josh,bsmedberg a=sheriff

This commit is contained in:
Steven Michaud 2011-05-24 01:58:28 -05:00
Родитель cc7b927b54
Коммит cf976ee5ab
15 изменённых файлов: 1459 добавлений и 1 удалений

Просмотреть файл

@ -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