b9265c3567f19bf9d8e08cfd6ec8c601244ac763
[platform/framework/web/crosswalk.git] / src / content / plugin / plugin_interpose_util_mac.mm
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
14 using content::PluginThread;
15
16 namespace {
17
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)) {
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.
35 void 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.
49 void 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.
59 void 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
67 struct 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
77 void 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
83 BOOL g_waiting_for_window_number = NO;
84
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;
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
215 static 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
234 namespace mac_plugin_interposing {
235
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:));
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