dom/plugins/ipc/PluginInterposeOSX.mm

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 // vim:set ts=2 sts=2 sw=2 et cin:
     3 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
     4 //
     5 // Redistribution and use in source and binary forms, with or without
     6 // modification, are permitted provided that the following conditions are
     7 // met:
     8 //
     9 //    * Redistributions of source code must retain the above copyright
    10 // notice, this list of conditions and the following disclaimer.
    11 //    * Redistributions in binary form must reproduce the above
    12 // copyright notice, this list of conditions and the following disclaimer
    13 // in the documentation and/or other materials provided with the
    14 // distribution.
    15 //    * Neither the name of Google Inc. nor the names of its
    16 // contributors may be used to endorse or promote products derived from
    17 // this software without specific prior written permission.
    18 //
    19 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
    20 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
    21 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
    22 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
    23 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
    24 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
    25 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
    26 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
    27 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    28 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
    29 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    31 #include "base/basictypes.h"
    32 #include "nsCocoaUtils.h"
    33 #include "PluginModuleChild.h"
    34 #include "nsDebug.h"
    35 #include "PluginInterposeOSX.h"
    36 #include <set>
    37 #import <AppKit/AppKit.h>
    38 #import <objc/runtime.h>
    39 #import <Carbon/Carbon.h>
    41 using mozilla::plugins::PluginModuleChild;
    42 using mozilla::plugins::AssertPluginThread;
    44 namespace mac_plugin_interposing {
    46 int32_t NSCursorInfo::mNativeCursorsSupported = -1;
    48 // This constructor may be called from the browser process or the plugin
    49 // process.
    50 NSCursorInfo::NSCursorInfo()
    51   : mType(TypeArrow)
    52   , mHotSpot(nsPoint(0, 0))
    53   , mCustomImageData(NULL)
    54   , mCustomImageDataLength(0)
    55 {
    56 }
    58 NSCursorInfo::NSCursorInfo(NSCursor* aCursor)
    59   : mType(TypeArrow)
    60   , mHotSpot(nsPoint(0, 0))
    61   , mCustomImageData(NULL)
    62   , mCustomImageDataLength(0)
    63 {
    64   // This constructor is only ever called from the plugin process, so the
    65   // following is safe.
    66   if (!GetNativeCursorsSupported()) {
    67     return;
    68   }
    70   NSPoint hotSpotCocoa = [aCursor hotSpot];
    71   mHotSpot = nsPoint(hotSpotCocoa.x, hotSpotCocoa.y);
    73   Class nsCursorClass = [NSCursor class];
    74   if ([aCursor isEqual:[NSCursor arrowCursor]]) {
    75     mType = TypeArrow;
    76   } else if ([aCursor isEqual:[NSCursor closedHandCursor]]) {
    77     mType = TypeClosedHand;
    78   } else if ([aCursor isEqual:[NSCursor crosshairCursor]]) {
    79     mType = TypeCrosshair;
    80   } else if ([aCursor isEqual:[NSCursor disappearingItemCursor]]) {
    81     mType = TypeDisappearingItem;
    82   } else if ([aCursor isEqual:[NSCursor IBeamCursor]]) {
    83     mType = TypeIBeam;
    84   } else if ([aCursor isEqual:[NSCursor openHandCursor]]) {
    85     mType = TypeOpenHand;
    86   } else if ([aCursor isEqual:[NSCursor pointingHandCursor]]) {
    87     mType = TypePointingHand;
    88   } else if ([aCursor isEqual:[NSCursor resizeDownCursor]]) {
    89     mType = TypeResizeDown;
    90   } else if ([aCursor isEqual:[NSCursor resizeLeftCursor]]) {
    91     mType = TypeResizeLeft;
    92   } else if ([aCursor isEqual:[NSCursor resizeLeftRightCursor]]) {
    93     mType = TypeResizeLeftRight;
    94   } else if ([aCursor isEqual:[NSCursor resizeRightCursor]]) {
    95     mType = TypeResizeRight;
    96   } else if ([aCursor isEqual:[NSCursor resizeUpCursor]]) {
    97     mType = TypeResizeUp;
    98   } else if ([aCursor isEqual:[NSCursor resizeUpDownCursor]]) {
    99     mType = TypeResizeUpDown;
   100   // The following cursor types are only supported on OS X 10.6 and up.
   101   } else if ([nsCursorClass respondsToSelector:@selector(contextualMenuCursor)] &&
   102              [aCursor isEqual:[nsCursorClass performSelector:@selector(contextualMenuCursor)]]) {
   103     mType = TypeContextualMenu;
   104   } else if ([nsCursorClass respondsToSelector:@selector(dragCopyCursor)] &&
   105              [aCursor isEqual:[nsCursorClass performSelector:@selector(dragCopyCursor)]]) {
   106     mType = TypeDragCopy;
   107   } else if ([nsCursorClass respondsToSelector:@selector(dragLinkCursor)] &&
   108              [aCursor isEqual:[nsCursorClass performSelector:@selector(dragLinkCursor)]]) {
   109     mType = TypeDragLink;
   110   } else if ([nsCursorClass respondsToSelector:@selector(operationNotAllowedCursor)] &&
   111              [aCursor isEqual:[nsCursorClass performSelector:@selector(operationNotAllowedCursor)]]) {
   112     mType = TypeNotAllowed;
   113   } else {
   114     NSImage* image = [aCursor image];
   115     NSArray* reps = image ? [image representations] : nil;
   116     NSUInteger repsCount = reps ? [reps count] : 0;
   117     if (!repsCount) {
   118       // If we have a custom cursor with no image representations, assume we
   119       // need a transparent cursor.
   120       mType = TypeTransparent;
   121     } else {
   122       CGImageRef cgImage = nil;
   123       // XXX We don't know how to deal with a cursor that doesn't have a
   124       //     bitmap image representation.  For now we fall back to an arrow
   125       //     cursor.
   126       for (NSUInteger i = 0; i < repsCount; ++i) {
   127         id rep = [reps objectAtIndex:i];
   128         if ([rep isKindOfClass:[NSBitmapImageRep class]]) {
   129           cgImage = [(NSBitmapImageRep*)rep CGImage];
   130           break;
   131         }
   132       }
   133       if (cgImage) {
   134         CFMutableDataRef data = ::CFDataCreateMutable(kCFAllocatorDefault, 0);
   135         if (data) {
   136           CGImageDestinationRef dest = ::CGImageDestinationCreateWithData(data,
   137                                                                           kUTTypePNG,
   138                                                                           1,
   139                                                                           NULL);
   140           if (dest) {
   141             ::CGImageDestinationAddImage(dest, cgImage, NULL);
   142             if (::CGImageDestinationFinalize(dest)) {
   143               uint32_t dataLength = (uint32_t) ::CFDataGetLength(data);
   144               mCustomImageData = (uint8_t*) moz_xmalloc(dataLength);
   145               ::CFDataGetBytes(data, ::CFRangeMake(0, dataLength), mCustomImageData);
   146               mCustomImageDataLength = dataLength;
   147               mType = TypeCustom;
   148             }
   149             ::CFRelease(dest);
   150           }
   151           ::CFRelease(data);
   152         }
   153       }
   154       if (!mCustomImageData) {
   155         mType = TypeArrow;
   156       }
   157     }
   158   }
   159 }
   161 NSCursorInfo::NSCursorInfo(const Cursor* aCursor)
   162   : mType(TypeArrow)
   163   , mHotSpot(nsPoint(0, 0))
   164   , mCustomImageData(NULL)
   165   , mCustomImageDataLength(0)
   166 {
   167   // This constructor is only ever called from the plugin process, so the
   168   // following is safe.
   169   if (!GetNativeCursorsSupported()) {
   170     return;
   171   }
   173   mHotSpot = nsPoint(aCursor->hotSpot.h, aCursor->hotSpot.v);
   175   int width = 16, height = 16;
   176   int bytesPerPixel = 4;
   177   int rowBytes = width * bytesPerPixel;
   178   int bitmapSize = height * rowBytes;
   180   bool isTransparent = true;
   182   uint8_t* bitmap = (uint8_t*) moz_xmalloc(bitmapSize);
   183   // The way we create 'bitmap' is largely "borrowed" from Chrome's
   184   // WebCursor::InitFromCursor().
   185   for (int y = 0; y < height; ++y) {
   186     unsigned short data = aCursor->data[y];
   187     unsigned short mask = aCursor->mask[y];
   188     // Change 'data' and 'mask' from big-endian to little-endian, but output
   189     // big-endian data below.
   190     data = ((data << 8) & 0xFF00) | ((data >> 8) & 0x00FF);
   191     mask = ((mask << 8) & 0xFF00) | ((mask >> 8) & 0x00FF);
   192     // It'd be nice to use a gray-scale bitmap.  But
   193     // CGBitmapContextCreateImage() (used below) won't work with one that also
   194     // has alpha values.
   195     for (int x = 0; x < width; ++x) {
   196       int offset = (y * rowBytes) + (x * bytesPerPixel);
   197       // Color value
   198       if (data & 0x8000) {
   199         bitmap[offset]     = 0x0;
   200         bitmap[offset + 1] = 0x0;
   201         bitmap[offset + 2] = 0x0;
   202       } else {
   203         bitmap[offset]     = 0xFF;
   204         bitmap[offset + 1] = 0xFF;
   205         bitmap[offset + 2] = 0xFF;
   206       }
   207       // Mask value
   208       if (mask & 0x8000) {
   209         bitmap[offset + 3] = 0xFF;
   210         isTransparent = false;
   211       } else {
   212         bitmap[offset + 3] = 0x0;
   213       }
   214       data <<= 1;
   215       mask <<= 1;
   216     }
   217   }
   219   if (isTransparent) {
   220     // If aCursor is transparent, we don't need to serialize custom cursor
   221     // data over IPC.
   222     mType = TypeTransparent;
   223   } else {
   224     CGColorSpaceRef color = ::CGColorSpaceCreateDeviceRGB();
   225     if (color) {
   226       CGContextRef context =
   227         ::CGBitmapContextCreate(bitmap,
   228                                 width,
   229                                 height,
   230                                 8,
   231                                 rowBytes,
   232                                 color,
   233                                 kCGImageAlphaPremultipliedLast |
   234                                   kCGBitmapByteOrder32Big);
   235       if (context) {
   236         CGImageRef image = ::CGBitmapContextCreateImage(context);
   237         if (image) {
   238           ::CFMutableDataRef data = ::CFDataCreateMutable(kCFAllocatorDefault, 0);
   239           if (data) {
   240             CGImageDestinationRef dest =
   241               ::CGImageDestinationCreateWithData(data,
   242                                                  kUTTypePNG,
   243                                                  1,
   244                                                  NULL);
   245             if (dest) {
   246               ::CGImageDestinationAddImage(dest, image, NULL);
   247               if (::CGImageDestinationFinalize(dest)) {
   248                 uint32_t dataLength = (uint32_t) ::CFDataGetLength(data);
   249                 mCustomImageData = (uint8_t*) moz_xmalloc(dataLength);
   250                 ::CFDataGetBytes(data,
   251                                  ::CFRangeMake(0, dataLength),
   252                                  mCustomImageData);
   253                 mCustomImageDataLength = dataLength;
   254                 mType = TypeCustom;
   255               }
   256               ::CFRelease(dest);
   257             }
   258             ::CFRelease(data);
   259           }
   260           ::CGImageRelease(image);
   261         }
   262         ::CGContextRelease(context);
   263       }
   264       ::CGColorSpaceRelease(color);
   265     }
   266   }
   268   moz_free(bitmap);
   269 }
   271 NSCursorInfo::~NSCursorInfo()
   272 {
   273   if (mCustomImageData) {
   274     moz_free(mCustomImageData);
   275   }
   276 }
   278 NSCursor* NSCursorInfo::GetNSCursor() const
   279 {
   280   NSCursor* retval = nil;
   282   Class nsCursorClass = [NSCursor class];
   283   switch(mType) {
   284     case TypeArrow:
   285       retval = [NSCursor arrowCursor];
   286       break;
   287     case TypeClosedHand:
   288       retval = [NSCursor closedHandCursor];
   289       break;
   290     case TypeCrosshair:
   291       retval = [NSCursor crosshairCursor];
   292       break;
   293     case TypeDisappearingItem:
   294       retval = [NSCursor disappearingItemCursor];
   295       break;
   296     case TypeIBeam:
   297       retval = [NSCursor IBeamCursor];
   298       break;
   299     case TypeOpenHand:
   300       retval = [NSCursor openHandCursor];
   301       break;
   302     case TypePointingHand:
   303       retval = [NSCursor pointingHandCursor];
   304       break;
   305     case TypeResizeDown:
   306       retval = [NSCursor resizeDownCursor];
   307       break;
   308     case TypeResizeLeft:
   309       retval = [NSCursor resizeLeftCursor];
   310       break;
   311     case TypeResizeLeftRight:
   312       retval = [NSCursor resizeLeftRightCursor];
   313       break;
   314     case TypeResizeRight:
   315       retval = [NSCursor resizeRightCursor];
   316       break;
   317     case TypeResizeUp:
   318       retval = [NSCursor resizeUpCursor];
   319       break;
   320     case TypeResizeUpDown:
   321       retval = [NSCursor resizeUpDownCursor];
   322       break;
   323     // The following four cursor types are only supported on OS X 10.6 and up.
   324     case TypeContextualMenu: {
   325       if ([nsCursorClass respondsToSelector:@selector(contextualMenuCursor)]) {
   326         retval = [nsCursorClass performSelector:@selector(contextualMenuCursor)];
   327       }
   328       break;
   329     }
   330     case TypeDragCopy: {
   331       if ([nsCursorClass respondsToSelector:@selector(dragCopyCursor)]) {
   332         retval = [nsCursorClass performSelector:@selector(dragCopyCursor)];
   333       }
   334       break;
   335     }
   336     case TypeDragLink: {
   337       if ([nsCursorClass respondsToSelector:@selector(dragLinkCursor)]) {
   338         retval = [nsCursorClass performSelector:@selector(dragLinkCursor)];
   339       }
   340       break;
   341     }
   342     case TypeNotAllowed: {
   343       if ([nsCursorClass respondsToSelector:@selector(operationNotAllowedCursor)]) {
   344         retval = [nsCursorClass performSelector:@selector(operationNotAllowedCursor)];
   345       }
   346       break;
   347     }
   348     case TypeTransparent:
   349       retval = GetTransparentCursor();
   350       break;
   351     default:
   352       break;
   353   }
   355   if (!retval && mCustomImageData && mCustomImageDataLength) {
   356     CGDataProviderRef provider = ::CGDataProviderCreateWithData(NULL,
   357                                                                 (const void*)mCustomImageData,
   358                                                                 mCustomImageDataLength,
   359                                                                 NULL);
   360     if (provider) {
   361       CGImageRef cgImage = ::CGImageCreateWithPNGDataProvider(provider,
   362                                                               NULL,
   363                                                               false,
   364                                                               kCGRenderingIntentDefault);
   365       if (cgImage) {
   366         NSBitmapImageRep* rep = [[NSBitmapImageRep alloc] initWithCGImage:cgImage];
   367         if (rep) {
   368           NSImage* image = [[NSImage alloc] init];
   369           if (image) {
   370             [image addRepresentation:rep];
   371             retval = [[[NSCursor alloc] initWithImage:image 
   372                                               hotSpot:NSMakePoint(mHotSpot.x, mHotSpot.y)]
   373                       autorelease];
   374             [image release];
   375           }
   376           [rep release];
   377         }
   378         ::CGImageRelease(cgImage);
   379       }
   380       ::CFRelease(provider);
   381     }
   382   }
   384   // Fall back to an arrow cursor if need be.
   385   if (!retval) {
   386     retval = [NSCursor arrowCursor];
   387   }
   389   return retval;
   390 }
   392 // Get a transparent cursor with the appropriate hot spot.  We need one if
   393 // (for example) we have a custom cursor with no image data.
   394 NSCursor* NSCursorInfo::GetTransparentCursor() const
   395 {
   396   NSCursor* retval = nil;
   398   int width = 16, height = 16;
   399   int bytesPerPixel = 2;
   400   int rowBytes = width * bytesPerPixel;
   401   int dataSize = height * rowBytes;
   403   uint8_t* data = (uint8_t*) moz_xmalloc(dataSize);
   404   for (int y = 0; y < height; ++y) {
   405     for (int x = 0; x < width; ++x) {
   406       int offset = (y * rowBytes) + (x * bytesPerPixel);
   407       data[offset] = 0x7E;  // Arbitrary gray-scale value
   408       data[offset + 1] = 0; // Alpha value to make us transparent
   409     }
   410   }
   412   NSBitmapImageRep* imageRep =
   413     [[[NSBitmapImageRep alloc] initWithBitmapDataPlanes:nil
   414                                              pixelsWide:width
   415                                              pixelsHigh:height
   416                                           bitsPerSample:8
   417                                         samplesPerPixel:2
   418                                                hasAlpha:YES
   419                                                isPlanar:NO
   420                                          colorSpaceName:NSCalibratedWhiteColorSpace
   421                                             bytesPerRow:rowBytes
   422                                            bitsPerPixel:16]
   423      autorelease];
   424   if (imageRep) {
   425     uint8_t* repDataPtr = [imageRep bitmapData];
   426     if (repDataPtr) {
   427       memcpy(repDataPtr, data, dataSize);
   428       NSImage *image =
   429         [[[NSImage alloc] initWithSize:NSMakeSize(width, height)]
   430          autorelease];
   431       if (image) {
   432         [image addRepresentation:imageRep];
   433         retval =
   434           [[[NSCursor alloc] initWithImage:image
   435                                    hotSpot:NSMakePoint(mHotSpot.x, mHotSpot.y)]
   436            autorelease];
   437       }
   438     }
   439   }
   441   moz_free(data);
   443   // Fall back to an arrow cursor if (for some reason) the above code failed.
   444   if (!retval) {
   445     retval = [NSCursor arrowCursor];
   446   }
   448   return retval;
   449 }
   451 NSCursorInfo::Type NSCursorInfo::GetType() const
   452 {
   453   return mType;
   454 }
   456 const char* NSCursorInfo::GetTypeName() const
   457 {
   458   switch(mType) {
   459     case TypeCustom:
   460       return "TypeCustom";
   461     case TypeArrow:
   462       return "TypeArrow";
   463     case TypeClosedHand:
   464       return "TypeClosedHand";
   465     case TypeContextualMenu:
   466       return "TypeContextualMenu";
   467     case TypeCrosshair:
   468       return "TypeCrosshair";
   469     case TypeDisappearingItem:
   470       return "TypeDisappearingItem";
   471     case TypeDragCopy:
   472       return "TypeDragCopy";
   473     case TypeDragLink:
   474       return "TypeDragLink";
   475     case TypeIBeam:
   476       return "TypeIBeam";
   477     case TypeNotAllowed:
   478       return "TypeNotAllowed";
   479     case TypeOpenHand:
   480       return "TypeOpenHand";
   481     case TypePointingHand:
   482       return "TypePointingHand";
   483     case TypeResizeDown:
   484       return "TypeResizeDown";
   485     case TypeResizeLeft:
   486       return "TypeResizeLeft";
   487     case TypeResizeLeftRight:
   488       return "TypeResizeLeftRight";
   489     case TypeResizeRight:
   490       return "TypeResizeRight";
   491     case TypeResizeUp:
   492       return "TypeResizeUp";
   493     case TypeResizeUpDown:
   494       return "TypeResizeUpDown";
   495     case TypeTransparent:
   496       return "TypeTransparent";
   497     default:
   498       break;
   499   }
   500   return "TypeUnknown";
   501 }
   503 nsPoint NSCursorInfo::GetHotSpot() const
   504 {
   505   return mHotSpot;
   506 }
   508 uint8_t* NSCursorInfo::GetCustomImageData() const
   509 {
   510   return mCustomImageData;
   511 }
   513 uint32_t NSCursorInfo::GetCustomImageDataLength() const
   514 {
   515   return mCustomImageDataLength;
   516 }
   518 void NSCursorInfo::SetType(Type aType)
   519 {
   520   mType = aType;
   521 }
   523 void NSCursorInfo::SetHotSpot(nsPoint aHotSpot)
   524 {
   525   mHotSpot = aHotSpot;
   526 }
   528 void NSCursorInfo::SetCustomImageData(uint8_t* aData, uint32_t aDataLength)
   529 {
   530   if (mCustomImageData) {
   531     moz_free(mCustomImageData);
   532   }
   533   if (aDataLength) {
   534     mCustomImageData = (uint8_t*) moz_xmalloc(aDataLength);
   535     memcpy(mCustomImageData, aData, aDataLength);
   536   } else {
   537     mCustomImageData = NULL;
   538   }
   539   mCustomImageDataLength = aDataLength;
   540 }
   542 // This should never be called from the browser process -- only from the
   543 // plugin process.
   544 bool NSCursorInfo::GetNativeCursorsSupported()
   545 {
   546   if (mNativeCursorsSupported == -1) {
   547     AssertPluginThread();
   548     PluginModuleChild *pmc = PluginModuleChild::current();
   549     if (pmc) {
   550       bool result = pmc->GetNativeCursorsSupported();
   551       if (result) {
   552         mNativeCursorsSupported = 1;
   553       } else {
   554         mNativeCursorsSupported = 0;
   555       }
   556     }
   557   }
   558   return (mNativeCursorsSupported == 1);
   559 }
   561 } // namespace mac_plugin_interposing
   563 namespace mac_plugin_interposing {
   564 namespace parent {
   566 // Tracks plugin windows currently visible.
   567 std::set<uint32_t> plugin_visible_windows_set_;
   568 // Tracks full screen windows currently visible.
   569 std::set<uint32_t> plugin_fullscreen_windows_set_;
   570 // Tracks modal windows currently visible.
   571 std::set<uint32_t> plugin_modal_windows_set_;
   573 void OnPluginShowWindow(uint32_t window_id,
   574                         CGRect window_bounds,
   575                         bool modal) {
   576   plugin_visible_windows_set_.insert(window_id);
   578   if (modal)
   579     plugin_modal_windows_set_.insert(window_id);
   581   CGRect main_display_bounds = ::CGDisplayBounds(CGMainDisplayID());
   583   if (CGRectEqualToRect(window_bounds, main_display_bounds) &&
   584       (plugin_fullscreen_windows_set_.find(window_id) ==
   585        plugin_fullscreen_windows_set_.end())) {
   586     plugin_fullscreen_windows_set_.insert(window_id);
   588     nsCocoaUtils::HideOSChromeOnScreen(TRUE, [[NSScreen screens] objectAtIndex:0]);
   589   }
   590 }
   592 static void ActivateProcess(pid_t pid) {
   593   ProcessSerialNumber process;
   594   OSStatus status = ::GetProcessForPID(pid, &process);
   596   if (status == noErr) {
   597     SetFrontProcess(&process);
   598   } else {
   599     NS_WARNING("Unable to get process for pid.");
   600   }
   601 }
   603 // Must be called on the UI thread.
   604 // If plugin_pid is -1, the browser will be the active process on return,
   605 // otherwise that process will be given focus back before this function returns.
   606 static void ReleasePluginFullScreen(pid_t plugin_pid) {
   607   // Releasing full screen only works if we are the frontmost process; grab
   608   // focus, but give it back to the plugin process if requested.
   609   ActivateProcess(base::GetCurrentProcId());
   611   nsCocoaUtils::HideOSChromeOnScreen(FALSE, [[NSScreen screens] objectAtIndex:0]);
   613   if (plugin_pid != -1) {
   614     ActivateProcess(plugin_pid);
   615   }
   616 }
   618 void OnPluginHideWindow(uint32_t window_id, pid_t aPluginPid) {
   619   bool had_windows = !plugin_visible_windows_set_.empty();
   620   plugin_visible_windows_set_.erase(window_id);
   621   bool browser_needs_activation = had_windows &&
   622       plugin_visible_windows_set_.empty();
   624   plugin_modal_windows_set_.erase(window_id);
   625   if (plugin_fullscreen_windows_set_.find(window_id) !=
   626       plugin_fullscreen_windows_set_.end()) {
   627     plugin_fullscreen_windows_set_.erase(window_id);
   628     pid_t plugin_pid = browser_needs_activation ? -1 : aPluginPid;
   629     browser_needs_activation = false;
   630     ReleasePluginFullScreen(plugin_pid);
   631   }
   633   if (browser_needs_activation) {
   634     ActivateProcess(getpid());
   635   }
   636 }
   638 void OnSetCursor(const NSCursorInfo& cursorInfo)
   639 {
   640   NSCursor* aCursor = cursorInfo.GetNSCursor();
   641   if (aCursor) {
   642     [aCursor set];
   643   }
   644 }
   646 void OnShowCursor(bool show)
   647 {
   648   if (show) {
   649     [NSCursor unhide];
   650   } else {
   651     [NSCursor hide];
   652   }
   653 }
   655 void OnPushCursor(const NSCursorInfo& cursorInfo)
   656 {
   657   NSCursor* aCursor = cursorInfo.GetNSCursor();
   658   if (aCursor) {
   659     [aCursor push];
   660   }
   661 }
   663 void OnPopCursor()
   664 {
   665   [NSCursor pop];
   666 }
   668 } // parent
   669 } // namespace mac_plugin_interposing
   671 namespace mac_plugin_interposing {
   672 namespace child {
   674 // TODO(stuartmorgan): Make this an IPC to order the plugin process above the
   675 // browser process only if the browser is current frontmost.
   676 void FocusPluginProcess() {
   677   ProcessSerialNumber this_process, front_process;
   678   if ((GetCurrentProcess(&this_process) != noErr) ||
   679       (GetFrontProcess(&front_process) != noErr)) {
   680     return;
   681   }
   683   Boolean matched = false;
   684   if ((SameProcess(&this_process, &front_process, &matched) == noErr) &&
   685       !matched) {
   686     SetFrontProcess(&this_process);
   687   }
   688 }
   690 void NotifyBrowserOfPluginShowWindow(uint32_t window_id, CGRect bounds,
   691                                      bool modal) {
   692   AssertPluginThread();
   694   PluginModuleChild *pmc = PluginModuleChild::current();
   695   if (pmc)
   696     pmc->PluginShowWindow(window_id, modal, bounds);
   697 }
   699 void NotifyBrowserOfPluginHideWindow(uint32_t window_id, CGRect bounds) {
   700   AssertPluginThread();
   702   PluginModuleChild *pmc = PluginModuleChild::current();
   703   if (pmc)
   704     pmc->PluginHideWindow(window_id);
   705 }
   707 void NotifyBrowserOfSetCursor(NSCursorInfo& aCursorInfo)
   708 {
   709   AssertPluginThread();
   710   PluginModuleChild *pmc = PluginModuleChild::current();
   711   if (pmc) {
   712     pmc->SetCursor(aCursorInfo);
   713   }
   714 }
   716 void NotifyBrowserOfShowCursor(bool show)
   717 {
   718   AssertPluginThread();
   719   PluginModuleChild *pmc = PluginModuleChild::current();
   720   if (pmc) {
   721     pmc->ShowCursor(show);
   722   }
   723 }
   725 void NotifyBrowserOfPushCursor(NSCursorInfo& aCursorInfo)
   726 {
   727   AssertPluginThread();
   728   PluginModuleChild *pmc = PluginModuleChild::current();
   729   if (pmc) {
   730     pmc->PushCursor(aCursorInfo);
   731   }
   732 }
   734 void NotifyBrowserOfPopCursor()
   735 {
   736   AssertPluginThread();
   737   PluginModuleChild *pmc = PluginModuleChild::current();
   738   if (pmc) {
   739     pmc->PopCursor();
   740   }
   741 }
   743 struct WindowInfo {
   744   uint32_t window_id;
   745   CGRect bounds;
   746   WindowInfo(NSWindow* window) {
   747     NSInteger window_num = [window windowNumber];
   748     window_id = window_num > 0 ? window_num : 0;
   749     bounds = NSRectToCGRect([window frame]);
   750   }
   751 };
   753 static void OnPluginWindowClosed(const WindowInfo& window_info) {
   754   if (window_info.window_id == 0)
   755     return;
   756   mac_plugin_interposing::child::NotifyBrowserOfPluginHideWindow(window_info.window_id,
   757                                                                  window_info.bounds);
   758 }
   760 static void OnPluginWindowShown(const WindowInfo& window_info, BOOL is_modal) {
   761   // The window id is 0 if it has never been shown (including while it is the
   762   // process of being shown for the first time); when that happens, we'll catch
   763   // it in _setWindowNumber instead.
   764   static BOOL s_pending_display_is_modal = NO;
   765   if (window_info.window_id == 0) {
   766     if (is_modal)
   767       s_pending_display_is_modal = YES;
   768     return;
   769   }
   770   if (s_pending_display_is_modal) {
   771     is_modal = YES;
   772     s_pending_display_is_modal = NO;
   773   }
   774   mac_plugin_interposing::child::NotifyBrowserOfPluginShowWindow(
   775     window_info.window_id, window_info.bounds, is_modal);
   776 }
   778 static BOOL OnSetCursor(NSCursorInfo &aInfo)
   779 {
   780   if (NSCursorInfo::GetNativeCursorsSupported()) {
   781     NotifyBrowserOfSetCursor(aInfo);
   782     return YES;
   783   }
   784   return NO;
   785 }
   787 static BOOL OnHideCursor()
   788 {
   789   if (NSCursorInfo::GetNativeCursorsSupported()) {
   790     NotifyBrowserOfShowCursor(NO);
   791     return YES;
   792   }
   793   return NO;
   794 }
   796 static BOOL OnUnhideCursor()
   797 {
   798   if (NSCursorInfo::GetNativeCursorsSupported()) {
   799     NotifyBrowserOfShowCursor(YES);
   800     return YES;
   801   }
   802   return NO;
   803 }
   805 static BOOL OnPushCursor(NSCursorInfo &aInfo)
   806 {
   807   if (NSCursorInfo::GetNativeCursorsSupported()) {
   808     NotifyBrowserOfPushCursor(aInfo);
   809     return YES;
   810   }
   811   return NO;
   812 }
   814 static BOOL OnPopCursor()
   815 {
   816   if (NSCursorInfo::GetNativeCursorsSupported()) {
   817     NotifyBrowserOfPopCursor();
   818     return YES;
   819   }
   820   return NO;
   821 }
   823 } // child
   824 } // namespace mac_plugin_interposing
   826 using namespace mac_plugin_interposing::child;
   828 @interface NSWindow (PluginInterposing)
   829 - (void)pluginInterpose_orderOut:(id)sender;
   830 - (void)pluginInterpose_orderFront:(id)sender;
   831 - (void)pluginInterpose_makeKeyAndOrderFront:(id)sender;
   832 - (void)pluginInterpose_setWindowNumber:(NSInteger)num;
   833 @end
   835 @implementation NSWindow (PluginInterposing)
   837 - (void)pluginInterpose_orderOut:(id)sender {
   838   WindowInfo window_info(self);
   839   [self pluginInterpose_orderOut:sender];
   840   OnPluginWindowClosed(window_info);
   841 }
   843 - (void)pluginInterpose_orderFront:(id)sender {
   844   mac_plugin_interposing::child::FocusPluginProcess();
   845   [self pluginInterpose_orderFront:sender];
   846   OnPluginWindowShown(WindowInfo(self), NO);
   847 }
   849 - (void)pluginInterpose_makeKeyAndOrderFront:(id)sender {
   850   mac_plugin_interposing::child::FocusPluginProcess();
   851   [self pluginInterpose_makeKeyAndOrderFront:sender];
   852   OnPluginWindowShown(WindowInfo(self), NO);
   853 }
   855 - (void)pluginInterpose_setWindowNumber:(NSInteger)num {
   856   if (num > 0)
   857     mac_plugin_interposing::child::FocusPluginProcess();
   858   [self pluginInterpose_setWindowNumber:num];
   859   if (num > 0)
   860     OnPluginWindowShown(WindowInfo(self), NO);
   861 }
   863 @end
   865 @interface NSApplication (PluginInterposing)
   866 - (NSInteger)pluginInterpose_runModalForWindow:(NSWindow*)window;
   867 @end
   869 @implementation NSApplication (PluginInterposing)
   871 - (NSInteger)pluginInterpose_runModalForWindow:(NSWindow*)window {
   872   mac_plugin_interposing::child::FocusPluginProcess();
   873   // This is out-of-order relative to the other calls, but runModalForWindow:
   874   // won't return until the window closes, and the order only matters for
   875   // full-screen windows.
   876   OnPluginWindowShown(WindowInfo(window), YES);
   877   return [self pluginInterpose_runModalForWindow:window];
   878 }
   880 @end
   882 // Hook commands to manipulate the current cursor, so that they can be passed
   883 // from the child process to the parent process.  These commands have no
   884 // effect unless they're performed in the parent process.
   885 @interface NSCursor (PluginInterposing)
   886 - (void)pluginInterpose_set;
   887 - (void)pluginInterpose_push;
   888 - (void)pluginInterpose_pop;
   889 + (NSCursor*)pluginInterpose_currentCursor;
   890 + (void)pluginInterpose_hide;
   891 + (void)pluginInterpose_unhide;
   892 + (void)pluginInterpose_pop;
   893 @end
   895 // Cache the results of [NSCursor set], [NSCursor push] and [NSCursor pop].
   896 // The last element is always the current cursor.
   897 static NSMutableArray* gCursorStack = nil;
   899 static BOOL initCursorStack()
   900 {
   901   if (!gCursorStack) {
   902     gCursorStack = [[NSMutableArray arrayWithCapacity:5] retain];
   903   }
   904   return (gCursorStack != NULL);
   905 }
   907 static NSCursor* currentCursorFromCache()
   908 {
   909   if (!initCursorStack())
   910     return nil;
   911   return (NSCursor*) [gCursorStack lastObject];
   912 }
   914 static void setCursorInCache(NSCursor* aCursor)
   915 {
   916   if (!initCursorStack() || !aCursor)
   917     return;
   918   NSUInteger count = [gCursorStack count];
   919   if (count) {
   920     [gCursorStack replaceObjectAtIndex:count - 1 withObject:aCursor];
   921   } else {
   922     [gCursorStack addObject:aCursor];
   923   }
   924 }
   926 static void pushCursorInCache(NSCursor* aCursor)
   927 {
   928   if (!initCursorStack() || !aCursor)
   929     return;
   930   [gCursorStack addObject:aCursor];
   931 }
   933 static void popCursorInCache()
   934 {
   935   if (!initCursorStack())
   936     return;
   937   // Apple's doc on the +[NSCursor pop] method says:  "If the current cursor
   938   // is the only cursor on the stack, this method does nothing."
   939   if ([gCursorStack count] > 1) {
   940     [gCursorStack removeLastObject];
   941   }
   942 }
   944 @implementation NSCursor (PluginInterposing)
   946 - (void)pluginInterpose_set
   947 {
   948   NSCursorInfo info(self);
   949   OnSetCursor(info);
   950   setCursorInCache(self);
   951   [self pluginInterpose_set];
   952 }
   954 - (void)pluginInterpose_push
   955 {
   956   NSCursorInfo info(self);
   957   OnPushCursor(info);
   958   pushCursorInCache(self);
   959   [self pluginInterpose_push];
   960 }
   962 - (void)pluginInterpose_pop
   963 {
   964   OnPopCursor();
   965   popCursorInCache();
   966   [self pluginInterpose_pop];
   967 }
   969 // The currentCursor method always returns nil when running in a background
   970 // process.  But this may confuse plugins (notably Flash, see bug 621117).  So
   971 // if we get a nil return from the "call to super", we return a cursor that's
   972 // been cached by previous calls to set or push.  According to Apple's docs,
   973 // currentCursor "only returns the cursor set by your application using
   974 // NSCursor methods".  So we don't need to worry about changes to the cursor
   975 // made by other methods like SetThemeCursor().
   976 + (NSCursor*)pluginInterpose_currentCursor
   977 {
   978   NSCursor* retval = [self pluginInterpose_currentCursor];
   979   if (!retval) {
   980     retval = currentCursorFromCache();
   981   }
   982   return retval;
   983 }
   985 + (void)pluginInterpose_hide
   986 {
   987   OnHideCursor();
   988   [self pluginInterpose_hide];
   989 }
   991 + (void)pluginInterpose_unhide
   992 {
   993   OnUnhideCursor();
   994   [self pluginInterpose_unhide];
   995 }
   997 + (void)pluginInterpose_pop
   998 {
   999   OnPopCursor();
  1000   popCursorInCache();
  1001   [self pluginInterpose_pop];
  1004 @end
  1006 static void ExchangeMethods(Class target_class,
  1007                             BOOL class_method,
  1008                             SEL original,
  1009                             SEL replacement) {
  1010   Method m1;
  1011   Method m2;
  1012   if (class_method) {
  1013     m1 = class_getClassMethod(target_class, original);
  1014     m2 = class_getClassMethod(target_class, replacement);
  1015   } else {
  1016     m1 = class_getInstanceMethod(target_class, original);
  1017     m2 = class_getInstanceMethod(target_class, replacement);
  1020   if (m1 == m2)
  1021     return;
  1023   if (m1 && m2)
  1024     method_exchangeImplementations(m1, m2);
  1025   else
  1026     NS_NOTREACHED("Cocoa swizzling failed");
  1029 namespace mac_plugin_interposing {
  1030 namespace child {
  1032 void SetUpCocoaInterposing() {
  1033   Class nswindow_class = [NSWindow class];
  1034   ExchangeMethods(nswindow_class, NO, @selector(orderOut:),
  1035                   @selector(pluginInterpose_orderOut:));
  1036   ExchangeMethods(nswindow_class, NO, @selector(orderFront:),
  1037                   @selector(pluginInterpose_orderFront:));
  1038   ExchangeMethods(nswindow_class, NO, @selector(makeKeyAndOrderFront:),
  1039                   @selector(pluginInterpose_makeKeyAndOrderFront:));
  1040   ExchangeMethods(nswindow_class, NO, @selector(_setWindowNumber:),
  1041                   @selector(pluginInterpose_setWindowNumber:));
  1043   ExchangeMethods([NSApplication class], NO, @selector(runModalForWindow:),
  1044                   @selector(pluginInterpose_runModalForWindow:));
  1046   Class nscursor_class = [NSCursor class];
  1047   ExchangeMethods(nscursor_class, NO, @selector(set),
  1048                   @selector(pluginInterpose_set));
  1049   ExchangeMethods(nscursor_class, NO, @selector(push),
  1050                   @selector(pluginInterpose_push));
  1051   ExchangeMethods(nscursor_class, NO, @selector(pop),
  1052                   @selector(pluginInterpose_pop));
  1053   ExchangeMethods(nscursor_class, YES, @selector(currentCursor),
  1054                   @selector(pluginInterpose_currentCursor));
  1055   ExchangeMethods(nscursor_class, YES, @selector(hide),
  1056                   @selector(pluginInterpose_hide));
  1057   ExchangeMethods(nscursor_class, YES, @selector(unhide),
  1058                   @selector(pluginInterpose_unhide));
  1059   ExchangeMethods(nscursor_class, YES, @selector(pop),
  1060                   @selector(pluginInterpose_pop));
  1063 }  // namespace child
  1064 }  // namespace mac_plugin_interposing
  1066 // Called from plugin_child_interpose.mm, which hooks calls to
  1067 // SetCursor() (the QuickDraw call) from the plugin child process.
  1068 extern "C" NS_VISIBILITY_DEFAULT BOOL
  1069 mac_plugin_interposing_child_OnSetCursor(const Cursor* cursor)
  1071   NSCursorInfo info(cursor);
  1072   return OnSetCursor(info);
  1075 // Called from plugin_child_interpose.mm, which hooks calls to
  1076 // SetThemeCursor() (the Appearance Manager call) from the plugin child
  1077 // process.
  1078 extern "C" NS_VISIBILITY_DEFAULT BOOL
  1079 mac_plugin_interposing_child_OnSetThemeCursor(ThemeCursor cursor)
  1081   NSCursorInfo info;
  1082   switch (cursor) {
  1083     case kThemeArrowCursor:
  1084       info.SetType(NSCursorInfo::TypeArrow);
  1085       break;
  1086     case kThemeCopyArrowCursor:
  1087       info.SetType(NSCursorInfo::TypeDragCopy);
  1088       break;
  1089     case kThemeAliasArrowCursor:
  1090       info.SetType(NSCursorInfo::TypeDragLink);
  1091       break;
  1092     case kThemeContextualMenuArrowCursor:
  1093       info.SetType(NSCursorInfo::TypeContextualMenu);
  1094       break;
  1095     case kThemeIBeamCursor:
  1096       info.SetType(NSCursorInfo::TypeIBeam);
  1097       break;
  1098     case kThemeCrossCursor:
  1099     case kThemePlusCursor:
  1100       info.SetType(NSCursorInfo::TypeCrosshair);
  1101       break;
  1102     case kThemeWatchCursor:
  1103     case kThemeSpinningCursor:
  1104       info.SetType(NSCursorInfo::TypeArrow);
  1105       break;
  1106     case kThemeClosedHandCursor:
  1107       info.SetType(NSCursorInfo::TypeClosedHand);
  1108       break;
  1109     case kThemeOpenHandCursor:
  1110       info.SetType(NSCursorInfo::TypeOpenHand);
  1111       break;
  1112     case kThemePointingHandCursor:
  1113     case kThemeCountingUpHandCursor:
  1114     case kThemeCountingDownHandCursor:
  1115     case kThemeCountingUpAndDownHandCursor:
  1116       info.SetType(NSCursorInfo::TypePointingHand);
  1117       break;
  1118     case kThemeResizeLeftCursor:
  1119       info.SetType(NSCursorInfo::TypeResizeLeft);
  1120       break;
  1121     case kThemeResizeRightCursor:
  1122       info.SetType(NSCursorInfo::TypeResizeRight);
  1123       break;
  1124     case kThemeResizeLeftRightCursor:
  1125       info.SetType(NSCursorInfo::TypeResizeLeftRight);
  1126       break;
  1127     case kThemeNotAllowedCursor:
  1128       info.SetType(NSCursorInfo::TypeNotAllowed);
  1129       break;
  1130     case kThemeResizeUpCursor:
  1131       info.SetType(NSCursorInfo::TypeResizeUp);
  1132       break;
  1133     case kThemeResizeDownCursor:
  1134       info.SetType(NSCursorInfo::TypeResizeDown);
  1135       break;
  1136     case kThemeResizeUpDownCursor:
  1137       info.SetType(NSCursorInfo::TypeResizeUpDown);
  1138       break;
  1139     case kThemePoofCursor:
  1140       info.SetType(NSCursorInfo::TypeDisappearingItem);
  1141       break;
  1142     default:
  1143       info.SetType(NSCursorInfo::TypeArrow);
  1144       break;
  1146   return OnSetCursor(info);
  1149 extern "C" NS_VISIBILITY_DEFAULT BOOL
  1150 mac_plugin_interposing_child_OnHideCursor()
  1152   return OnHideCursor();
  1155 extern "C" NS_VISIBILITY_DEFAULT BOOL
  1156 mac_plugin_interposing_child_OnShowCursor()
  1158   return OnUnhideCursor();

mercurial