Home | History | Annotate | Download | only in plugin
      1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "content/plugin/plugin_interpose_util_mac.h"
      6 
      7 #import <AppKit/AppKit.h>
      8 #import <objc/runtime.h>
      9 
     10 #include "content/child/npapi/webplugin_delegate_impl.h"
     11 #include "content/common/plugin_process_messages.h"
     12 #include "content/plugin/plugin_thread.h"
     13 
     14 using content::PluginThread;
     15 
     16 namespace mac_plugin_interposing {
     17 
     18 // TODO(stuartmorgan): Make this an IPC to order the plugin process above the
     19 // browser process only if the browser is current frontmost.
     20 __attribute__((visibility("default")))
     21 void SwitchToPluginProcess() {
     22   ProcessSerialNumber this_process, front_process;
     23   if ((GetCurrentProcess(&this_process) != noErr) ||
     24       (GetFrontProcess(&front_process) != noErr)) {
     25     return;
     26   }
     27 
     28   Boolean matched = false;
     29   if ((SameProcess(&this_process, &front_process, &matched) == noErr) &&
     30       !matched) {
     31     SetFrontProcess(&this_process);
     32   }
     33 }
     34 
     35 __attribute__((visibility("default")))
     36 OpaquePluginRef GetActiveDelegate() {
     37   return content::WebPluginDelegateImpl::GetActiveDelegate();
     38 }
     39 
     40 __attribute__((visibility("default")))
     41 void NotifyBrowserOfPluginSelectWindow(uint32 window_id, CGRect bounds,
     42                                        bool modal) {
     43   PluginThread* plugin_thread = PluginThread::current();
     44   if (plugin_thread) {
     45     gfx::Rect window_bounds(bounds);
     46     plugin_thread->Send(
     47         new PluginProcessHostMsg_PluginSelectWindow(window_id, window_bounds,
     48                                                     modal));
     49   }
     50 }
     51 
     52 __attribute__((visibility("default")))
     53 void NotifyBrowserOfPluginShowWindow(uint32 window_id, CGRect bounds,
     54                                      bool modal) {
     55   PluginThread* plugin_thread = PluginThread::current();
     56   if (plugin_thread) {
     57     gfx::Rect window_bounds(bounds);
     58     plugin_thread->Send(
     59         new PluginProcessHostMsg_PluginShowWindow(window_id, window_bounds,
     60                                                   modal));
     61   }
     62 }
     63 
     64 __attribute__((visibility("default")))
     65 void NotifyBrowserOfPluginHideWindow(uint32 window_id, CGRect bounds) {
     66   PluginThread* plugin_thread = PluginThread::current();
     67   if (plugin_thread) {
     68     gfx::Rect window_bounds(bounds);
     69     plugin_thread->Send(
     70         new PluginProcessHostMsg_PluginHideWindow(window_id, window_bounds));
     71   }
     72 }
     73 
     74 void NotifyPluginOfSetCursorVisibility(bool visibility) {
     75   PluginThread* plugin_thread = PluginThread::current();
     76   if (plugin_thread) {
     77     plugin_thread->Send(
     78         new PluginProcessHostMsg_PluginSetCursorVisibility(visibility));
     79   }
     80 }
     81 
     82 }  // namespace mac_plugin_interposing
     83 
     84 #pragma mark -
     85 
     86 struct WindowInfo {
     87   uint32 window_id;
     88   CGRect bounds;
     89   WindowInfo(NSWindow* window) {
     90     NSInteger window_num = [window windowNumber];
     91     window_id = window_num > 0 ? window_num : 0;
     92     bounds = NSRectToCGRect([window frame]);
     93   }
     94 };
     95 
     96 static void OnPluginWindowClosed(const WindowInfo& window_info) {
     97   if (window_info.window_id == 0)
     98     return;
     99   mac_plugin_interposing::NotifyBrowserOfPluginHideWindow(window_info.window_id,
    100                                                           window_info.bounds);
    101 }
    102 
    103 static BOOL g_waiting_for_window_number = NO;
    104 
    105 static void OnPluginWindowShown(const WindowInfo& window_info, BOOL is_modal) {
    106   // The window id is 0 if it has never been shown (including while it is the
    107   // process of being shown for the first time); when that happens, we'll catch
    108   // it in _setWindowNumber instead.
    109   static BOOL s_pending_display_is_modal = NO;
    110   if (window_info.window_id == 0) {
    111     g_waiting_for_window_number = YES;
    112     if (is_modal)
    113       s_pending_display_is_modal = YES;
    114     return;
    115   }
    116   g_waiting_for_window_number = NO;
    117   if (s_pending_display_is_modal) {
    118     is_modal = YES;
    119     s_pending_display_is_modal = NO;
    120   }
    121   mac_plugin_interposing::NotifyBrowserOfPluginShowWindow(
    122     window_info.window_id, window_info.bounds, is_modal);
    123 }
    124 
    125 @interface NSWindow (ChromePluginUtilities)
    126 // Returns YES if the window is visible and actually on the screen.
    127 - (BOOL)chromePlugin_isWindowOnScreen;
    128 @end
    129 
    130 @implementation NSWindow (ChromePluginUtilities)
    131 
    132 - (BOOL)chromePlugin_isWindowOnScreen {
    133   if (![self isVisible])
    134     return NO;
    135   NSRect window_frame = [self frame];
    136   for (NSScreen* screen in [NSScreen screens]) {
    137     if (NSIntersectsRect(window_frame, [screen frame]))
    138       return YES;
    139   }
    140   return NO;
    141 }
    142 
    143 @end
    144 
    145 @interface NSWindow (ChromePluginInterposing)
    146 - (void)chromePlugin_orderOut:(id)sender;
    147 - (void)chromePlugin_orderFront:(id)sender;
    148 - (void)chromePlugin_makeKeyAndOrderFront:(id)sender;
    149 - (void)chromePlugin_setWindowNumber:(NSInteger)num;
    150 @end
    151 
    152 @implementation NSWindow (ChromePluginInterposing)
    153 
    154 - (void)chromePlugin_orderOut:(id)sender {
    155   WindowInfo window_info(self);
    156   [self chromePlugin_orderOut:sender];
    157   OnPluginWindowClosed(window_info);
    158 }
    159 
    160 - (void)chromePlugin_orderFront:(id)sender {
    161   [self chromePlugin_orderFront:sender];
    162   if ([self chromePlugin_isWindowOnScreen])
    163     mac_plugin_interposing::SwitchToPluginProcess();
    164   OnPluginWindowShown(WindowInfo(self), NO);
    165 }
    166 
    167 - (void)chromePlugin_makeKeyAndOrderFront:(id)sender {
    168   [self chromePlugin_makeKeyAndOrderFront:sender];
    169   if ([self chromePlugin_isWindowOnScreen])
    170     mac_plugin_interposing::SwitchToPluginProcess();
    171   OnPluginWindowShown(WindowInfo(self), NO);
    172 }
    173 
    174 - (void)chromePlugin_setWindowNumber:(NSInteger)num {
    175   if (!g_waiting_for_window_number || num <= 0) {
    176     [self chromePlugin_setWindowNumber:num];
    177     return;
    178   }
    179   mac_plugin_interposing::SwitchToPluginProcess();
    180   [self chromePlugin_setWindowNumber:num];
    181   OnPluginWindowShown(WindowInfo(self), NO);
    182 }
    183 
    184 @end
    185 
    186 @interface NSApplication (ChromePluginInterposing)
    187 - (NSInteger)chromePlugin_runModalForWindow:(NSWindow*)window;
    188 @end
    189 
    190 @implementation NSApplication (ChromePluginInterposing)
    191 
    192 - (NSInteger)chromePlugin_runModalForWindow:(NSWindow*)window {
    193   mac_plugin_interposing::SwitchToPluginProcess();
    194   // This is out-of-order relative to the other calls, but runModalForWindow:
    195   // won't return until the window closes, and the order only matters for
    196   // full-screen windows.
    197   OnPluginWindowShown(WindowInfo(window), YES);
    198   return [self chromePlugin_runModalForWindow:window];
    199 }
    200 
    201 @end
    202 
    203 @interface NSCursor (ChromePluginInterposing)
    204 - (void)chromePlugin_set;
    205 + (void)chromePlugin_hide;
    206 + (void)chromePlugin_unhide;
    207 @end
    208 
    209 @implementation NSCursor (ChromePluginInterposing)
    210 
    211 - (void)chromePlugin_set {
    212   OpaquePluginRef delegate = mac_plugin_interposing::GetActiveDelegate();
    213   if (delegate) {
    214     static_cast<content::WebPluginDelegateImpl*>(delegate)->SetNSCursor(self);
    215     return;
    216   }
    217   [self chromePlugin_set];
    218 }
    219 
    220 + (void)chromePlugin_hide {
    221   mac_plugin_interposing::NotifyPluginOfSetCursorVisibility(false);
    222 }
    223 
    224 + (void)chromePlugin_unhide {
    225   mac_plugin_interposing::NotifyPluginOfSetCursorVisibility(true);
    226 }
    227 
    228 @end
    229 
    230 #pragma mark -
    231 
    232 static void ExchangeMethods(Class target_class,
    233                             BOOL class_method,
    234                             SEL original,
    235                             SEL replacement) {
    236   Method m1;
    237   Method m2;
    238   if (class_method) {
    239     m1 = class_getClassMethod(target_class, original);
    240     m2 = class_getClassMethod(target_class, replacement);
    241   } else {
    242     m1 = class_getInstanceMethod(target_class, original);
    243     m2 = class_getInstanceMethod(target_class, replacement);
    244   }
    245   if (m1 && m2)
    246     method_exchangeImplementations(m1, m2);
    247   else
    248     NOTREACHED() << "Cocoa swizzling failed";
    249 }
    250 
    251 namespace mac_plugin_interposing {
    252 
    253 void SetUpCocoaInterposing() {
    254   Class nswindow_class = [NSWindow class];
    255   ExchangeMethods(nswindow_class, NO, @selector(orderOut:),
    256                   @selector(chromePlugin_orderOut:));
    257   ExchangeMethods(nswindow_class, NO, @selector(orderFront:),
    258                   @selector(chromePlugin_orderFront:));
    259   ExchangeMethods(nswindow_class, NO, @selector(makeKeyAndOrderFront:),
    260                   @selector(chromePlugin_makeKeyAndOrderFront:));
    261   ExchangeMethods(nswindow_class, NO, @selector(_setWindowNumber:),
    262                   @selector(chromePlugin_setWindowNumber:));
    263 
    264   ExchangeMethods([NSApplication class], NO, @selector(runModalForWindow:),
    265                   @selector(chromePlugin_runModalForWindow:));
    266 
    267   Class nscursor_class = [NSCursor class];
    268   ExchangeMethods(nscursor_class, NO, @selector(set),
    269                   @selector(chromePlugin_set));
    270   ExchangeMethods(nscursor_class, YES, @selector(hide),
    271                   @selector(chromePlugin_hide));
    272   ExchangeMethods(nscursor_class, YES, @selector(unhide),
    273                   @selector(chromePlugin_unhide));
    274 }
    275 
    276 }  // namespace mac_plugin_interposing
    277