• 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 {
17
18// Brings the plugin process to the front so that the user can see its windows.
19void SwitchToPluginProcess() {
20  ProcessSerialNumber this_process, front_process;
21  if ((GetCurrentProcess(&this_process) != noErr) ||
22      (GetFrontProcess(&front_process) != noErr)) {
23    return;
24  }
25
26  Boolean matched = false;
27  if ((SameProcess(&this_process, &front_process, &matched) == noErr) &&
28      !matched) {
29    SetFrontProcess(&this_process);
30  }
31}
32
33// Sends a message to the browser process to inform it that the given window
34// has been shown.
35void NotifyBrowserOfPluginShowWindow(uint32 window_id, CGRect bounds,
36                                     bool modal) {
37  PluginThread* plugin_thread = PluginThread::current();
38  if (plugin_thread) {
39    gfx::Rect window_bounds(bounds);
40    plugin_thread->Send(
41        new PluginProcessHostMsg_PluginShowWindow(window_id, window_bounds,
42                                                  modal));
43  }
44}
45
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.
49void NotifyBrowserOfPluginHideWindow(uint32 window_id, CGRect bounds) {
50  PluginThread* plugin_thread = PluginThread::current();
51  if (plugin_thread) {
52    gfx::Rect window_bounds(bounds);
53    plugin_thread->Send(
54        new PluginProcessHostMsg_PluginHideWindow(window_id, window_bounds));
55  }
56}
57
58// Informs the host that the plugin has changed the cursor visibility.
59void NotifyPluginOfSetCursorVisibility(bool visibility) {
60  PluginThread* plugin_thread = PluginThread::current();
61  if (plugin_thread) {
62    plugin_thread->Send(
63        new PluginProcessHostMsg_PluginSetCursorVisibility(visibility));
64  }
65}
66
67struct WindowInfo {
68  uint32 window_id;
69  CGRect bounds;
70  WindowInfo(NSWindow* window) {
71    NSInteger window_num = [window windowNumber];
72    window_id = window_num > 0 ? window_num : 0;
73    bounds = NSRectToCGRect([window frame]);
74  }
75};
76
77void OnPluginWindowClosed(const WindowInfo& window_info) {
78  if (window_info.window_id == 0)
79    return;
80  NotifyBrowserOfPluginHideWindow(window_info.window_id, window_info.bounds);
81}
82
83BOOL g_waiting_for_window_number = NO;
84
85void 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;
92    if (is_modal)
93      s_pending_display_is_modal = YES;
94    return;
95  }
96  g_waiting_for_window_number = NO;
97  if (s_pending_display_is_modal) {
98    is_modal = YES;
99    s_pending_display_is_modal = NO;
100  }
101  NotifyBrowserOfPluginShowWindow(window_info.window_id, window_info.bounds,
102                                  is_modal);
103}
104
105}  // namespace
106
107@interface NSWindow (ChromePluginUtilities)
108// Returns YES if the window is visible and actually on the screen.
109- (BOOL)chromePlugin_isWindowOnScreen;
110@end
111
112@implementation NSWindow (ChromePluginUtilities)
113
114- (BOOL)chromePlugin_isWindowOnScreen {
115  if (![self isVisible])
116    return NO;
117  NSRect window_frame = [self frame];
118  for (NSScreen* screen in [NSScreen screens]) {
119    if (NSIntersectsRect(window_frame, [screen frame]))
120      return YES;
121  }
122  return NO;
123}
124
125@end
126
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;
132@end
133
134@implementation NSWindow (ChromePluginInterposing)
135
136- (void)chromePlugin_orderOut:(id)sender {
137  WindowInfo window_info(self);
138  [self chromePlugin_orderOut:sender];
139  OnPluginWindowClosed(window_info);
140}
141
142- (void)chromePlugin_orderFront:(id)sender {
143  [self chromePlugin_orderFront:sender];
144  if ([self chromePlugin_isWindowOnScreen])
145    SwitchToPluginProcess();
146  OnPluginWindowShown(WindowInfo(self), NO);
147}
148
149- (void)chromePlugin_makeKeyAndOrderFront:(id)sender {
150  [self chromePlugin_makeKeyAndOrderFront:sender];
151  if ([self chromePlugin_isWindowOnScreen])
152    SwitchToPluginProcess();
153  OnPluginWindowShown(WindowInfo(self), NO);
154}
155
156- (void)chromePlugin_setWindowNumber:(NSInteger)num {
157  if (!g_waiting_for_window_number || num <= 0) {
158    [self chromePlugin_setWindowNumber:num];
159    return;
160  }
161  SwitchToPluginProcess();
162  [self chromePlugin_setWindowNumber:num];
163  OnPluginWindowShown(WindowInfo(self), NO);
164}
165
166@end
167
168@interface NSApplication (ChromePluginInterposing)
169- (NSInteger)chromePlugin_runModalForWindow:(NSWindow*)window;
170@end
171
172@implementation NSApplication (ChromePluginInterposing)
173
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];
181}
182
183@end
184
185@interface NSCursor (ChromePluginInterposing)
186- (void)chromePlugin_set;
187+ (void)chromePlugin_hide;
188+ (void)chromePlugin_unhide;
189@end
190
191@implementation NSCursor (ChromePluginInterposing)
192
193- (void)chromePlugin_set {
194  content::WebPluginDelegateImpl* delegate =
195      content::WebPluginDelegateImpl::GetActiveDelegate();
196  if (delegate) {
197    delegate->SetNSCursor(self);
198    return;
199  }
200  [self chromePlugin_set];
201}
202
203+ (void)chromePlugin_hide {
204  NotifyPluginOfSetCursorVisibility(false);
205}
206
207+ (void)chromePlugin_unhide {
208  NotifyPluginOfSetCursorVisibility(true);
209}
210
211@end
212
213#pragma mark -
214
215static void ExchangeMethods(Class target_class,
216                            BOOL class_method,
217                            SEL original,
218                            SEL replacement) {
219  Method m1;
220  Method m2;
221  if (class_method) {
222    m1 = class_getClassMethod(target_class, original);
223    m2 = class_getClassMethod(target_class, replacement);
224  } else {
225    m1 = class_getInstanceMethod(target_class, original);
226    m2 = class_getInstanceMethod(target_class, replacement);
227  }
228  if (m1 && m2)
229    method_exchangeImplementations(m1, m2);
230  else
231    NOTREACHED() << "Cocoa swizzling failed";
232}
233
234namespace mac_plugin_interposing {
235
236void 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:));
246
247  ExchangeMethods([NSApplication class], NO, @selector(runModalForWindow:),
248                  @selector(chromePlugin_runModalForWindow:));
249
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));
257}
258
259}  // namespace mac_plugin_interposing
260