Fix regexp match pair end-index == -1 assumption. (r=dmandelin, a=blocker b=605754)
[mozilla-central.git] / dom / plugins / PluginInterposeOSX.mm
blob23f19da9f50378638f31b7b2c325afd84f2aa553
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.
4 //
5 // Redistribution and use in source and binary forms, with or without
6 // modification, are permitted provided that the following conditions are
7 // met:
8 //
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
14 // distribution.
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"
34 #include "nsDebug.h"
35 #include <set>
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 {
43 namespace parent {
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,
53                         CGRect window_bounds,
54                         bool modal) {
55   plugin_visible_windows_set_.insert(window_id);
57   if (modal)
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]);
68   }
71 static void ActivateProcess(pid_t pid) {
72   ProcessSerialNumber process;
73   OSStatus status = ::GetProcessForPID(pid, &process);
75   if (status == noErr) {
76     SetFrontProcess(&process);
77   } else {
78     NS_WARNING("Unable to get process for pid.");
79   }
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);
94   }
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);
110   }
112   if (browser_needs_activation) {
113     ActivateProcess(getpid());
114   }
117 } // parent
118 } // namespace mac_plugin_interposing
120 namespace mac_plugin_interposing {
121 namespace child {
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)) {
129     return;
130   }
132   Boolean matched = false;
133   if ((SameProcess(&this_process, &front_process, &matched) == noErr) &&
134       !matched) {
135     SetFrontProcess(&this_process);
136   }
139 void NotifyBrowserOfPluginShowWindow(uint32_t window_id, CGRect bounds,
140                                      bool modal) {
141   AssertPluginThread();
143   PluginModuleChild *pmc = PluginModuleChild::current();
144   if (pmc)
145     pmc->PluginShowWindow(window_id, modal, bounds);
148 void NotifyBrowserOfPluginHideWindow(uint32_t window_id, CGRect bounds) {
149   AssertPluginThread();
151   PluginModuleChild *pmc = PluginModuleChild::current();
152   if (pmc)
153     pmc->PluginHideWindow(window_id);
156 struct WindowInfo {
157   uint32_t window_id;
158   CGRect bounds;
159   WindowInfo(NSWindow* window) {
160     NSInteger window_num = [window windowNumber];
161     window_id = window_num > 0 ? window_num : 0;
162     bounds = NSRectToCGRect([window frame]);
163   }
166 static void OnPluginWindowClosed(const WindowInfo& window_info) {
167   if (window_info.window_id == 0)
168     return;
169   mac_plugin_interposing::child::NotifyBrowserOfPluginHideWindow(window_info.window_id,
170                                                                  window_info.bounds);
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) {
179     if (is_modal)
180       s_pending_display_is_modal = YES;
181     return;
182   }
183   if (s_pending_display_is_modal) {
184     is_modal = YES;
185     s_pending_display_is_modal = NO;
186   }
187   mac_plugin_interposing::child::NotifyBrowserOfPluginShowWindow(
188     window_info.window_id, window_info.bounds, is_modal);
191 } // child
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;
201 @end
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 {
224   if (num > 0)
225     mac_plugin_interposing::child::FocusPluginProcess();
226   [self pluginInterpose_setWindowNumber:num];
227   if (num > 0)
228     OnPluginWindowShown(WindowInfo(self), NO);
231 @end
233 @interface NSApplication (PluginInterposing)
234 - (NSInteger)pluginInterpose_runModalForWindow:(NSWindow*)window;
235 @end
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];
248 @end
250 static void ExchangeMethods(Class target_class,
251                             BOOL class_method,
252                             SEL original,
253                             SEL replacement) {
254   Method m1;
255   Method m2;
256   if (class_method) {
257     m1 = class_getClassMethod(target_class, original);
258     m2 = class_getClassMethod(target_class, replacement);
259   } else {
260     m1 = class_getInstanceMethod(target_class, original);
261     m2 = class_getInstanceMethod(target_class, replacement);
262   }
264   if (m1 == m2)
265     return;
267   if (m1 && m2)
268     method_exchangeImplementations(m1, m2);
269   else
270     NS_NOTREACHED("Cocoa swizzling failed");
273 namespace mac_plugin_interposing {
274 namespace child {
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:));
291 }  // namespace child
292 }  // namespace mac_plugin_interposing