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;
18 // Brings the plugin process to the front so that the user can see its windows.
19 void SwitchToPluginProcess() {
20 ProcessSerialNumber this_process, front_process;
21 if ((GetCurrentProcess(&this_process) != noErr) ||
22 (GetFrontProcess(&front_process) != noErr)) {
26 Boolean matched = false;
27 if ((SameProcess(&this_process, &front_process, &matched) == noErr) &&
29 SetFrontProcess(&this_process);
33 // Sends a message to the browser process to inform it that the given window
35 void NotifyBrowserOfPluginShowWindow(uint32 window_id, CGRect bounds,
37 PluginThread* plugin_thread = PluginThread::current();
39 gfx::Rect window_bounds(bounds);
41 new PluginProcessHostMsg_PluginShowWindow(window_id, window_bounds,
46 // Sends a message to the browser process to inform it that the given window
47 // has been hidden, and switches focus back to the browser process if there are
48 // no remaining plugin windows.
49 void NotifyBrowserOfPluginHideWindow(uint32 window_id, CGRect bounds) {
50 PluginThread* plugin_thread = PluginThread::current();
52 gfx::Rect window_bounds(bounds);
54 new PluginProcessHostMsg_PluginHideWindow(window_id, window_bounds));
58 // Informs the host that the plugin has changed the cursor visibility.
59 void NotifyPluginOfSetCursorVisibility(bool visibility) {
60 PluginThread* plugin_thread = PluginThread::current();
63 new PluginProcessHostMsg_PluginSetCursorVisibility(visibility));
70 WindowInfo(NSWindow* window) {
71 NSInteger window_num = [window windowNumber];
72 window_id = window_num > 0 ? window_num : 0;
73 bounds = NSRectToCGRect([window frame]);
77 void OnPluginWindowClosed(const WindowInfo& window_info) {
78 if (window_info.window_id == 0)
80 NotifyBrowserOfPluginHideWindow(window_info.window_id, window_info.bounds);
83 BOOL g_waiting_for_window_number = NO;
85 void OnPluginWindowShown(const WindowInfo& window_info, BOOL is_modal) {
86 // The window id is 0 if it has never been shown (including while it is the
87 // process of being shown for the first time); when that happens, we'll catch
88 // it in _setWindowNumber instead.
89 static BOOL s_pending_display_is_modal = NO;
90 if (window_info.window_id == 0) {
91 g_waiting_for_window_number = YES;
93 s_pending_display_is_modal = YES;
96 g_waiting_for_window_number = NO;
97 if (s_pending_display_is_modal) {
99 s_pending_display_is_modal = NO;
101 NotifyBrowserOfPluginShowWindow(window_info.window_id, window_info.bounds,
107 @interface NSWindow (ChromePluginUtilities)
108 // Returns YES if the window is visible and actually on the screen.
109 - (BOOL)chromePlugin_isWindowOnScreen;
112 @implementation NSWindow (ChromePluginUtilities)
114 - (BOOL)chromePlugin_isWindowOnScreen {
115 if (![self isVisible])
117 NSRect window_frame = [self frame];
118 for (NSScreen* screen in [NSScreen screens]) {
119 if (NSIntersectsRect(window_frame, [screen frame]))
127 @interface NSWindow (ChromePluginInterposing)
128 - (void)chromePlugin_orderOut:(id)sender;
129 - (void)chromePlugin_orderFront:(id)sender;
130 - (void)chromePlugin_makeKeyAndOrderFront:(id)sender;
131 - (void)chromePlugin_setWindowNumber:(NSInteger)num;
134 @implementation NSWindow (ChromePluginInterposing)
136 - (void)chromePlugin_orderOut:(id)sender {
137 WindowInfo window_info(self);
138 [self chromePlugin_orderOut:sender];
139 OnPluginWindowClosed(window_info);
142 - (void)chromePlugin_orderFront:(id)sender {
143 [self chromePlugin_orderFront:sender];
144 if ([self chromePlugin_isWindowOnScreen])
145 SwitchToPluginProcess();
146 OnPluginWindowShown(WindowInfo(self), NO);
149 - (void)chromePlugin_makeKeyAndOrderFront:(id)sender {
150 [self chromePlugin_makeKeyAndOrderFront:sender];
151 if ([self chromePlugin_isWindowOnScreen])
152 SwitchToPluginProcess();
153 OnPluginWindowShown(WindowInfo(self), NO);
156 - (void)chromePlugin_setWindowNumber:(NSInteger)num {
157 if (!g_waiting_for_window_number || num <= 0) {
158 [self chromePlugin_setWindowNumber:num];
161 SwitchToPluginProcess();
162 [self chromePlugin_setWindowNumber:num];
163 OnPluginWindowShown(WindowInfo(self), NO);
168 @interface NSApplication (ChromePluginInterposing)
169 - (NSInteger)chromePlugin_runModalForWindow:(NSWindow*)window;
172 @implementation NSApplication (ChromePluginInterposing)
174 - (NSInteger)chromePlugin_runModalForWindow:(NSWindow*)window {
175 SwitchToPluginProcess();
176 // This is out-of-order relative to the other calls, but runModalForWindow:
177 // won't return until the window closes, and the order only matters for
178 // full-screen windows.
179 OnPluginWindowShown(WindowInfo(window), YES);
180 return [self chromePlugin_runModalForWindow:window];
185 @interface NSCursor (ChromePluginInterposing)
186 - (void)chromePlugin_set;
187 + (void)chromePlugin_hide;
188 + (void)chromePlugin_unhide;
191 @implementation NSCursor (ChromePluginInterposing)
193 - (void)chromePlugin_set {
194 content::WebPluginDelegateImpl* delegate =
195 content::WebPluginDelegateImpl::GetActiveDelegate();
197 delegate->SetNSCursor(self);
200 [self chromePlugin_set];
203 + (void)chromePlugin_hide {
204 NotifyPluginOfSetCursorVisibility(false);
207 + (void)chromePlugin_unhide {
208 NotifyPluginOfSetCursorVisibility(true);
215 static void ExchangeMethods(Class target_class,
222 m1 = class_getClassMethod(target_class, original);
223 m2 = class_getClassMethod(target_class, replacement);
225 m1 = class_getInstanceMethod(target_class, original);
226 m2 = class_getInstanceMethod(target_class, replacement);
229 method_exchangeImplementations(m1, m2);
231 NOTREACHED() << "Cocoa swizzling failed";
234 namespace mac_plugin_interposing {
236 void SetUpCocoaInterposing() {
237 Class nswindow_class = [NSWindow class];
238 ExchangeMethods(nswindow_class, NO, @selector(orderOut:),
239 @selector(chromePlugin_orderOut:));
240 ExchangeMethods(nswindow_class, NO, @selector(orderFront:),
241 @selector(chromePlugin_orderFront:));
242 ExchangeMethods(nswindow_class, NO, @selector(makeKeyAndOrderFront:),
243 @selector(chromePlugin_makeKeyAndOrderFront:));
244 ExchangeMethods(nswindow_class, NO, @selector(_setWindowNumber:),
245 @selector(chromePlugin_setWindowNumber:));
247 ExchangeMethods([NSApplication class], NO, @selector(runModalForWindow:),
248 @selector(chromePlugin_runModalForWindow:));
250 Class nscursor_class = [NSCursor class];
251 ExchangeMethods(nscursor_class, NO, @selector(set),
252 @selector(chromePlugin_set));
253 ExchangeMethods(nscursor_class, YES, @selector(hide),
254 @selector(chromePlugin_hide));
255 ExchangeMethods(nscursor_class, YES, @selector(unhide),
256 @selector(chromePlugin_unhide));
259 } // namespace mac_plugin_interposing