• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "content/plugin/plugin_interpose_util_mac.h"
6
7#import <AppKit/AppKit.h>
8#import <objc/runtime.h>
9
10#include "content/child/npapi/webplugin_delegate_impl.h"
11#include "content/common/plugin_process_messages.h"
12#include "content/plugin/plugin_thread.h"
13
14using content::PluginThread;
15
16namespace mac_plugin_interposing {
17
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")))
21void SwitchToPluginProcess() {
22  ProcessSerialNumber this_process, front_process;
23  if ((GetCurrentProcess(&this_process) != noErr) ||
24      (GetFrontProcess(&front_process) != noErr)) {
25    return;
26  }
27
28  Boolean matched = false;
29  if ((SameProcess(&this_process, &front_process, &matched) == noErr) &&
30      !matched) {
31    SetFrontProcess(&this_process);
32  }
33}
34
35__attribute__((visibility("default")))
36OpaquePluginRef GetActiveDelegate() {
37  return content::WebPluginDelegateImpl::GetActiveDelegate();
38}
39
40__attribute__((visibility("default")))
41void NotifyBrowserOfPluginSelectWindow(uint32 window_id, CGRect bounds,
42                                       bool modal) {
43  PluginThread* plugin_thread = PluginThread::current();
44  if (plugin_thread) {
45    gfx::Rect window_bounds(bounds);
46    plugin_thread->Send(
47        new PluginProcessHostMsg_PluginSelectWindow(window_id, window_bounds,
48                                                    modal));
49  }
50}
51
52__attribute__((visibility("default")))
53void NotifyBrowserOfPluginShowWindow(uint32 window_id, CGRect bounds,
54                                     bool modal) {
55  PluginThread* plugin_thread = PluginThread::current();
56  if (plugin_thread) {
57    gfx::Rect window_bounds(bounds);
58    plugin_thread->Send(
59        new PluginProcessHostMsg_PluginShowWindow(window_id, window_bounds,
60                                                  modal));
61  }
62}
63
64__attribute__((visibility("default")))
65void NotifyBrowserOfPluginHideWindow(uint32 window_id, CGRect bounds) {
66  PluginThread* plugin_thread = PluginThread::current();
67  if (plugin_thread) {
68    gfx::Rect window_bounds(bounds);
69    plugin_thread->Send(
70        new PluginProcessHostMsg_PluginHideWindow(window_id, window_bounds));
71  }
72}
73
74void NotifyPluginOfSetCursorVisibility(bool visibility) {
75  PluginThread* plugin_thread = PluginThread::current();
76  if (plugin_thread) {
77    plugin_thread->Send(
78        new PluginProcessHostMsg_PluginSetCursorVisibility(visibility));
79  }
80}
81
82}  // namespace mac_plugin_interposing
83
84#pragma mark -
85
86struct WindowInfo {
87  uint32 window_id;
88  CGRect bounds;
89  WindowInfo(NSWindow* window) {
90    NSInteger window_num = [window windowNumber];
91    window_id = window_num > 0 ? window_num : 0;
92    bounds = NSRectToCGRect([window frame]);
93  }
94};
95
96static void OnPluginWindowClosed(const WindowInfo& window_info) {
97  if (window_info.window_id == 0)
98    return;
99  mac_plugin_interposing::NotifyBrowserOfPluginHideWindow(window_info.window_id,
100                                                          window_info.bounds);
101}
102
103static BOOL g_waiting_for_window_number = NO;
104
105static 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;
112    if (is_modal)
113      s_pending_display_is_modal = YES;
114    return;
115  }
116  g_waiting_for_window_number = NO;
117  if (s_pending_display_is_modal) {
118    is_modal = YES;
119    s_pending_display_is_modal = NO;
120  }
121  mac_plugin_interposing::NotifyBrowserOfPluginShowWindow(
122    window_info.window_id, window_info.bounds, is_modal);
123}
124
125@interface NSWindow (ChromePluginUtilities)
126// Returns YES if the window is visible and actually on the screen.
127- (BOOL)chromePlugin_isWindowOnScreen;
128@end
129
130@implementation NSWindow (ChromePluginUtilities)
131
132- (BOOL)chromePlugin_isWindowOnScreen {
133  if (![self isVisible])
134    return NO;
135  NSRect window_frame = [self frame];
136  for (NSScreen* screen in [NSScreen screens]) {
137    if (NSIntersectsRect(window_frame, [screen frame]))
138      return YES;
139  }
140  return NO;
141}
142
143@end
144
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;
150@end
151
152@implementation NSWindow (ChromePluginInterposing)
153
154- (void)chromePlugin_orderOut:(id)sender {
155  WindowInfo window_info(self);
156  [self chromePlugin_orderOut:sender];
157  OnPluginWindowClosed(window_info);
158}
159
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);
165}
166
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);
172}
173
174- (void)chromePlugin_setWindowNumber:(NSInteger)num {
175  if (!g_waiting_for_window_number || num <= 0) {
176    [self chromePlugin_setWindowNumber:num];
177    return;
178  }
179  mac_plugin_interposing::SwitchToPluginProcess();
180  [self chromePlugin_setWindowNumber:num];
181  OnPluginWindowShown(WindowInfo(self), NO);
182}
183
184@end
185
186@interface NSApplication (ChromePluginInterposing)
187- (NSInteger)chromePlugin_runModalForWindow:(NSWindow*)window;
188@end
189
190@implementation NSApplication (ChromePluginInterposing)
191
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];
199}
200
201@end
202
203@interface NSCursor (ChromePluginInterposing)
204- (void)chromePlugin_set;
205+ (void)chromePlugin_hide;
206+ (void)chromePlugin_unhide;
207@end
208
209@implementation NSCursor (ChromePluginInterposing)
210
211- (void)chromePlugin_set {
212  OpaquePluginRef delegate = mac_plugin_interposing::GetActiveDelegate();
213  if (delegate) {
214    static_cast<content::WebPluginDelegateImpl*>(delegate)->SetNSCursor(self);
215    return;
216  }
217  [self chromePlugin_set];
218}
219
220+ (void)chromePlugin_hide {
221  mac_plugin_interposing::NotifyPluginOfSetCursorVisibility(false);
222}
223
224+ (void)chromePlugin_unhide {
225  mac_plugin_interposing::NotifyPluginOfSetCursorVisibility(true);
226}
227
228@end
229
230#pragma mark -
231
232static void ExchangeMethods(Class target_class,
233                            BOOL class_method,
234                            SEL original,
235                            SEL replacement) {
236  Method m1;
237  Method m2;
238  if (class_method) {
239    m1 = class_getClassMethod(target_class, original);
240    m2 = class_getClassMethod(target_class, replacement);
241  } else {
242    m1 = class_getInstanceMethod(target_class, original);
243    m2 = class_getInstanceMethod(target_class, replacement);
244  }
245  if (m1 && m2)
246    method_exchangeImplementations(m1, m2);
247  else
248    NOTREACHED() << "Cocoa swizzling failed";
249}
250
251namespace mac_plugin_interposing {
252
253void 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:));
263
264  ExchangeMethods([NSApplication class], NO, @selector(runModalForWindow:),
265                  @selector(chromePlugin_runModalForWindow:));
266
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));
274}
275
276}  // namespace mac_plugin_interposing
277