| 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 |