62cef3a968f3451a6651366c7a7040c76afdd775
[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 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")))
21 void 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")))
36 OpaquePluginRef GetActiveDelegate() {
37   return content::WebPluginDelegateImpl::GetActiveDelegate();
38 }
39
40 __attribute__((visibility("default")))
41 void 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")))
53 void 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")))
65 void 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
74 void 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
86 struct 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
96 static 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
103 static BOOL g_waiting_for_window_number = NO;
104
105 static 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
232 static 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
251 namespace mac_plugin_interposing {
252
253 void 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