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