OLD | NEW |
1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #import <Cocoa/Cocoa.h> | 5 #import <Cocoa/Cocoa.h> |
6 | 6 |
7 #include "webkit/glue/plugins/webplugin_delegate_impl.h" | 7 #include "webkit/glue/plugins/webplugin_delegate_impl.h" |
8 | 8 |
9 #include <string> | 9 #include <string> |
10 #include <vector> | 10 #include <vector> |
11 | 11 |
12 #include "base/file_util.h" | 12 #include "base/file_util.h" |
13 #include "base/lazy_instance.h" | 13 #include "base/lazy_instance.h" |
14 #include "base/message_loop.h" | 14 #include "base/message_loop.h" |
15 #include "base/stats_counters.h" | 15 #include "base/stats_counters.h" |
16 #include "base/string_util.h" | 16 #include "base/string_util.h" |
17 #include "webkit/api/public/WebInputEvent.h" | 17 #include "webkit/api/public/WebInputEvent.h" |
18 #include "webkit/default_plugin/plugin_impl.h" | 18 #include "webkit/default_plugin/plugin_impl.h" |
19 #include "webkit/glue/glue_util.h" | 19 #include "webkit/glue/glue_util.h" |
20 #include "webkit/glue/webplugin.h" | 20 #include "webkit/glue/webplugin.h" |
| 21 #include "webkit/glue/plugins/fake_plugin_window_tracker_mac.h" |
21 #include "webkit/glue/plugins/plugin_constants_win.h" | 22 #include "webkit/glue/plugins/plugin_constants_win.h" |
22 #include "webkit/glue/plugins/plugin_instance.h" | 23 #include "webkit/glue/plugins/plugin_instance.h" |
23 #include "webkit/glue/plugins/plugin_lib.h" | 24 #include "webkit/glue/plugins/plugin_lib.h" |
24 #include "webkit/glue/plugins/plugin_list.h" | 25 #include "webkit/glue/plugins/plugin_list.h" |
25 #include "webkit/glue/plugins/plugin_stream_url.h" | 26 #include "webkit/glue/plugins/plugin_stream_url.h" |
26 #include "webkit/glue/webkit_glue.h" | 27 #include "webkit/glue/webkit_glue.h" |
27 | 28 |
28 using WebKit::WebCursorInfo; | 29 using WebKit::WebCursorInfo; |
29 using WebKit::WebKeyboardEvent; | 30 using WebKit::WebKeyboardEvent; |
30 using WebKit::WebInputEvent; | 31 using WebKit::WebInputEvent; |
(...skipping 20 matching lines...) Expand all Loading... |
51 // this to keep the placeholder Carbon WindowRef's origin in sync with the | 52 // this to keep the placeholder Carbon WindowRef's origin in sync with the |
52 // actual browser window, without having to pass that geometry over IPC. If we | 53 // actual browser window, without having to pass that geometry over IPC. If we |
53 // end up needing to interpose on Carbon APIs in the plugin process (in order | 54 // end up needing to interpose on Carbon APIs in the plugin process (in order |
54 // to simulate window activation, for example), this could be replaced by | 55 // to simulate window activation, for example), this could be replaced by |
55 // interposing on GlobalToLocal and/or LocalToGlobal (see related TODO comments | 56 // interposing on GlobalToLocal and/or LocalToGlobal (see related TODO comments |
56 // below in WebPluginDelegateImpl::OnNullEvent()). | 57 // below in WebPluginDelegateImpl::OnNullEvent()). |
57 | 58 |
58 int g_current_x_offset = 0; | 59 int g_current_x_offset = 0; |
59 int g_current_y_offset = 0; | 60 int g_current_y_offset = 0; |
60 | 61 |
61 WindowRef g_last_front_window = NULL; | |
62 ProcessSerialNumber g_saved_front_process; | |
63 | |
64 } // namespace | 62 } // namespace |
65 | 63 |
66 WebPluginDelegate* WebPluginDelegate::Create( | 64 WebPluginDelegate* WebPluginDelegate::Create( |
67 const FilePath& filename, | 65 const FilePath& filename, |
68 const std::string& mime_type, | 66 const std::string& mime_type, |
69 gfx::PluginWindowHandle containing_view) { | 67 gfx::PluginWindowHandle containing_view) { |
70 scoped_refptr<NPAPI::PluginLib> plugin = | 68 scoped_refptr<NPAPI::PluginLib> plugin = |
71 NPAPI::PluginLib::CreatePluginLib(filename); | 69 NPAPI::PluginLib::CreatePluginLib(filename); |
72 if (plugin.get() == NULL) | 70 if (plugin.get() == NULL) |
73 return NULL; | 71 return NULL; |
(...skipping 20 matching lines...) Expand all Loading... |
94 handle_event_depth_(0), | 92 handle_event_depth_(0), |
95 user_gesture_message_posted_(this), | 93 user_gesture_message_posted_(this), |
96 user_gesture_msg_factory_(this), | 94 user_gesture_msg_factory_(this), |
97 null_event_factory_(this), | 95 null_event_factory_(this), |
98 last_mouse_x_(0), | 96 last_mouse_x_(0), |
99 last_mouse_y_(0) { | 97 last_mouse_y_(0) { |
100 memset(&window_, 0, sizeof(window_)); | 98 memset(&window_, 0, sizeof(window_)); |
101 } | 99 } |
102 | 100 |
103 WebPluginDelegateImpl::~WebPluginDelegateImpl() { | 101 WebPluginDelegateImpl::~WebPluginDelegateImpl() { |
| 102 FakePluginWindowTracker::SharedInstance()->RemoveFakeWindowForDelegate( |
| 103 this, cg_context_.window); |
104 DestroyInstance(); | 104 DestroyInstance(); |
105 | |
106 if (cg_context_.window) | |
107 DisposeWindow(cg_context_.window); | |
108 } | 105 } |
109 | 106 |
110 void WebPluginDelegateImpl::PluginDestroyed() { | 107 void WebPluginDelegateImpl::PluginDestroyed() { |
111 delete this; | 108 delete this; |
112 } | 109 } |
113 | 110 |
114 bool WebPluginDelegateImpl::Initialize(const GURL& url, | 111 bool WebPluginDelegateImpl::Initialize(const GURL& url, |
115 char** argn, | 112 char** argn, |
116 char** argv, | 113 char** argv, |
117 int argc, | 114 int argc, |
118 WebPlugin* plugin, | 115 WebPlugin* plugin, |
119 bool load_manually) { | 116 bool load_manually) { |
120 plugin_ = plugin; | 117 plugin_ = plugin; |
121 | 118 |
122 instance_->set_web_plugin(plugin); | 119 instance_->set_web_plugin(plugin); |
123 NPAPI::PluginInstance* old_instance = | 120 NPAPI::PluginInstance* old_instance = |
124 NPAPI::PluginInstance::SetInitializingInstance(instance_); | 121 NPAPI::PluginInstance::SetInitializingInstance(instance_); |
125 | 122 |
126 | 123 |
127 bool start_result = instance_->Start(url, argn, argv, argc, load_manually); | 124 bool start_result = instance_->Start(url, argn, argv, argc, load_manually); |
128 | 125 |
129 NPAPI::PluginInstance::SetInitializingInstance(old_instance); | 126 NPAPI::PluginInstance::SetInitializingInstance(old_instance); |
130 | 127 |
131 if (!start_result) | 128 if (!start_result) |
132 return false; | 129 return false; |
133 | 130 |
134 cg_context_.window = NULL; | 131 FakePluginWindowTracker* window_tracker = |
| 132 FakePluginWindowTracker::SharedInstance(); |
| 133 cg_context_.window = window_tracker->GenerateFakeWindowForDelegate(this); |
| 134 Rect window_bounds = { 0, 0, window_rect_.height(), window_rect_.width() }; |
| 135 SetWindowBounds(cg_context_.window, kWindowContentRgn, &window_bounds); |
135 window_.window = &cg_context_; | 136 window_.window = &cg_context_; |
136 window_.type = NPWindowTypeWindow; | 137 window_.type = NPWindowTypeWindow; |
137 | 138 |
138 plugin->SetWindow(NULL); | 139 plugin->SetWindow(NULL); |
139 plugin_url_ = url.spec(); | 140 plugin_url_ = url.spec(); |
140 | 141 |
141 MessageLoop::current()->PostDelayedTask(FROM_HERE, | 142 MessageLoop::current()->PostDelayedTask(FROM_HERE, |
142 null_event_factory_.NewRunnableMethod( | 143 null_event_factory_.NewRunnableMethod( |
143 &WebPluginDelegateImpl::OnNullEvent), | 144 &WebPluginDelegateImpl::OnNullEvent), |
144 kPluginIdleThrottleDelayMs); | 145 kPluginIdleThrottleDelayMs); |
145 return true; | 146 return true; |
146 } | 147 } |
147 | 148 |
148 void WebPluginDelegateImpl::DestroyInstance() { | 149 void WebPluginDelegateImpl::DestroyInstance() { |
149 if (instance_ && (instance_->npp()->ndata != NULL)) { | 150 if (instance_ && (instance_->npp()->ndata != NULL)) { |
150 // Shutdown all streams before destroying so that | 151 // Shutdown all streams before destroying so that |
151 // no streams are left "in progress". Need to do | 152 // no streams are left "in progress". Need to do |
152 // this before calling set_web_plugin(NULL) because the | 153 // this before calling set_web_plugin(NULL) because the |
153 // instance uses the helper to do the download. | 154 // instance uses the helper to do the download. |
154 instance_->CloseStreams(); | 155 instance_->CloseStreams(); |
155 instance_->NPP_Destroy(); | 156 instance_->NPP_Destroy(); |
156 instance_->set_web_plugin(NULL); | 157 instance_->set_web_plugin(NULL); |
157 instance_ = 0; | 158 instance_ = 0; |
158 } | 159 } |
159 } | 160 } |
160 | 161 |
161 void WebPluginDelegateImpl::UpdateGeometry( | 162 void WebPluginDelegateImpl::UpdateGeometry( |
162 const gfx::Rect& window_rect, | 163 const gfx::Rect& window_rect, |
163 const gfx::Rect& clip_rect) { | 164 const gfx::Rect& clip_rect) { |
164 | |
165 DCHECK(windowless_); | 165 DCHECK(windowless_); |
166 WindowlessUpdateGeometry(window_rect, clip_rect); | 166 WindowlessUpdateGeometry(window_rect, clip_rect); |
167 } | 167 } |
168 | 168 |
169 void WebPluginDelegateImpl::Paint(CGContextRef context, const gfx::Rect& rect) { | 169 void WebPluginDelegateImpl::Paint(CGContextRef context, const gfx::Rect& rect) { |
170 DCHECK(windowless_); | 170 DCHECK(windowless_); |
171 WindowlessPaint(context, rect); | 171 WindowlessPaint(context, rect); |
172 } | 172 } |
173 | 173 |
174 void WebPluginDelegateImpl::Print(CGContextRef context) { | 174 void WebPluginDelegateImpl::Print(CGContextRef context) { |
(...skipping 102 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
277 paint_event.where.v = 0; | 277 paint_event.where.v = 0; |
278 paint_event.modifiers = 0; | 278 paint_event.modifiers = 0; |
279 instance()->NPP_HandleEvent(&paint_event); | 279 instance()->NPP_HandleEvent(&paint_event); |
280 | 280 |
281 CGContextRestoreGState(context); | 281 CGContextRestoreGState(context); |
282 [NSGraphicsContext restoreGraphicsState]; | 282 [NSGraphicsContext restoreGraphicsState]; |
283 } | 283 } |
284 | 284 |
285 // Moves our dummy window to the given offset relative to the last known | 285 // Moves our dummy window to the given offset relative to the last known |
286 // location of the real renderer window's content view. | 286 // location of the real renderer window's content view. |
287 static void UpdateDummyWindowLocationWithOffset(WindowRef window, | 287 // If new_width or new_height is non-zero, the window size (content region) |
288 int x_offset, int y_offset) { | 288 // will be updated accordingly; if they are zero, the existing size will be |
| 289 // preserved. |
| 290 static void UpdateDummyWindowBoundsWithOffset(WindowRef window, |
| 291 int x_offset, int y_offset, |
| 292 int new_width, int new_height) { |
289 int target_x = g_current_x_offset + x_offset; | 293 int target_x = g_current_x_offset + x_offset; |
290 int target_y = g_current_y_offset + y_offset; | 294 int target_y = g_current_y_offset + y_offset; |
291 Rect window_bounds; | 295 Rect window_bounds; |
292 GetWindowBounds(window, kWindowContentRgn, &window_bounds); | 296 GetWindowBounds(window, kWindowContentRgn, &window_bounds); |
293 if ((window_bounds.left != target_x) || | 297 if ((window_bounds.left != target_x) || |
294 (window_bounds.top != target_y)) { | 298 (window_bounds.top != target_y)) { |
295 int height = window_bounds.bottom - window_bounds.top; | 299 int height = new_height ? new_height |
296 int width = window_bounds.right - window_bounds.left; | 300 : window_bounds.bottom - window_bounds.top; |
| 301 int width = new_width ? new_width |
| 302 : window_bounds.right - window_bounds.left; |
297 window_bounds.left = target_x; | 303 window_bounds.left = target_x; |
298 window_bounds.top = target_y; | 304 window_bounds.top = target_y; |
299 window_bounds.right = window_bounds.left + width; | 305 window_bounds.right = window_bounds.left + width; |
300 window_bounds.bottom = window_bounds.top + height; | 306 window_bounds.bottom = window_bounds.top + height; |
301 SetWindowBounds(window, kWindowContentRgn, &window_bounds); | 307 SetWindowBounds(window, kWindowContentRgn, &window_bounds); |
302 } | 308 } |
303 } | 309 } |
304 | 310 |
305 void WebPluginDelegateImpl::WindowlessSetWindow(bool force_set_window) { | 311 void WebPluginDelegateImpl::WindowlessSetWindow(bool force_set_window) { |
306 if (!instance()) | 312 if (!instance()) |
307 return; | 313 return; |
308 | 314 |
309 if (window_rect_.IsEmpty()) // wait for geometry to be set. | 315 if (window_rect_.IsEmpty()) // wait for geometry to be set. |
310 return; | 316 return; |
311 | 317 |
312 window_.clipRect.top = 0; | 318 window_.clipRect.top = 0; |
313 window_.clipRect.left = 0; | 319 window_.clipRect.left = 0; |
314 window_.clipRect.bottom = window_rect_.height(); | 320 window_.clipRect.bottom = window_rect_.height(); |
315 window_.clipRect.right = window_rect_.width(); | 321 window_.clipRect.right = window_rect_.width(); |
316 window_.height = window_rect_.height(); | 322 window_.height = window_rect_.height(); |
317 window_.width = window_rect_.width(); | 323 window_.width = window_rect_.width(); |
318 window_.x = 0; | 324 window_.x = 0; |
319 window_.y = 0; | 325 window_.y = 0; |
320 window_.type = NPWindowTypeWindow; | 326 window_.type = NPWindowTypeWindow; |
321 | 327 |
322 if (!force_set_window) | 328 if (!force_set_window) |
323 // Reset this flag before entering the instance in case of side-effects. | 329 // Reset this flag before entering the instance in case of side-effects. |
324 windowless_needs_set_window_ = false; | 330 windowless_needs_set_window_ = false; |
325 | 331 |
326 if (!cg_context_.window) { | 332 UpdateDummyWindowBoundsWithOffset(cg_context_.window, window_rect_.x(), |
327 // For all plugins we create a placeholder offscreen window for the use | 333 window_rect_.y(), window_rect_.width(), |
328 // of NPWindow. NPAPI on the Mac requires a Carbon WindowRef for the | 334 window_rect_.height()); |
329 // "browser window", even if we're not using the Quickdraw drawing model. | |
330 // Not having a valid window reference causes subtle bugs with plugins | |
331 // which retreive the NPWindow and validate the same. The NPWindow | |
332 // can be retreived via NPN_GetValue of NPNVnetscapeWindow. | |
333 Rect window_bounds = { 0, 0, window_rect_.height(), window_rect_.width() }; | |
334 WindowRef window_ref; | |
335 if (CreateNewWindow(kDocumentWindowClass, | |
336 kWindowStandardDocumentAttributes, | |
337 &window_bounds, | |
338 &window_ref) == noErr) { | |
339 cg_context_.window = window_ref; | |
340 SelectWindow(window_ref); | |
341 g_last_front_window = window_ref; | |
342 } | |
343 } | |
344 UpdateDummyWindowLocationWithOffset(cg_context_.window, window_rect_.x(), | |
345 window_rect_.y()); | |
346 | 335 |
347 if (!force_set_window) | 336 if (!force_set_window) |
348 windowless_needs_set_window_ = false; | 337 windowless_needs_set_window_ = false; |
349 NPError err = instance()->NPP_SetWindow(&window_); | 338 NPError err = instance()->NPP_SetWindow(&window_); |
350 DCHECK(err == NPERR_NO_ERROR); | 339 DCHECK(err == NPERR_NO_ERROR); |
351 } | 340 } |
352 | 341 |
353 void WebPluginDelegateImpl::SetFocus() { | 342 void WebPluginDelegateImpl::SetFocus() { |
354 NPEvent focus_event = { 0 }; | 343 NPEvent focus_event = { 0 }; |
355 focus_event.what = NPEventType_GetFocusEvent; | 344 focus_event.what = NPEventType_GetFocusEvent; |
(...skipping 121 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
477 return false; | 466 return false; |
478 } | 467 } |
479 | 468 |
480 static void UpdateWindowLocation(WindowRef window, const WebMouseEvent& event) { | 469 static void UpdateWindowLocation(WindowRef window, const WebMouseEvent& event) { |
481 // TODO: figure out where the vertical offset of 22 comes from (and if 22 is | 470 // TODO: figure out where the vertical offset of 22 comes from (and if 22 is |
482 // exactly right) and replace with an appropriate calculation. It feels like | 471 // exactly right) and replace with an appropriate calculation. It feels like |
483 // window structure or the menu bar, but neither should be involved here. | 472 // window structure or the menu bar, but neither should be involved here. |
484 g_current_x_offset = event.globalX - event.windowX; | 473 g_current_x_offset = event.globalX - event.windowX; |
485 g_current_y_offset = event.globalY - event.windowY + 22; | 474 g_current_y_offset = event.globalY - event.windowY + 22; |
486 | 475 |
487 UpdateDummyWindowLocationWithOffset(window, event.windowX - event.x, | 476 UpdateDummyWindowBoundsWithOffset(window, event.windowX - event.x, |
488 event.windowY - event.y); | 477 event.windowY - event.y, 0, 0); |
489 } | 478 } |
490 | 479 |
491 bool WebPluginDelegateImpl::HandleInputEvent(const WebInputEvent& event, | 480 bool WebPluginDelegateImpl::HandleInputEvent(const WebInputEvent& event, |
492 WebCursorInfo* cursor) { | 481 WebCursorInfo* cursor) { |
493 DCHECK(windowless_) << "events should only be received in windowless mode"; | 482 DCHECK(windowless_) << "events should only be received in windowless mode"; |
494 DCHECK(cursor != NULL); | 483 DCHECK(cursor != NULL); |
495 | 484 |
496 NPEvent np_event = {0}; | 485 NPEvent np_event = {0}; |
497 if (!NPEventFromWebInputEvent(event, &np_event)) { | 486 if (!NPEventFromWebInputEvent(event, &np_event)) { |
498 return false; | 487 return false; |
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
554 NPEvent np_event = {0}; | 543 NPEvent np_event = {0}; |
555 np_event.what = nullEvent; | 544 np_event.what = nullEvent; |
556 np_event.when = TickCount(); | 545 np_event.when = TickCount(); |
557 np_event.modifiers = GetCurrentKeyModifiers(); | 546 np_event.modifiers = GetCurrentKeyModifiers(); |
558 if (!Button()) | 547 if (!Button()) |
559 np_event.modifiers |= btnState; | 548 np_event.modifiers |= btnState; |
560 np_event.where.h = last_mouse_x_; | 549 np_event.where.h = last_mouse_x_; |
561 np_event.where.v = last_mouse_y_; | 550 np_event.where.v = last_mouse_y_; |
562 instance()->NPP_HandleEvent(&np_event); | 551 instance()->NPP_HandleEvent(&np_event); |
563 | 552 |
564 WindowRef front_window = FrontWindow(); | |
565 if (front_window == cg_context_.window) { | |
566 if (front_window != g_last_front_window) { | |
567 // If our dummy window is now the front window, but was not previously, | |
568 // it means that a plugin window has been destroyed. Make sure our fake | |
569 // browser window is selected. | |
570 // TODO: Use DYLD_INSERT_LIBRARIES to interpose on Carbon window | |
571 // APIs within the plugin process. This will allow us to (a) get rid of | |
572 // the dummy window, and (b) explicitly track the creation and | |
573 // destruction of windows by the plugin. | |
574 g_last_front_window = front_window; | |
575 SelectWindow(cg_context_.window); | |
576 | |
577 // If the plugin process is still the front process, bring the prior | |
578 // front process (normally this will be the browser process) back to | |
579 // the front. | |
580 // TODO: make this an IPC message so that the browser can properly | |
581 // reactivate the window. | |
582 ProcessSerialNumber this_process, front_process; | |
583 GetCurrentProcess(&this_process); | |
584 GetFrontProcess(&front_process); | |
585 Boolean matched = false; | |
586 SameProcess(&this_process, &front_process, &matched); | |
587 if (matched) | |
588 SetFrontProcess(&g_saved_front_process); | |
589 g_last_front_window = FrontWindow(); | |
590 } | |
591 } else if (front_window != g_last_front_window) { | |
592 // The plugin has just created a new window and brought it to the front. | |
593 // bring the plugin process to the front so that the user can see it (for | |
594 // example, an alert or file selection dialog). | |
595 // TODO: make this an IPC to order the plugin process above the browser | |
596 // process but not necessarily the frontmost. | |
597 ProcessSerialNumber this_process; | |
598 GetCurrentProcess(&this_process); | |
599 GetFrontProcess(&g_saved_front_process); | |
600 SetFrontProcess(&this_process); | |
601 g_last_front_window = front_window; | |
602 SelectWindow(front_window); | |
603 } | |
604 | |
605 MessageLoop::current()->PostDelayedTask(FROM_HERE, | 553 MessageLoop::current()->PostDelayedTask(FROM_HERE, |
606 null_event_factory_.NewRunnableMethod( | 554 null_event_factory_.NewRunnableMethod( |
607 &WebPluginDelegateImpl::OnNullEvent), | 555 &WebPluginDelegateImpl::OnNullEvent), |
608 kPluginIdleThrottleDelayMs); | 556 kPluginIdleThrottleDelayMs); |
609 } | 557 } |
OLD | NEW |