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.
5 // Redistribution and use in source and binary forms, with or without
6 // modification, are permitted provided that the following conditions are
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
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.
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"
36 #import <AppKit/AppKit.h>
37 #import <objc/runtime.h>
39 using mozilla::plugins::PluginModuleChild;
40 using mozilla::plugins::AssertPluginThread;
42 namespace mac_plugin_interposing {
45 // Tracks plugin windows currently visible.
46 std::set<uint32_t> plugin_visible_windows_set_;
47 // Tracks full screen windows currently visible.
48 std::set<uint32_t> plugin_fullscreen_windows_set_;
49 // Tracks modal windows currently visible.
50 std::set<uint32_t> plugin_modal_windows_set_;
52 void OnPluginShowWindow(uint32_t window_id,
55 plugin_visible_windows_set_.insert(window_id);
58 plugin_modal_windows_set_.insert(window_id);
60 CGRect main_display_bounds = ::CGDisplayBounds(CGMainDisplayID());
62 if (CGRectEqualToRect(window_bounds, main_display_bounds) &&
63 (plugin_fullscreen_windows_set_.find(window_id) ==
64 plugin_fullscreen_windows_set_.end())) {
65 plugin_fullscreen_windows_set_.insert(window_id);
67 nsCocoaUtils::HideOSChromeOnScreen(TRUE, [[NSScreen screens] objectAtIndex:0]);
71 static void ActivateProcess(pid_t pid) {
72 ProcessSerialNumber process;
73 OSStatus status = ::GetProcessForPID(pid, &process);
75 if (status == noErr) {
76 SetFrontProcess(&process);
78 NS_WARNING("Unable to get process for pid.");
82 // Must be called on the UI thread.
83 // If plugin_pid is -1, the browser will be the active process on return,
84 // otherwise that process will be given focus back before this function returns.
85 static void ReleasePluginFullScreen(pid_t plugin_pid) {
86 // Releasing full screen only works if we are the frontmost process; grab
87 // focus, but give it back to the plugin process if requested.
88 ActivateProcess(base::GetCurrentProcId());
90 nsCocoaUtils::HideOSChromeOnScreen(FALSE, [[NSScreen screens] objectAtIndex:0]);
92 if (plugin_pid != -1) {
93 ActivateProcess(plugin_pid);
97 void OnPluginHideWindow(uint32_t window_id, pid_t aPluginPid) {
98 bool had_windows = !plugin_visible_windows_set_.empty();
99 plugin_visible_windows_set_.erase(window_id);
100 bool browser_needs_activation = had_windows &&
101 plugin_visible_windows_set_.empty();
103 plugin_modal_windows_set_.erase(window_id);
104 if (plugin_fullscreen_windows_set_.find(window_id) !=
105 plugin_fullscreen_windows_set_.end()) {
106 plugin_fullscreen_windows_set_.erase(window_id);
107 pid_t plugin_pid = browser_needs_activation ? -1 : aPluginPid;
108 browser_needs_activation = false;
109 ReleasePluginFullScreen(plugin_pid);
112 if (browser_needs_activation) {
113 ActivateProcess(getpid());
118 } // namespace mac_plugin_interposing
120 namespace mac_plugin_interposing {
123 // TODO(stuartmorgan): Make this an IPC to order the plugin process above the
124 // browser process only if the browser is current frontmost.
125 void FocusPluginProcess() {
126 ProcessSerialNumber this_process, front_process;
127 if ((GetCurrentProcess(&this_process) != noErr) ||
128 (GetFrontProcess(&front_process) != noErr)) {
132 Boolean matched = false;
133 if ((SameProcess(&this_process, &front_process, &matched) == noErr) &&
135 SetFrontProcess(&this_process);
139 void NotifyBrowserOfPluginShowWindow(uint32_t window_id, CGRect bounds,
141 AssertPluginThread();
143 PluginModuleChild *pmc = PluginModuleChild::current();
145 pmc->PluginShowWindow(window_id, modal, bounds);
148 void NotifyBrowserOfPluginHideWindow(uint32_t window_id, CGRect bounds) {
149 AssertPluginThread();
151 PluginModuleChild *pmc = PluginModuleChild::current();
153 pmc->PluginHideWindow(window_id);
159 WindowInfo(NSWindow* window) {
160 NSInteger window_num = [window windowNumber];
161 window_id = window_num > 0 ? window_num : 0;
162 bounds = NSRectToCGRect([window frame]);
166 static void OnPluginWindowClosed(const WindowInfo& window_info) {
167 if (window_info.window_id == 0)
169 mac_plugin_interposing::child::NotifyBrowserOfPluginHideWindow(window_info.window_id,
173 static void OnPluginWindowShown(const WindowInfo& window_info, BOOL is_modal) {
174 // The window id is 0 if it has never been shown (including while it is the
175 // process of being shown for the first time); when that happens, we'll catch
176 // it in _setWindowNumber instead.
177 static BOOL s_pending_display_is_modal = NO;
178 if (window_info.window_id == 0) {
180 s_pending_display_is_modal = YES;
183 if (s_pending_display_is_modal) {
185 s_pending_display_is_modal = NO;
187 mac_plugin_interposing::child::NotifyBrowserOfPluginShowWindow(
188 window_info.window_id, window_info.bounds, is_modal);
192 } // namespace mac_plugin_interposing
194 using mac_plugin_interposing::child::WindowInfo;
196 @interface NSWindow (PluginInterposing)
197 - (void)pluginInterpose_orderOut:(id)sender;
198 - (void)pluginInterpose_orderFront:(id)sender;
199 - (void)pluginInterpose_makeKeyAndOrderFront:(id)sender;
200 - (void)pluginInterpose_setWindowNumber:(NSInteger)num;
203 @implementation NSWindow (PluginInterposing)
205 - (void)pluginInterpose_orderOut:(id)sender {
206 WindowInfo window_info(self);
207 [self pluginInterpose_orderOut:sender];
208 OnPluginWindowClosed(window_info);
211 - (void)pluginInterpose_orderFront:(id)sender {
212 mac_plugin_interposing::child::FocusPluginProcess();
213 [self pluginInterpose_orderFront:sender];
214 OnPluginWindowShown(WindowInfo(self), NO);
217 - (void)pluginInterpose_makeKeyAndOrderFront:(id)sender {
218 mac_plugin_interposing::child::FocusPluginProcess();
219 [self pluginInterpose_makeKeyAndOrderFront:sender];
220 OnPluginWindowShown(WindowInfo(self), NO);
223 - (void)pluginInterpose_setWindowNumber:(NSInteger)num {
225 mac_plugin_interposing::child::FocusPluginProcess();
226 [self pluginInterpose_setWindowNumber:num];
228 OnPluginWindowShown(WindowInfo(self), NO);
233 @interface NSApplication (PluginInterposing)
234 - (NSInteger)pluginInterpose_runModalForWindow:(NSWindow*)window;
237 @implementation NSApplication (PluginInterposing)
239 - (NSInteger)pluginInterpose_runModalForWindow:(NSWindow*)window {
240 mac_plugin_interposing::child::FocusPluginProcess();
241 // This is out-of-order relative to the other calls, but runModalForWindow:
242 // won't return until the window closes, and the order only matters for
243 // full-screen windows.
244 OnPluginWindowShown(WindowInfo(window), YES);
245 return [self pluginInterpose_runModalForWindow:window];
250 static void ExchangeMethods(Class target_class,
257 m1 = class_getClassMethod(target_class, original);
258 m2 = class_getClassMethod(target_class, replacement);
260 m1 = class_getInstanceMethod(target_class, original);
261 m2 = class_getInstanceMethod(target_class, replacement);
268 method_exchangeImplementations(m1, m2);
270 NS_NOTREACHED("Cocoa swizzling failed");
273 namespace mac_plugin_interposing {
276 void SetUpCocoaInterposing() {
277 Class nswindow_class = [NSWindow class];
278 ExchangeMethods(nswindow_class, NO, @selector(orderOut:),
279 @selector(pluginInterpose_orderOut:));
280 ExchangeMethods(nswindow_class, NO, @selector(orderFront:),
281 @selector(pluginInterpose_orderFront:));
282 ExchangeMethods(nswindow_class, NO, @selector(makeKeyAndOrderFront:),
283 @selector(pluginInterpose_makeKeyAndOrderFront:));
284 ExchangeMethods(nswindow_class, NO, @selector(_setWindowNumber:),
285 @selector(pluginInterpose_setWindowNumber:));
287 ExchangeMethods([NSApplication class], NO, @selector(runModalForWindow:),
288 @selector(pluginInterpose_runModalForWindow:));
292 } // namespace mac_plugin_interposing