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