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.
5 #include "content/plugin/plugin_interpose_util_mac.h"
7 #import <AppKit/AppKit.h>
8 #import <objc/runtime.h>
10 #include "content/child/npapi/webplugin_delegate_impl.h"
11 #include "content/common/plugin_process_messages.h"
12 #include "content/plugin/plugin_thread.h"
14 using content::PluginThread;
16 namespace mac_plugin_interposing {
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)) {
28 Boolean matched = false;
29 if ((SameProcess(&this_process, &front_process, &matched) == noErr) &&
31 SetFrontProcess(&this_process);
35 __attribute__((visibility("default")))
36 OpaquePluginRef GetActiveDelegate() {
37 return content::WebPluginDelegateImpl::GetActiveDelegate();
40 __attribute__((visibility("default")))
41 void NotifyBrowserOfPluginSelectWindow(uint32 window_id, CGRect bounds,
43 PluginThread* plugin_thread = PluginThread::current();
45 gfx::Rect window_bounds(bounds);
47 new PluginProcessHostMsg_PluginSelectWindow(window_id, window_bounds,
52 __attribute__((visibility("default")))
53 void NotifyBrowserOfPluginShowWindow(uint32 window_id, CGRect bounds,
55 PluginThread* plugin_thread = PluginThread::current();
57 gfx::Rect window_bounds(bounds);
59 new PluginProcessHostMsg_PluginShowWindow(window_id, window_bounds,
64 __attribute__((visibility("default")))
65 void NotifyBrowserOfPluginHideWindow(uint32 window_id, CGRect bounds) {
66 PluginThread* plugin_thread = PluginThread::current();
68 gfx::Rect window_bounds(bounds);
70 new PluginProcessHostMsg_PluginHideWindow(window_id, window_bounds));
74 void NotifyPluginOfSetCursorVisibility(bool visibility) {
75 PluginThread* plugin_thread = PluginThread::current();
78 new PluginProcessHostMsg_PluginSetCursorVisibility(visibility));
82 } // namespace mac_plugin_interposing
89 WindowInfo(NSWindow* window) {
90 NSInteger window_num = [window windowNumber];
91 window_id = window_num > 0 ? window_num : 0;
92 bounds = NSRectToCGRect([window frame]);
96 static void OnPluginWindowClosed(const WindowInfo& window_info) {
97 if (window_info.window_id == 0)
99 mac_plugin_interposing::NotifyBrowserOfPluginHideWindow(window_info.window_id,
103 static BOOL g_waiting_for_window_number = NO;
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;
113 s_pending_display_is_modal = YES;
116 g_waiting_for_window_number = NO;
117 if (s_pending_display_is_modal) {
119 s_pending_display_is_modal = NO;
121 mac_plugin_interposing::NotifyBrowserOfPluginShowWindow(
122 window_info.window_id, window_info.bounds, is_modal);
125 @interface NSWindow (ChromePluginUtilities)
126 // Returns YES if the window is visible and actually on the screen.
127 - (BOOL)chromePlugin_isWindowOnScreen;
130 @implementation NSWindow (ChromePluginUtilities)
132 - (BOOL)chromePlugin_isWindowOnScreen {
133 if (![self isVisible])
135 NSRect window_frame = [self frame];
136 for (NSScreen* screen in [NSScreen screens]) {
137 if (NSIntersectsRect(window_frame, [screen frame]))
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;
152 @implementation NSWindow (ChromePluginInterposing)
154 - (void)chromePlugin_orderOut:(id)sender {
155 WindowInfo window_info(self);
156 [self chromePlugin_orderOut:sender];
157 OnPluginWindowClosed(window_info);
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);
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);
174 - (void)chromePlugin_setWindowNumber:(NSInteger)num {
175 if (!g_waiting_for_window_number || num <= 0) {
176 [self chromePlugin_setWindowNumber:num];
179 mac_plugin_interposing::SwitchToPluginProcess();
180 [self chromePlugin_setWindowNumber:num];
181 OnPluginWindowShown(WindowInfo(self), NO);
186 @interface NSApplication (ChromePluginInterposing)
187 - (NSInteger)chromePlugin_runModalForWindow:(NSWindow*)window;
190 @implementation NSApplication (ChromePluginInterposing)
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];
203 @interface NSCursor (ChromePluginInterposing)
204 - (void)chromePlugin_set;
205 + (void)chromePlugin_hide;
206 + (void)chromePlugin_unhide;
209 @implementation NSCursor (ChromePluginInterposing)
211 - (void)chromePlugin_set {
212 OpaquePluginRef delegate = mac_plugin_interposing::GetActiveDelegate();
214 static_cast<content::WebPluginDelegateImpl*>(delegate)->SetNSCursor(self);
217 [self chromePlugin_set];
220 + (void)chromePlugin_hide {
221 mac_plugin_interposing::NotifyPluginOfSetCursorVisibility(false);
224 + (void)chromePlugin_unhide {
225 mac_plugin_interposing::NotifyPluginOfSetCursorVisibility(true);
232 static void ExchangeMethods(Class target_class,
239 m1 = class_getClassMethod(target_class, original);
240 m2 = class_getClassMethod(target_class, replacement);
242 m1 = class_getInstanceMethod(target_class, original);
243 m2 = class_getInstanceMethod(target_class, replacement);
246 method_exchangeImplementations(m1, m2);
248 NOTREACHED() << "Cocoa swizzling failed";
251 namespace mac_plugin_interposing {
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:));
264 ExchangeMethods([NSApplication class], NO, @selector(runModalForWindow:),
265 @selector(chromePlugin_runModalForWindow:));
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));
276 } // namespace mac_plugin_interposing