OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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 #include "content/child/npapi/webplugin_delegate_impl.h" | 5 #include "content/child/npapi/webplugin_delegate_impl.h" |
6 | 6 |
7 #include <stdint.h> | 7 #include <stdint.h> |
8 #include <string.h> | 8 #include <string.h> |
9 | 9 |
10 #include <map> | 10 #include <map> |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
51 // Flash can easily exceed the limits of our CPU if we don't throttle it. | 51 // Flash can easily exceed the limits of our CPU if we don't throttle it. |
52 // The throttle has been chosen by testing various delays and compromising | 52 // The throttle has been chosen by testing various delays and compromising |
53 // on acceptable Flash performance and reasonable CPU consumption. | 53 // on acceptable Flash performance and reasonable CPU consumption. |
54 // | 54 // |
55 // I'd like to make the throttle delay variable, based on the amount of | 55 // I'd like to make the throttle delay variable, based on the amount of |
56 // time currently required to paint Flash plugins. There isn't a good | 56 // time currently required to paint Flash plugins. There isn't a good |
57 // way to count the time spent in aggregate plugin painting, however, so | 57 // way to count the time spent in aggregate plugin painting, however, so |
58 // this seems to work well enough. | 58 // this seems to work well enough. |
59 const int kFlashWMUSERMessageThrottleDelayMs = 5; | 59 const int kFlashWMUSERMessageThrottleDelayMs = 5; |
60 | 60 |
61 // Flash displays popups in response to user clicks by posting a WM_USER | |
62 // message to the plugin window. The handler for this message displays | |
63 // the popup. To ensure that the popups allowed state is sent correctly | |
64 // to the renderer we reset the popups allowed state in a timer. | |
65 const int kWindowedPluginPopupTimerMs = 50; | |
66 | |
67 // The current instance of the plugin which entered the modal loop. | 61 // The current instance of the plugin which entered the modal loop. |
68 WebPluginDelegateImpl* g_current_plugin_instance = NULL; | 62 WebPluginDelegateImpl* g_current_plugin_instance = NULL; |
69 | 63 |
70 typedef std::deque<MSG> ThrottleQueue; | 64 typedef std::deque<MSG> ThrottleQueue; |
71 base::LazyInstance<ThrottleQueue> g_throttle_queue = LAZY_INSTANCE_INITIALIZER; | 65 base::LazyInstance<ThrottleQueue> g_throttle_queue = LAZY_INSTANCE_INITIALIZER; |
72 | 66 |
73 base::LazyInstance<std::map<HWND, WNDPROC> > g_window_handle_proc_map = | 67 base::LazyInstance<std::map<HWND, WNDPROC> > g_window_handle_proc_map = |
74 LAZY_INSTANCE_INITIALIZER; | 68 LAZY_INSTANCE_INITIALIZER; |
75 | 69 |
76 // Helper object for patching the TrackPopupMenu API. | 70 // Helper object for patching the TrackPopupMenu API. |
(...skipping 128 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
205 LRESULT CALLBACK WebPluginDelegateImpl::HandleEventMessageFilterHook( | 199 LRESULT CALLBACK WebPluginDelegateImpl::HandleEventMessageFilterHook( |
206 int code, WPARAM wParam, LPARAM lParam) { | 200 int code, WPARAM wParam, LPARAM lParam) { |
207 if (g_current_plugin_instance) { | 201 if (g_current_plugin_instance) { |
208 g_current_plugin_instance->OnModalLoopEntered(); | 202 g_current_plugin_instance->OnModalLoopEntered(); |
209 } else { | 203 } else { |
210 NOTREACHED(); | 204 NOTREACHED(); |
211 } | 205 } |
212 return CallNextHookEx(NULL, code, wParam, lParam); | 206 return CallNextHookEx(NULL, code, wParam, lParam); |
213 } | 207 } |
214 | 208 |
215 LRESULT CALLBACK WebPluginDelegateImpl::MouseHookProc( | |
216 int code, WPARAM wParam, LPARAM lParam) { | |
217 if (code == HC_ACTION) { | |
218 MOUSEHOOKSTRUCT* hook_struct = reinterpret_cast<MOUSEHOOKSTRUCT*>(lParam); | |
219 if (hook_struct) | |
220 HandleCaptureForMessage(hook_struct->hwnd, wParam); | |
221 } | |
222 | |
223 return CallNextHookEx(NULL, code, wParam, lParam); | |
224 } | |
225 | |
226 WebPluginDelegateImpl::WebPluginDelegateImpl(WebPlugin* plugin, | 209 WebPluginDelegateImpl::WebPluginDelegateImpl(WebPlugin* plugin, |
227 PluginInstance* instance) | 210 PluginInstance* instance) |
228 : windowed_handle_(NULL), | 211 : plugin_(plugin), |
229 windowed_did_set_window_(false), | |
230 windowless_(false), | |
231 plugin_(plugin), | |
232 instance_(instance), | 212 instance_(instance), |
233 plugin_wnd_proc_(NULL), | |
234 last_message_(0), | |
235 is_calling_wndproc(false), | |
236 quirks_(0), | 213 quirks_(0), |
237 dummy_window_for_activation_(NULL), | 214 dummy_window_for_activation_(NULL), |
238 dummy_window_parent_(NULL), | 215 dummy_window_parent_(NULL), |
239 old_dummy_window_proc_(NULL), | 216 old_dummy_window_proc_(NULL), |
240 handle_event_message_filter_hook_(NULL), | 217 handle_event_message_filter_hook_(NULL), |
241 handle_event_pump_messages_event_(NULL), | 218 handle_event_pump_messages_event_(NULL), |
242 user_gesture_message_posted_(false), | |
243 mouse_hook_(NULL), | |
244 handle_event_depth_(0), | 219 handle_event_depth_(0), |
245 first_set_window_call_(true), | 220 first_set_window_call_(true), |
246 plugin_has_focus_(false), | 221 plugin_has_focus_(false), |
247 has_webkit_focus_(false), | 222 has_webkit_focus_(false), |
248 containing_view_has_focus_(true), | 223 containing_view_has_focus_(true), |
249 creation_succeeded_(false), | 224 creation_succeeded_(false), |
250 user_gesture_msg_factory_(this) { | 225 user_gesture_msg_factory_(this) { |
251 memset(&window_, 0, sizeof(window_)); | 226 memset(&window_, 0, sizeof(window_)); |
252 | 227 |
253 const WebPluginInfo& plugin_info = instance_->plugin_lib()->plugin_info(); | 228 const WebPluginInfo& plugin_info = instance_->plugin_lib()->plugin_info(); |
(...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
326 if (current_wnd_proc == DummyWindowProc) { | 301 if (current_wnd_proc == DummyWindowProc) { |
327 SetWindowLongPtr(dummy_window_for_activation_, | 302 SetWindowLongPtr(dummy_window_for_activation_, |
328 GWLP_WNDPROC, | 303 GWLP_WNDPROC, |
329 reinterpret_cast<LONG_PTR>(old_dummy_window_proc_)); | 304 reinterpret_cast<LONG_PTR>(old_dummy_window_proc_)); |
330 } | 305 } |
331 ::DestroyWindow(dummy_window_for_activation_); | 306 ::DestroyWindow(dummy_window_for_activation_); |
332 } | 307 } |
333 | 308 |
334 DestroyInstance(); | 309 DestroyInstance(); |
335 | 310 |
336 if (!windowless_) | |
337 WindowedDestroyWindow(); | |
338 | |
339 if (handle_event_pump_messages_event_) { | 311 if (handle_event_pump_messages_event_) { |
340 CloseHandle(handle_event_pump_messages_event_); | 312 CloseHandle(handle_event_pump_messages_event_); |
341 } | 313 } |
342 } | 314 } |
343 | 315 |
344 bool WebPluginDelegateImpl::PlatformInitialize() { | 316 bool WebPluginDelegateImpl::PlatformInitialize() { |
345 plugin_->SetWindow(windowed_handle_); | 317 CreateDummyWindowForActivation(); |
346 | 318 handle_event_pump_messages_event_ = CreateEvent(NULL, TRUE, FALSE, NULL); |
347 if (windowless_) { | 319 plugin_->SetWindowlessData( |
348 CreateDummyWindowForActivation(); | 320 handle_event_pump_messages_event_, |
349 handle_event_pump_messages_event_ = CreateEvent(NULL, TRUE, FALSE, NULL); | 321 reinterpret_cast<gfx::NativeViewId>(dummy_window_for_activation_)); |
350 plugin_->SetWindowlessData( | |
351 handle_event_pump_messages_event_, | |
352 reinterpret_cast<gfx::NativeViewId>(dummy_window_for_activation_)); | |
353 } | |
354 | 322 |
355 // Windowless plugins call the WindowFromPoint API and passes the result of | 323 // Windowless plugins call the WindowFromPoint API and passes the result of |
356 // that to the TrackPopupMenu API call as the owner window. This causes the | 324 // that to the TrackPopupMenu API call as the owner window. This causes the |
357 // API to fail as the API expects the window handle to live on the same | 325 // API to fail as the API expects the window handle to live on the same |
358 // thread as the caller. It works in the other browsers as the plugin lives | 326 // thread as the caller. It works in the other browsers as the plugin lives |
359 // on the browser thread. Our workaround is to intercept the TrackPopupMenu | 327 // on the browser thread. Our workaround is to intercept the TrackPopupMenu |
360 // API and replace the window handle with the dummy activation window. | 328 // API and replace the window handle with the dummy activation window. |
361 if (windowless_ && !g_iat_patch_track_popup_menu.Pointer()->is_patched()) { | 329 if (!g_iat_patch_track_popup_menu.Pointer()->is_patched()) { |
362 g_iat_patch_track_popup_menu.Pointer()->Patch( | 330 g_iat_patch_track_popup_menu.Pointer()->Patch( |
363 GetPluginPath().value().c_str(), "user32.dll", "TrackPopupMenu", | 331 GetPluginPath().value().c_str(), "user32.dll", "TrackPopupMenu", |
364 WebPluginDelegateImpl::TrackPopupMenuPatch); | 332 WebPluginDelegateImpl::TrackPopupMenuPatch); |
365 } | 333 } |
366 | 334 |
367 // Windowless plugins can set cursors by calling the SetCursor API. This | 335 // Windowless plugins can set cursors by calling the SetCursor API. This |
368 // works because the thread inputs of the browser UI thread and the plugin | 336 // works because the thread inputs of the browser UI thread and the plugin |
369 // thread are attached. We intercept the SetCursor API for windowless | 337 // thread are attached. We intercept the SetCursor API for windowless |
370 // plugins and remember the cursor being set. This is shipped over to the | 338 // plugins and remember the cursor being set. This is shipped over to the |
371 // browser in the HandleEvent call, which ensures that the cursor does not | 339 // browser in the HandleEvent call, which ensures that the cursor does not |
372 // change when a windowless plugin instance changes the cursor | 340 // change when a windowless plugin instance changes the cursor |
373 // in a background tab. | 341 // in a background tab. |
374 if (windowless_ && !g_iat_patch_set_cursor.Pointer()->is_patched() && | 342 if (!g_iat_patch_set_cursor.Pointer()->is_patched() && |
375 (quirks_ & PLUGIN_QUIRK_PATCH_SETCURSOR)) { | 343 (quirks_ & PLUGIN_QUIRK_PATCH_SETCURSOR)) { |
376 g_iat_patch_set_cursor.Pointer()->Patch( | 344 g_iat_patch_set_cursor.Pointer()->Patch( |
377 GetPluginPath().value().c_str(), "user32.dll", "SetCursor", | 345 GetPluginPath().value().c_str(), "user32.dll", "SetCursor", |
378 WebPluginDelegateImpl::SetCursorPatch); | 346 WebPluginDelegateImpl::SetCursorPatch); |
379 } | 347 } |
380 | 348 |
381 // The windowed flash plugin has a bug which occurs when the plugin enters | |
382 // fullscreen mode. It basically captures the mouse on WM_LBUTTONDOWN and | |
383 // does not release capture correctly causing it to stop receiving | |
384 // subsequent mouse events. This problem is also seen in Safari where there | |
385 // is code to handle this in the wndproc. However the plugin subclasses the | |
386 // window again in WM_LBUTTONDOWN before entering full screen. As a result | |
387 // Safari does not receive the WM_LBUTTONUP message. To workaround this | |
388 // issue we use a per thread mouse hook. This bug does not occur in Firefox | |
389 // and opera. Firefox has code similar to Safari. It could well be a bug in | |
390 // the flash plugin, which only occurs in webkit based browsers. | |
391 if (quirks_ & PLUGIN_QUIRK_HANDLE_MOUSE_CAPTURE) { | |
392 mouse_hook_ = SetWindowsHookEx(WH_MOUSE, MouseHookProc, NULL, | |
393 GetCurrentThreadId()); | |
394 } | |
395 | |
396 // On XP, WMP will use its old UI unless a registry key under HKLM has the | 349 // On XP, WMP will use its old UI unless a registry key under HKLM has the |
397 // name of the current process. We do it in the installer for admin users, | 350 // name of the current process. We do it in the installer for admin users, |
398 // for the rest patch this function. | 351 // for the rest patch this function. |
399 if ((quirks_ & PLUGIN_QUIRK_PATCH_REGENUMKEYEXW) && | 352 if ((quirks_ & PLUGIN_QUIRK_PATCH_REGENUMKEYEXW) && |
400 base::win::GetVersion() == base::win::VERSION_XP && | 353 base::win::GetVersion() == base::win::VERSION_XP && |
401 (base::win::RegKey().Open(HKEY_LOCAL_MACHINE, | 354 (base::win::RegKey().Open(HKEY_LOCAL_MACHINE, |
402 L"SOFTWARE\\Microsoft\\MediaPlayer\\ShimInclusionList\\chrome.exe", | 355 L"SOFTWARE\\Microsoft\\MediaPlayer\\ShimInclusionList\\chrome.exe", |
403 KEY_READ) != ERROR_SUCCESS) && | 356 KEY_READ) != ERROR_SUCCESS) && |
404 !g_iat_patch_reg_enum_key_ex_w.Pointer()->is_patched()) { | 357 !g_iat_patch_reg_enum_key_ex_w.Pointer()->is_patched()) { |
405 g_iat_patch_reg_enum_key_ex_w.Pointer()->Patch( | 358 g_iat_patch_reg_enum_key_ex_w.Pointer()->Patch( |
406 L"wmpdxm.dll", "advapi32.dll", "RegEnumKeyExW", | 359 L"wmpdxm.dll", "advapi32.dll", "RegEnumKeyExW", |
407 WebPluginDelegateImpl::RegEnumKeyExWPatch); | 360 WebPluginDelegateImpl::RegEnumKeyExWPatch); |
408 } | 361 } |
409 | 362 |
410 // Flash retrieves the pointers to IMM32 functions with GetProcAddress() calls | 363 // Flash retrieves the pointers to IMM32 functions with GetProcAddress() calls |
411 // and use them to retrieve IME data. We add a patch to this function so we | 364 // and use them to retrieve IME data. We add a patch to this function so we |
412 // can dispatch these IMM32 calls to the WebPluginIMEWin class, which emulates | 365 // can dispatch these IMM32 calls to the WebPluginIMEWin class, which emulates |
413 // IMM32 functions for Flash. | 366 // IMM32 functions for Flash. |
414 if (!g_iat_patch_get_proc_address.Pointer()->is_patched() && | 367 if (!g_iat_patch_get_proc_address.Pointer()->is_patched() && |
415 (quirks_ & PLUGIN_QUIRK_EMULATE_IME)) { | 368 (quirks_ & PLUGIN_QUIRK_EMULATE_IME)) { |
416 g_iat_patch_get_proc_address.Pointer()->Patch( | 369 g_iat_patch_get_proc_address.Pointer()->Patch( |
417 GetPluginPath().value().c_str(), "kernel32.dll", "GetProcAddress", | 370 GetPluginPath().value().c_str(), "kernel32.dll", "GetProcAddress", |
418 GetProcAddressPatch); | 371 GetProcAddressPatch); |
419 } | 372 } |
420 | 373 |
421 if (windowless_ && !g_iat_patch_window_from_point.Pointer()->is_patched() && | 374 if (!g_iat_patch_window_from_point.Pointer()->is_patched() && |
422 (quirks_ & PLUGIN_QUIRK_FAKE_WINDOW_FROM_POINT)) { | 375 (quirks_ & PLUGIN_QUIRK_FAKE_WINDOW_FROM_POINT)) { |
423 g_iat_patch_window_from_point.Pointer()->Patch( | 376 g_iat_patch_window_from_point.Pointer()->Patch( |
424 GetPluginPath().value().c_str(), "user32.dll", "WindowFromPoint", | 377 GetPluginPath().value().c_str(), "user32.dll", "WindowFromPoint", |
425 WebPluginDelegateImpl::WindowFromPointPatch); | 378 WebPluginDelegateImpl::WindowFromPointPatch); |
426 } | 379 } |
427 | 380 |
428 return true; | 381 return true; |
429 } | 382 } |
430 | 383 |
431 void WebPluginDelegateImpl::PlatformDestroyInstance() { | 384 void WebPluginDelegateImpl::PlatformDestroyInstance() { |
432 if (!instance_->plugin_lib()) | 385 if (!instance_->plugin_lib()) |
433 return; | 386 return; |
434 | 387 |
435 // Unpatch if this is the last plugin instance. | 388 // Unpatch if this is the last plugin instance. |
436 if (instance_->plugin_lib()->instance_count() != 1) | 389 if (instance_->plugin_lib()->instance_count() != 1) |
437 return; | 390 return; |
438 | 391 |
439 if (g_iat_patch_set_cursor.Pointer()->is_patched()) | 392 if (g_iat_patch_set_cursor.Pointer()->is_patched()) |
440 g_iat_patch_set_cursor.Pointer()->Unpatch(); | 393 g_iat_patch_set_cursor.Pointer()->Unpatch(); |
441 | 394 |
442 if (g_iat_patch_track_popup_menu.Pointer()->is_patched()) | 395 if (g_iat_patch_track_popup_menu.Pointer()->is_patched()) |
443 g_iat_patch_track_popup_menu.Pointer()->Unpatch(); | 396 g_iat_patch_track_popup_menu.Pointer()->Unpatch(); |
444 | 397 |
445 if (g_iat_patch_reg_enum_key_ex_w.Pointer()->is_patched()) | 398 if (g_iat_patch_reg_enum_key_ex_w.Pointer()->is_patched()) |
446 g_iat_patch_reg_enum_key_ex_w.Pointer()->Unpatch(); | 399 g_iat_patch_reg_enum_key_ex_w.Pointer()->Unpatch(); |
447 | 400 |
448 if (g_iat_patch_window_from_point.Pointer()->is_patched()) | 401 if (g_iat_patch_window_from_point.Pointer()->is_patched()) |
449 g_iat_patch_window_from_point.Pointer()->Unpatch(); | 402 g_iat_patch_window_from_point.Pointer()->Unpatch(); |
450 | |
451 if (mouse_hook_) { | |
452 UnhookWindowsHookEx(mouse_hook_); | |
453 mouse_hook_ = NULL; | |
454 } | |
455 } | 403 } |
456 | 404 |
457 void WebPluginDelegateImpl::Paint(SkCanvas* canvas, const gfx::Rect& rect) { | 405 void WebPluginDelegateImpl::Paint(SkCanvas* canvas, const gfx::Rect& rect) { |
458 if (windowless_ && skia::SupportsPlatformPaint(canvas)) { | 406 if (skia::SupportsPlatformPaint(canvas)) { |
459 skia::ScopedPlatformPaint scoped_platform_paint(canvas); | 407 skia::ScopedPlatformPaint scoped_platform_paint(canvas); |
460 HDC hdc = scoped_platform_paint.GetPlatformSurface(); | 408 HDC hdc = scoped_platform_paint.GetPlatformSurface(); |
461 WindowlessPaint(hdc, rect); | 409 WindowlessPaint(hdc, rect); |
462 } | 410 } |
463 } | 411 } |
464 | 412 |
465 bool WebPluginDelegateImpl::WindowedCreatePlugin() { | |
466 DCHECK(!windowed_handle_); | |
467 | |
468 RegisterNativeWindowClass(); | |
469 | |
470 // The window will be sized and shown later. | |
471 windowed_handle_ = CreateWindowEx( | |
472 WS_EX_LEFT | WS_EX_LTRREADING | WS_EX_RIGHTSCROLLBAR, | |
473 kNativeWindowClassName, | |
474 0, | |
475 WS_POPUP | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, | |
476 0, | |
477 0, | |
478 0, | |
479 0, | |
480 GetDesktopWindow(), | |
481 0, | |
482 GetModuleHandle(NULL), | |
483 0); | |
484 if (windowed_handle_ == 0) | |
485 return false; | |
486 | |
487 // This is a tricky workaround for Issue 2673 in chromium "Flash: IME not | |
488 // available". To use IMEs in this window, we have to make Windows attach | |
489 // IMEs to this window (i.e. load IME DLLs, attach them to this process, and | |
490 // add their message hooks to this window). Windows attaches IMEs while this | |
491 // process creates a top-level window. On the other hand, to layout this | |
492 // window correctly in the given parent window (RenderWidgetHostViewWin or | |
493 // RenderWidgetHostViewAura), this window should be a child window of the | |
494 // parent window. To satisfy both of the above conditions, this code once | |
495 // creates a top-level window and change it to a child window of the parent | |
496 // window (in the browser process). | |
497 SetWindowLongPtr(windowed_handle_, GWL_STYLE, | |
498 WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS); | |
499 | |
500 BOOL result = SetProp(windowed_handle_, kWebPluginDelegateProperty, this); | |
501 DCHECK(result == TRUE) << "SetProp failed, last error = " << GetLastError(); | |
502 | |
503 // Calling SetWindowLongPtrA here makes the window proc ASCII, which is | |
504 // required by at least the Shockwave Director plugin. | |
505 SetWindowLongPtrA(windowed_handle_, | |
506 GWLP_WNDPROC, | |
507 reinterpret_cast<LONG_PTR>(DefWindowProcA)); | |
508 | |
509 return true; | |
510 } | |
511 | |
512 void WebPluginDelegateImpl::WindowedDestroyWindow() { | |
513 if (windowed_handle_ != NULL) { | |
514 // Unsubclass the window. | |
515 WNDPROC current_wnd_proc = reinterpret_cast<WNDPROC>( | |
516 GetWindowLongPtr(windowed_handle_, GWLP_WNDPROC)); | |
517 if (current_wnd_proc == NativeWndProc) { | |
518 SetWindowLongPtr(windowed_handle_, | |
519 GWLP_WNDPROC, | |
520 reinterpret_cast<LONG_PTR>(plugin_wnd_proc_)); | |
521 } | |
522 | |
523 plugin_->WillDestroyWindow(windowed_handle_); | |
524 | |
525 DestroyWindow(windowed_handle_); | |
526 windowed_handle_ = 0; | |
527 } | |
528 } | |
529 | |
530 // Erase all messages in the queue destined for a particular window. | 413 // Erase all messages in the queue destined for a particular window. |
531 // When windows are closing, callers should use this function to clear | 414 // When windows are closing, callers should use this function to clear |
532 // the queue. | 415 // the queue. |
533 // static | 416 // static |
534 void WebPluginDelegateImpl::ClearThrottleQueueForWindow(HWND window) { | 417 void WebPluginDelegateImpl::ClearThrottleQueueForWindow(HWND window) { |
535 ThrottleQueue* throttle_queue = g_throttle_queue.Pointer(); | 418 ThrottleQueue* throttle_queue = g_throttle_queue.Pointer(); |
536 | 419 |
537 ThrottleQueue::iterator it; | 420 ThrottleQueue::iterator it; |
538 for (it = throttle_queue->begin(); it != throttle_queue->end(); ) { | 421 for (it = throttle_queue->begin(); it != throttle_queue->end(); ) { |
539 if (it->hwnd == window) { | 422 if (it->hwnd == window) { |
(...skipping 208 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
748 reinterpret_cast<LPARAM>( | 631 reinterpret_cast<LPARAM>( |
749 &WebPluginDelegateImpl::FlashWindowlessWndProc))) { | 632 &WebPluginDelegateImpl::FlashWindowlessWndProc))) { |
750 // Log that this happened. Flash will still work; it just means the | 633 // Log that this happened. Flash will still work; it just means the |
751 // throttle isn't installed (and Flash will use more CPU). | 634 // throttle isn't installed (and Flash will use more CPU). |
752 NOTREACHED(); | 635 NOTREACHED(); |
753 LOG(ERROR) << "Failed to wrap all windowless Flash windows"; | 636 LOG(ERROR) << "Failed to wrap all windowless Flash windows"; |
754 } | 637 } |
755 return true; | 638 return true; |
756 } | 639 } |
757 | 640 |
758 bool WebPluginDelegateImpl::WindowedReposition( | |
759 const gfx::Rect& window_rect_in_dip, | |
760 const gfx::Rect& clip_rect_in_dip) { | |
761 if (!windowed_handle_) { | |
762 NOTREACHED(); | |
763 return false; | |
764 } | |
765 | |
766 gfx::Rect window_rect = gfx::win::DIPToScreenRect(window_rect_in_dip); | |
767 gfx::Rect clip_rect = gfx::win::DIPToScreenRect(clip_rect_in_dip); | |
768 if (window_rect_ == window_rect && clip_rect_ == clip_rect) | |
769 return false; | |
770 | |
771 // We only set the plugin's size here. Its position is moved elsewhere, which | |
772 // allows the window moves/scrolling/clipping to be synchronized with the page | |
773 // and other windows. | |
774 // If the plugin window has no parent, then don't focus it because it isn't | |
775 // being displayed anywhere. See: | |
776 // http://code.google.com/p/chromium/issues/detail?id=32658 | |
777 if (window_rect.size() != window_rect_.size()) { | |
778 UINT flags = SWP_NOMOVE | SWP_NOZORDER; | |
779 if (!GetParent(windowed_handle_)) | |
780 flags |= SWP_NOACTIVATE; | |
781 ::SetWindowPos(windowed_handle_, | |
782 NULL, | |
783 0, | |
784 0, | |
785 window_rect.width(), | |
786 window_rect.height(), | |
787 flags); | |
788 } | |
789 | |
790 window_rect_ = window_rect; | |
791 clip_rect_ = clip_rect; | |
792 | |
793 // Ensure that the entire window gets repainted. | |
794 ::InvalidateRect(windowed_handle_, NULL, FALSE); | |
795 | |
796 return true; | |
797 } | |
798 | |
799 void WebPluginDelegateImpl::WindowedSetWindow() { | |
800 if (!instance_.get()) | |
801 return; | |
802 | |
803 if (!windowed_handle_) { | |
804 NOTREACHED(); | |
805 return; | |
806 } | |
807 | |
808 instance()->set_window_handle(windowed_handle_); | |
809 | |
810 DCHECK(!instance()->windowless()); | |
811 | |
812 window_.clipRect.top = std::max(0, clip_rect_.y()); | |
813 window_.clipRect.left = std::max(0, clip_rect_.x()); | |
814 window_.clipRect.bottom = std::max(0, clip_rect_.y() + clip_rect_.height()); | |
815 window_.clipRect.right = std::max(0, clip_rect_.x() + clip_rect_.width()); | |
816 window_.height = window_rect_.height(); | |
817 window_.width = window_rect_.width(); | |
818 window_.x = 0; | |
819 window_.y = 0; | |
820 | |
821 window_.window = windowed_handle_; | |
822 window_.type = NPWindowTypeWindow; | |
823 | |
824 // Reset this flag before entering the instance in case of side-effects. | |
825 windowed_did_set_window_ = true; | |
826 | |
827 instance()->NPP_SetWindow(&window_); | |
828 if (quirks_ & PLUGIN_QUIRK_SETWINDOW_TWICE) | |
829 instance()->NPP_SetWindow(&window_); | |
830 | |
831 WNDPROC current_wnd_proc = reinterpret_cast<WNDPROC>( | |
832 GetWindowLongPtr(windowed_handle_, GWLP_WNDPROC)); | |
833 if (current_wnd_proc != NativeWndProc) { | |
834 plugin_wnd_proc_ = reinterpret_cast<WNDPROC>( | |
835 SetWindowLongPtr(windowed_handle_, | |
836 GWLP_WNDPROC, | |
837 reinterpret_cast<LONG_PTR>(NativeWndProc))); | |
838 } | |
839 } | |
840 | |
841 ATOM WebPluginDelegateImpl::RegisterNativeWindowClass() { | |
842 static bool have_registered_window_class = false; | |
843 if (have_registered_window_class == true) | |
844 return true; | |
845 | |
846 have_registered_window_class = true; | |
847 | |
848 WNDCLASSEX wcex; | |
849 wcex.cbSize = sizeof(WNDCLASSEX); | |
850 wcex.style = CS_DBLCLKS; | |
851 wcex.lpfnWndProc = WrapperWindowProc; | |
852 wcex.cbClsExtra = 0; | |
853 wcex.cbWndExtra = 0; | |
854 wcex.hInstance = GetModuleHandle(NULL); | |
855 wcex.hIcon = 0; | |
856 wcex.hCursor = 0; | |
857 // Some plugins like windows media player 11 create child windows parented | |
858 // by our plugin window, where the media content is rendered. These plugins | |
859 // dont implement WM_ERASEBKGND, which causes painting issues, when the | |
860 // window where the media is rendered is moved around. DefWindowProc does | |
861 // implement WM_ERASEBKGND correctly if we have a valid background brush. | |
862 wcex.hbrBackground = reinterpret_cast<HBRUSH>(COLOR_WINDOW+1); | |
863 wcex.lpszMenuName = 0; | |
864 wcex.lpszClassName = kNativeWindowClassName; | |
865 wcex.hIconSm = 0; | |
866 | |
867 return RegisterClassEx(&wcex); | |
868 } | |
869 | |
870 LRESULT CALLBACK WebPluginDelegateImpl::WrapperWindowProc( | |
871 HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { | |
872 // This is another workaround for Issue 2673 in chromium "Flash: IME not | |
873 // available". Somehow, the CallWindowProc() function does not dispatch | |
874 // window messages when its first parameter is a handle representing the | |
875 // DefWindowProc() function. To avoid this problem, this code creates a | |
876 // wrapper function which just encapsulates the DefWindowProc() function | |
877 // and set it as the window procedure of a windowed plugin. | |
878 return DefWindowProc(hWnd, message, wParam, lParam); | |
879 } | |
880 | |
881 // Returns true if the message passed in corresponds to a user gesture. | 641 // Returns true if the message passed in corresponds to a user gesture. |
882 static bool IsUserGestureMessage(unsigned int message) { | 642 static bool IsUserGestureMessage(unsigned int message) { |
883 switch (message) { | 643 switch (message) { |
884 case WM_LBUTTONDOWN: | 644 case WM_LBUTTONDOWN: |
885 case WM_LBUTTONUP: | 645 case WM_LBUTTONUP: |
886 case WM_RBUTTONDOWN: | 646 case WM_RBUTTONDOWN: |
887 case WM_RBUTTONUP: | 647 case WM_RBUTTONUP: |
888 case WM_MBUTTONDOWN: | 648 case WM_MBUTTONDOWN: |
889 case WM_MBUTTONUP: | 649 case WM_MBUTTONUP: |
890 case WM_KEYDOWN: | 650 case WM_KEYDOWN: |
891 case WM_KEYUP: | 651 case WM_KEYUP: |
892 return true; | 652 return true; |
893 | 653 |
894 default: | 654 default: |
895 break; | 655 break; |
896 } | 656 } |
897 | 657 |
898 return false; | 658 return false; |
899 } | 659 } |
900 | 660 |
901 LRESULT CALLBACK WebPluginDelegateImpl::NativeWndProc( | |
902 HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) { | |
903 WebPluginDelegateImpl* delegate = reinterpret_cast<WebPluginDelegateImpl*>( | |
904 GetProp(hwnd, kWebPluginDelegateProperty)); | |
905 if (!delegate) { | |
906 NOTREACHED(); | |
907 return 0; | |
908 } | |
909 | |
910 if (message == delegate->last_message_ && | |
911 delegate->GetQuirks() & PLUGIN_QUIRK_DONT_CALL_WND_PROC_RECURSIVELY && | |
912 delegate->is_calling_wndproc) { | |
913 // Real may go into a state where it recursively dispatches the same event | |
914 // when subclassed. See https://bugzilla.mozilla.org/show_bug.cgi?id=192914 | |
915 // We only do the recursive check for Real because it's possible and valid | |
916 // for a plugin to synchronously dispatch a message to itself such that it | |
917 // looks like it's in recursion. | |
918 return TRUE; | |
919 } | |
920 | |
921 // Flash may flood the message queue with WM_USER+1 message causing 100% CPU | |
922 // usage. See https://bugzilla.mozilla.org/show_bug.cgi?id=132759. We | |
923 // prevent this by throttling the messages. | |
924 if (message == WM_USER + 1 && | |
925 delegate->GetQuirks() & PLUGIN_QUIRK_THROTTLE_WM_USER_PLUS_ONE) { | |
926 WebPluginDelegateImpl::ThrottleMessage(delegate->plugin_wnd_proc_, hwnd, | |
927 message, wparam, lparam); | |
928 return FALSE; | |
929 } | |
930 | |
931 LRESULT result; | |
932 uint32_t old_message = delegate->last_message_; | |
933 delegate->last_message_ = message; | |
934 | |
935 static UINT custom_msg = RegisterWindowMessage(kPaintMessageName); | |
936 if (message == custom_msg) { | |
937 // Get the invalid rect which is in screen coordinates and convert to | |
938 // window coordinates. | |
939 gfx::Rect invalid_rect; | |
940 invalid_rect.set_x(static_cast<short>(LOWORD(wparam))); | |
941 invalid_rect.set_y(static_cast<short>(HIWORD(wparam))); | |
942 invalid_rect.set_width(static_cast<short>(LOWORD(lparam))); | |
943 invalid_rect.set_height(static_cast<short>(HIWORD(lparam))); | |
944 | |
945 RECT window_rect; | |
946 GetWindowRect(hwnd, &window_rect); | |
947 invalid_rect.Offset(-window_rect.left, -window_rect.top); | |
948 | |
949 // The plugin window might have non-client area. If we don't pass in | |
950 // RDW_FRAME then the children don't receive WM_NCPAINT messages while | |
951 // scrolling, which causes painting problems (http://b/issue?id=923945). | |
952 uint32_t flags = RDW_INVALIDATE | RDW_ALLCHILDREN | RDW_FRAME; | |
953 | |
954 // If a plugin (like Google Earth or Java) has child windows that are hosted | |
955 // in a different process, then RedrawWindow with UPDATENOW will | |
956 // synchronously wait for this call to complete. Some messages are pumped | |
957 // but not others, which could lead to a deadlock. So avoid reentrancy by | |
958 // only synchronously calling RedrawWindow once at a time. | |
959 if (old_message != custom_msg) | |
960 flags |= RDW_UPDATENOW; | |
961 RECT rect = invalid_rect.ToRECT(); | |
962 RedrawWindow(hwnd, &rect, NULL, flags); | |
963 result = FALSE; | |
964 } else { | |
965 delegate->is_calling_wndproc = true; | |
966 | |
967 if (!delegate->user_gesture_message_posted_ && | |
968 IsUserGestureMessage(message)) { | |
969 delegate->user_gesture_message_posted_ = true; | |
970 | |
971 delegate->instance()->PushPopupsEnabledState(true); | |
972 | |
973 base::MessageLoop::current()->PostDelayedTask( | |
974 FROM_HERE, | |
975 base::Bind(&WebPluginDelegateImpl::OnUserGestureEnd, | |
976 delegate->user_gesture_msg_factory_.GetWeakPtr()), | |
977 base::TimeDelta::FromMilliseconds(kWindowedPluginPopupTimerMs)); | |
978 } | |
979 | |
980 HandleCaptureForMessage(hwnd, message); | |
981 | |
982 // Maintain a local/global stack for the g_current_plugin_instance variable | |
983 // as this may be a nested invocation. | |
984 WebPluginDelegateImpl* last_plugin_instance = g_current_plugin_instance; | |
985 | |
986 g_current_plugin_instance = delegate; | |
987 | |
988 result = CallWindowProc( | |
989 delegate->plugin_wnd_proc_, hwnd, message, wparam, lparam); | |
990 | |
991 // The plugin instance may have been destroyed in the CallWindowProc call | |
992 // above. This will also destroy the plugin window. Before attempting to | |
993 // access the WebPluginDelegateImpl instance we validate if the window is | |
994 // still valid. | |
995 if (::IsWindow(hwnd)) | |
996 delegate->is_calling_wndproc = false; | |
997 | |
998 g_current_plugin_instance = last_plugin_instance; | |
999 | |
1000 if (message == WM_NCDESTROY) { | |
1001 RemoveProp(hwnd, kWebPluginDelegateProperty); | |
1002 ClearThrottleQueueForWindow(hwnd); | |
1003 } | |
1004 } | |
1005 if (::IsWindow(hwnd)) | |
1006 delegate->last_message_ = old_message; | |
1007 return result; | |
1008 } | |
1009 | |
1010 void WebPluginDelegateImpl::WindowlessUpdateGeometry( | 661 void WebPluginDelegateImpl::WindowlessUpdateGeometry( |
1011 const gfx::Rect& window_rect, | 662 const gfx::Rect& window_rect, |
1012 const gfx::Rect& clip_rect) { | 663 const gfx::Rect& clip_rect) { |
1013 bool window_rect_changed = (window_rect_ != window_rect); | 664 bool window_rect_changed = (window_rect_ != window_rect); |
1014 // Only resend to the instance if the geometry has changed. | 665 // Only resend to the instance if the geometry has changed. |
1015 if (!window_rect_changed && clip_rect == clip_rect_) | 666 if (!window_rect_changed && clip_rect == clip_rect_) |
1016 return; | 667 return; |
1017 | 668 |
1018 clip_rect_ = clip_rect; | 669 clip_rect_ = clip_rect; |
1019 window_rect_ = window_rect; | 670 window_rect_ = window_rect; |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1058 window_.window = old_dc; | 709 window_.window = old_dc; |
1059 } | 710 } |
1060 | 711 |
1061 void WebPluginDelegateImpl::WindowlessSetWindow() { | 712 void WebPluginDelegateImpl::WindowlessSetWindow() { |
1062 if (!instance()) | 713 if (!instance()) |
1063 return; | 714 return; |
1064 | 715 |
1065 if (window_rect_.IsEmpty()) // wait for geometry to be set. | 716 if (window_rect_.IsEmpty()) // wait for geometry to be set. |
1066 return; | 717 return; |
1067 | 718 |
1068 DCHECK(instance()->windowless()); | |
1069 | |
1070 window_.clipRect.top = clip_rect_.y(); | 719 window_.clipRect.top = clip_rect_.y(); |
1071 window_.clipRect.left = clip_rect_.x(); | 720 window_.clipRect.left = clip_rect_.x(); |
1072 window_.clipRect.bottom = clip_rect_.y() + clip_rect_.height(); | 721 window_.clipRect.bottom = clip_rect_.y() + clip_rect_.height(); |
1073 window_.clipRect.right = clip_rect_.x() + clip_rect_.width(); | 722 window_.clipRect.right = clip_rect_.x() + clip_rect_.width(); |
1074 window_.height = window_rect_.height(); | 723 window_.height = window_rect_.height(); |
1075 window_.width = window_rect_.width(); | 724 window_.width = window_rect_.width(); |
1076 window_.x = window_rect_.x(); | 725 window_.x = window_rect_.x(); |
1077 window_.y = window_rect_.y(); | 726 window_.y = window_rect_.y(); |
1078 window_.type = NPWindowTypeDrawable; | 727 window_.type = NPWindowTypeDrawable; |
1079 DrawableContextEnforcer enforcer(&window_); | 728 DrawableContextEnforcer enforcer(&window_); |
1080 | 729 |
1081 NPError err = instance()->NPP_SetWindow(&window_); | 730 NPError err = instance()->NPP_SetWindow(&window_); |
1082 DCHECK(err == NPERR_NO_ERROR); | 731 DCHECK(err == NPERR_NO_ERROR); |
1083 } | 732 } |
1084 | 733 |
1085 bool WebPluginDelegateImpl::PlatformSetPluginHasFocus(bool focused) { | 734 bool WebPluginDelegateImpl::PlatformSetPluginHasFocus(bool focused) { |
1086 DCHECK(instance()->windowless()); | |
1087 | |
1088 NPEvent focus_event; | 735 NPEvent focus_event; |
1089 focus_event.event = focused ? WM_SETFOCUS : WM_KILLFOCUS; | 736 focus_event.event = focused ? WM_SETFOCUS : WM_KILLFOCUS; |
1090 focus_event.wParam = 0; | 737 focus_event.wParam = 0; |
1091 focus_event.lParam = 0; | 738 focus_event.lParam = 0; |
1092 | 739 |
1093 instance()->NPP_HandleEvent(&focus_event); | 740 instance()->NPP_HandleEvent(&focus_event); |
1094 return true; | 741 return true; |
1095 } | 742 } |
1096 | 743 |
1097 static bool NPEventFromWebMouseEvent(const WebMouseEvent& event, | 744 static bool NPEventFromWebMouseEvent(const WebMouseEvent& event, |
(...skipping 248 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1346 UnhookWindowsHookEx(handle_event_message_filter_hook_); | 993 UnhookWindowsHookEx(handle_event_message_filter_hook_); |
1347 handle_event_message_filter_hook_ = NULL; | 994 handle_event_message_filter_hook_ = NULL; |
1348 } | 995 } |
1349 | 996 |
1350 bool WebPluginDelegateImpl::ShouldTrackEventForModalLoops(NPEvent* event) { | 997 bool WebPluginDelegateImpl::ShouldTrackEventForModalLoops(NPEvent* event) { |
1351 if (event->event == WM_RBUTTONDOWN) | 998 if (event->event == WM_RBUTTONDOWN) |
1352 return true; | 999 return true; |
1353 return false; | 1000 return false; |
1354 } | 1001 } |
1355 | 1002 |
1356 void WebPluginDelegateImpl::OnUserGestureEnd() { | |
1357 user_gesture_message_posted_ = false; | |
1358 instance()->PopPopupsEnabledState(); | |
1359 } | |
1360 | |
1361 BOOL WINAPI WebPluginDelegateImpl::TrackPopupMenuPatch( | 1003 BOOL WINAPI WebPluginDelegateImpl::TrackPopupMenuPatch( |
1362 HMENU menu, unsigned int flags, int x, int y, int reserved, | 1004 HMENU menu, unsigned int flags, int x, int y, int reserved, |
1363 HWND window, const RECT* rect) { | 1005 HWND window, const RECT* rect) { |
1364 | 1006 |
1365 if (g_current_plugin_instance) { | 1007 if (g_current_plugin_instance) { |
1366 unsigned long window_process_id = 0; | 1008 unsigned long window_process_id = 0; |
1367 unsigned long window_thread_id = | 1009 unsigned long window_thread_id = |
1368 GetWindowThreadProcessId(window, &window_process_id); | 1010 GetWindowThreadProcessId(window, &window_process_id); |
1369 // TrackPopupMenu fails if the window passed in belongs to a different | 1011 // TrackPopupMenu fails if the window passed in belongs to a different |
1370 // thread. | 1012 // thread. |
(...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1457 HWND window = WindowFromPoint(point); | 1099 HWND window = WindowFromPoint(point); |
1458 if (::ScreenToClient(window, &point)) { | 1100 if (::ScreenToClient(window, &point)) { |
1459 HWND child = ChildWindowFromPoint(window, point); | 1101 HWND child = ChildWindowFromPoint(window, point); |
1460 if (::IsWindow(child) && | 1102 if (::IsWindow(child) && |
1461 ::GetProp(child, content::kPluginDummyParentProperty)) | 1103 ::GetProp(child, content::kPluginDummyParentProperty)) |
1462 return child; | 1104 return child; |
1463 } | 1105 } |
1464 return window; | 1106 return window; |
1465 } | 1107 } |
1466 | 1108 |
1467 void WebPluginDelegateImpl::HandleCaptureForMessage(HWND window, | |
1468 UINT message) { | |
1469 if (gfx::GetClassName(window) != base::string16(kNativeWindowClassName)) | |
1470 return; | |
1471 | |
1472 switch (message) { | |
1473 case WM_LBUTTONDOWN: | |
1474 case WM_MBUTTONDOWN: | |
1475 case WM_RBUTTONDOWN: | |
1476 ::SetCapture(window); | |
1477 // As per documentation the WM_PARENTNOTIFY message is sent to the parent | |
1478 // window chain if mouse input is received by the child window. However | |
1479 // the parent receives the WM_PARENTNOTIFY message only if we doubleclick | |
1480 // on the window. We send the WM_PARENTNOTIFY message for mouse input | |
1481 // messages to the parent to indicate that user action is expected. | |
1482 ::SendMessage(::GetParent(window), WM_PARENTNOTIFY, message, 0); | |
1483 break; | |
1484 | |
1485 case WM_LBUTTONUP: | |
1486 case WM_MBUTTONUP: | |
1487 case WM_RBUTTONUP: | |
1488 ::ReleaseCapture(); | |
1489 break; | |
1490 | |
1491 default: | |
1492 break; | |
1493 } | |
1494 } | |
1495 | |
1496 } // namespace content | 1109 } // namespace content |
OLD | NEW |