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
3 // found in the LICENSE file.
5 #include "content/child/npapi/webplugin_delegate_impl.h"
12 #include "base/bind.h"
13 #include "base/compiler_specific.h"
14 #include "base/lazy_instance.h"
15 #include "base/memory/scoped_ptr.h"
16 #include "base/message_loop/message_loop.h"
17 #include "base/strings/string_util.h"
18 #include "base/strings/stringprintf.h"
19 #include "base/synchronization/lock.h"
20 #include "base/version.h"
21 #include "base/win/iat_patch_function.h"
22 #include "base/win/registry.h"
23 #include "base/win/windows_version.h"
24 #include "content/child/npapi/plugin_instance.h"
25 #include "content/child/npapi/plugin_lib.h"
26 #include "content/child/npapi/plugin_stream_url.h"
27 #include "content/child/npapi/webplugin.h"
28 #include "content/child/npapi/webplugin_ime_win.h"
29 #include "content/common/cursors/webcursor.h"
30 #include "content/common/plugin_constants_win.h"
31 #include "content/public/common/content_constants.h"
32 #include "skia/ext/platform_canvas.h"
33 #include "third_party/WebKit/public/web/WebInputEvent.h"
34 #include "ui/gfx/win/dpi.h"
35 #include "ui/gfx/win/hwnd_util.h"
37 using blink::WebKeyboardEvent
;
38 using blink::WebInputEvent
;
39 using blink::WebMouseEvent
;
45 const wchar_t kWebPluginDelegateProperty
[] = L
"WebPluginDelegateProperty";
46 const wchar_t kPluginFlashThrottle
[] = L
"FlashThrottle";
48 // The fastest we are willing to process WM_USER+1 events for Flash.
49 // Flash can easily exceed the limits of our CPU if we don't throttle it.
50 // The throttle has been chosen by testing various delays and compromising
51 // on acceptable Flash performance and reasonable CPU consumption.
53 // I'd like to make the throttle delay variable, based on the amount of
54 // time currently required to paint Flash plugins. There isn't a good
55 // way to count the time spent in aggregate plugin painting, however, so
56 // this seems to work well enough.
57 const int kFlashWMUSERMessageThrottleDelayMs
= 5;
59 // Flash displays popups in response to user clicks by posting a WM_USER
60 // message to the plugin window. The handler for this message displays
61 // the popup. To ensure that the popups allowed state is sent correctly
62 // to the renderer we reset the popups allowed state in a timer.
63 const int kWindowedPluginPopupTimerMs
= 50;
65 // The current instance of the plugin which entered the modal loop.
66 WebPluginDelegateImpl
* g_current_plugin_instance
= NULL
;
68 typedef std::deque
<MSG
> ThrottleQueue
;
69 base::LazyInstance
<ThrottleQueue
> g_throttle_queue
= LAZY_INSTANCE_INITIALIZER
;
71 base::LazyInstance
<std::map
<HWND
, WNDPROC
> > g_window_handle_proc_map
=
72 LAZY_INSTANCE_INITIALIZER
;
74 // Helper object for patching the TrackPopupMenu API.
75 base::LazyInstance
<base::win::IATPatchFunction
> g_iat_patch_track_popup_menu
=
76 LAZY_INSTANCE_INITIALIZER
;
78 // Helper object for patching the SetCursor API.
79 base::LazyInstance
<base::win::IATPatchFunction
> g_iat_patch_set_cursor
=
80 LAZY_INSTANCE_INITIALIZER
;
82 // Helper object for patching the RegEnumKeyExW API.
83 base::LazyInstance
<base::win::IATPatchFunction
> g_iat_patch_reg_enum_key_ex_w
=
84 LAZY_INSTANCE_INITIALIZER
;
86 // Helper object for patching the GetProcAddress API.
87 base::LazyInstance
<base::win::IATPatchFunction
> g_iat_patch_get_proc_address
=
88 LAZY_INSTANCE_INITIALIZER
;
90 base::LazyInstance
<base::win::IATPatchFunction
> g_iat_patch_window_from_point
=
91 LAZY_INSTANCE_INITIALIZER
;
93 // http://crbug.com/16114
94 // Enforces providing a valid device context in NPWindow, so that NPP_SetWindow
95 // is never called with NPNWindoTypeDrawable and NPWindow set to NULL.
96 // Doing so allows removing NPP_SetWindow call during painting a windowless
97 // plugin, which otherwise could trigger layout change while painting by
98 // invoking NPN_Evaluate. Which would cause bad, bad crashes. Bad crashes.
99 // TODO(dglazkov): If this approach doesn't produce regressions, move class to
100 // webplugin_delegate_impl.h and implement for other platforms.
101 class DrawableContextEnforcer
{
103 explicit DrawableContextEnforcer(NPWindow
* window
)
105 disposable_dc_(window
&& !window
->window
) {
106 // If NPWindow is NULL, create a device context with monochrome 1x1 surface
107 // and stuff it to NPWindow.
109 window_
->window
= CreateCompatibleDC(NULL
);
112 ~DrawableContextEnforcer() {
116 DeleteDC(static_cast<HDC
>(window_
->window
));
117 window_
->window
= NULL
;
125 // These are from ntddk.h
126 typedef LONG NTSTATUS
;
128 #ifndef STATUS_SUCCESS
129 #define STATUS_SUCCESS ((NTSTATUS)0x00000000L)
132 #ifndef STATUS_BUFFER_TOO_SMALL
133 #define STATUS_BUFFER_TOO_SMALL ((NTSTATUS)0xC0000023L)
136 typedef enum _KEY_INFORMATION_CLASS
{
141 KeyCachedInformation
,
142 KeyVirtualizationInformation
143 } KEY_INFORMATION_CLASS
;
145 typedef struct _KEY_NAME_INFORMATION
{
148 } KEY_NAME_INFORMATION
, *PKEY_NAME_INFORMATION
;
150 typedef DWORD (__stdcall
*ZwQueryKeyType
)(
152 int key_information_class
,
153 PVOID key_information
,
155 PULONG result_length
);
157 // Returns a key's full path.
158 std::wstring
GetKeyPath(HKEY key
) {
162 HMODULE dll
= GetModuleHandle(L
"ntdll.dll");
166 ZwQueryKeyType func
= reinterpret_cast<ZwQueryKeyType
>(
167 ::GetProcAddress(dll
, "ZwQueryKey"));
173 result
= func(key
, KeyNameInformation
, 0, 0, &size
);
174 if (result
!= STATUS_BUFFER_TOO_SMALL
)
177 scoped_ptr
<char[]> buffer(new char[size
]);
178 if (buffer
.get() == NULL
)
181 result
= func(key
, KeyNameInformation
, buffer
.get(), size
, &size
);
182 if (result
!= STATUS_SUCCESS
)
185 KEY_NAME_INFORMATION
* info
=
186 reinterpret_cast<KEY_NAME_INFORMATION
*>(buffer
.get());
187 return std::wstring(info
->Name
, info
->NameLength
/ sizeof(wchar_t));
190 uint32_t GetPluginMajorVersion(const WebPluginInfo
& plugin_info
) {
191 Version plugin_version
;
192 WebPluginInfo::CreateVersionFromString(plugin_info
.version
, &plugin_version
);
194 uint32_t major_version
= 0;
195 if (plugin_version
.IsValid())
196 major_version
= plugin_version
.components()[0];
198 return major_version
;
203 LRESULT CALLBACK
WebPluginDelegateImpl::HandleEventMessageFilterHook(
204 int code
, WPARAM wParam
, LPARAM lParam
) {
205 if (g_current_plugin_instance
) {
206 g_current_plugin_instance
->OnModalLoopEntered();
210 return CallNextHookEx(NULL
, code
, wParam
, lParam
);
213 LRESULT CALLBACK
WebPluginDelegateImpl::MouseHookProc(
214 int code
, WPARAM wParam
, LPARAM lParam
) {
215 if (code
== HC_ACTION
) {
216 MOUSEHOOKSTRUCT
* hook_struct
= reinterpret_cast<MOUSEHOOKSTRUCT
*>(lParam
);
218 HandleCaptureForMessage(hook_struct
->hwnd
, wParam
);
221 return CallNextHookEx(NULL
, code
, wParam
, lParam
);
224 WebPluginDelegateImpl::WebPluginDelegateImpl(
226 PluginInstance
* instance
)
227 : instance_(instance
),
231 windowed_handle_(NULL
),
232 windowed_did_set_window_(false),
233 plugin_wnd_proc_(NULL
),
235 is_calling_wndproc(false),
236 dummy_window_for_activation_(NULL
),
237 dummy_window_parent_(NULL
),
238 old_dummy_window_proc_(NULL
),
239 handle_event_message_filter_hook_(NULL
),
240 handle_event_pump_messages_event_(NULL
),
241 user_gesture_message_posted_(false),
242 user_gesture_msg_factory_(this),
243 handle_event_depth_(0),
245 first_set_window_call_(true),
246 plugin_has_focus_(false),
247 has_webkit_focus_(false),
248 containing_view_has_focus_(true),
249 creation_succeeded_(false) {
250 memset(&window_
, 0, sizeof(window_
));
252 const WebPluginInfo
& plugin_info
= instance_
->plugin_lib()->plugin_info();
253 std::wstring filename
=
254 base::StringToLowerASCII(plugin_info
.path
.BaseName().value());
256 if (instance_
->mime_type() == kFlashPluginSwfMimeType
||
257 filename
== kFlashPlugin
) {
258 // Flash only requests windowless plugins if we return a Mozilla user
260 instance_
->set_use_mozilla_user_agent();
261 quirks_
|= PLUGIN_QUIRK_THROTTLE_WM_USER_PLUS_ONE
;
262 quirks_
|= PLUGIN_QUIRK_PATCH_SETCURSOR
;
263 quirks_
|= PLUGIN_QUIRK_ALWAYS_NOTIFY_SUCCESS
;
264 quirks_
|= PLUGIN_QUIRK_HANDLE_MOUSE_CAPTURE
;
265 quirks_
|= PLUGIN_QUIRK_EMULATE_IME
;
266 quirks_
|= PLUGIN_QUIRK_FAKE_WINDOW_FROM_POINT
;
267 } else if (filename
== kAcrobatReaderPlugin
) {
268 // Check for the version number above or equal 9.
269 uint32_t major_version
= GetPluginMajorVersion(plugin_info
);
270 if (major_version
>= 9) {
271 quirks_
|= PLUGIN_QUIRK_DIE_AFTER_UNLOAD
;
273 quirks_
|= PLUGIN_QUIRK_SETWINDOW_TWICE
;
275 quirks_
|= PLUGIN_QUIRK_BLOCK_NONSTANDARD_GETURL_REQUESTS
;
276 } else if (plugin_info
.name
.find(L
"Windows Media Player") !=
277 std::wstring::npos
) {
278 // Windows Media Player needs two NPP_SetWindow calls.
279 quirks_
|= PLUGIN_QUIRK_SETWINDOW_TWICE
;
281 // Windowless mode doesn't work in the WMP NPAPI plugin.
282 quirks_
|= PLUGIN_QUIRK_NO_WINDOWLESS
;
284 // The media player plugin sets its size on the first NPP_SetWindow call
285 // and never updates its size. We should call the underlying NPP_SetWindow
286 // only when we have the correct size.
287 quirks_
|= PLUGIN_QUIRK_IGNORE_FIRST_SETWINDOW_CALL
;
289 if (filename
== kOldWMPPlugin
) {
290 // Non-admin users on XP couldn't modify the key to force the new UI.
291 quirks_
|= PLUGIN_QUIRK_PATCH_REGENUMKEYEXW
;
293 } else if (instance_
->mime_type() == "audio/x-pn-realaudio-plugin" ||
294 filename
== kRealPlayerPlugin
) {
295 quirks_
|= PLUGIN_QUIRK_DONT_CALL_WND_PROC_RECURSIVELY
;
296 } else if (plugin_info
.name
.find(L
"VLC Multimedia Plugin") !=
297 std::wstring::npos
||
298 plugin_info
.name
.find(L
"VLC Multimedia Plug-in") !=
299 std::wstring::npos
) {
300 // VLC hangs on NPP_Destroy if we call NPP_SetWindow with a null window
302 quirks_
|= PLUGIN_QUIRK_DONT_SET_NULL_WINDOW_HANDLE_ON_DESTROY
;
303 uint32_t major_version
= GetPluginMajorVersion(plugin_info
);
304 if (major_version
== 0) {
305 // VLC 0.8.6d and 0.8.6e crash if multiple instances are created.
306 quirks_
|= PLUGIN_QUIRK_DONT_ALLOW_MULTIPLE_INSTANCES
;
308 } else if (filename
== kSilverlightPlugin
) {
309 // Explanation for this quirk can be found in
310 // WebPluginDelegateImpl::Initialize.
311 quirks_
|= PLUGIN_QUIRK_PATCH_SETCURSOR
;
312 } else if (plugin_info
.name
.find(L
"DivX Web Player") !=
313 std::wstring::npos
) {
314 // The divx plugin sets its size on the first NPP_SetWindow call and never
315 // updates its size. We should call the underlying NPP_SetWindow only when
316 // we have the correct size.
317 quirks_
|= PLUGIN_QUIRK_IGNORE_FIRST_SETWINDOW_CALL
;
321 WebPluginDelegateImpl::~WebPluginDelegateImpl() {
322 if (::IsWindow(dummy_window_for_activation_
)) {
323 WNDPROC current_wnd_proc
= reinterpret_cast<WNDPROC
>(
324 GetWindowLongPtr(dummy_window_for_activation_
, GWLP_WNDPROC
));
325 if (current_wnd_proc
== DummyWindowProc
) {
326 SetWindowLongPtr(dummy_window_for_activation_
,
328 reinterpret_cast<LONG_PTR
>(old_dummy_window_proc_
));
330 ::DestroyWindow(dummy_window_for_activation_
);
336 WindowedDestroyWindow();
338 if (handle_event_pump_messages_event_
) {
339 CloseHandle(handle_event_pump_messages_event_
);
343 bool WebPluginDelegateImpl::PlatformInitialize() {
344 plugin_
->SetWindow(windowed_handle_
);
347 CreateDummyWindowForActivation();
348 handle_event_pump_messages_event_
= CreateEvent(NULL
, TRUE
, FALSE
, NULL
);
349 plugin_
->SetWindowlessData(
350 handle_event_pump_messages_event_
,
351 reinterpret_cast<gfx::NativeViewId
>(dummy_window_for_activation_
));
354 // Windowless plugins call the WindowFromPoint API and passes the result of
355 // that to the TrackPopupMenu API call as the owner window. This causes the
356 // API to fail as the API expects the window handle to live on the same
357 // thread as the caller. It works in the other browsers as the plugin lives
358 // on the browser thread. Our workaround is to intercept the TrackPopupMenu
359 // API and replace the window handle with the dummy activation window.
360 if (windowless_
&& !g_iat_patch_track_popup_menu
.Pointer()->is_patched()) {
361 g_iat_patch_track_popup_menu
.Pointer()->Patch(
362 GetPluginPath().value().c_str(), "user32.dll", "TrackPopupMenu",
363 WebPluginDelegateImpl::TrackPopupMenuPatch
);
366 // Windowless plugins can set cursors by calling the SetCursor API. This
367 // works because the thread inputs of the browser UI thread and the plugin
368 // thread are attached. We intercept the SetCursor API for windowless
369 // plugins and remember the cursor being set. This is shipped over to the
370 // browser in the HandleEvent call, which ensures that the cursor does not
371 // change when a windowless plugin instance changes the cursor
372 // in a background tab.
373 if (windowless_
&& !g_iat_patch_set_cursor
.Pointer()->is_patched() &&
374 (quirks_
& PLUGIN_QUIRK_PATCH_SETCURSOR
)) {
375 g_iat_patch_set_cursor
.Pointer()->Patch(
376 GetPluginPath().value().c_str(), "user32.dll", "SetCursor",
377 WebPluginDelegateImpl::SetCursorPatch
);
380 // The windowed flash plugin has a bug which occurs when the plugin enters
381 // fullscreen mode. It basically captures the mouse on WM_LBUTTONDOWN and
382 // does not release capture correctly causing it to stop receiving
383 // subsequent mouse events. This problem is also seen in Safari where there
384 // is code to handle this in the wndproc. However the plugin subclasses the
385 // window again in WM_LBUTTONDOWN before entering full screen. As a result
386 // Safari does not receive the WM_LBUTTONUP message. To workaround this
387 // issue we use a per thread mouse hook. This bug does not occur in Firefox
388 // and opera. Firefox has code similar to Safari. It could well be a bug in
389 // the flash plugin, which only occurs in webkit based browsers.
390 if (quirks_
& PLUGIN_QUIRK_HANDLE_MOUSE_CAPTURE
) {
391 mouse_hook_
= SetWindowsHookEx(WH_MOUSE
, MouseHookProc
, NULL
,
392 GetCurrentThreadId());
395 // On XP, WMP will use its old UI unless a registry key under HKLM has the
396 // name of the current process. We do it in the installer for admin users,
397 // for the rest patch this function.
398 if ((quirks_
& PLUGIN_QUIRK_PATCH_REGENUMKEYEXW
) &&
399 base::win::GetVersion() == base::win::VERSION_XP
&&
400 (base::win::RegKey().Open(HKEY_LOCAL_MACHINE
,
401 L
"SOFTWARE\\Microsoft\\MediaPlayer\\ShimInclusionList\\chrome.exe",
402 KEY_READ
) != ERROR_SUCCESS
) &&
403 !g_iat_patch_reg_enum_key_ex_w
.Pointer()->is_patched()) {
404 g_iat_patch_reg_enum_key_ex_w
.Pointer()->Patch(
405 L
"wmpdxm.dll", "advapi32.dll", "RegEnumKeyExW",
406 WebPluginDelegateImpl::RegEnumKeyExWPatch
);
409 // Flash retrieves the pointers to IMM32 functions with GetProcAddress() calls
410 // and use them to retrieve IME data. We add a patch to this function so we
411 // can dispatch these IMM32 calls to the WebPluginIMEWin class, which emulates
412 // IMM32 functions for Flash.
413 if (!g_iat_patch_get_proc_address
.Pointer()->is_patched() &&
414 (quirks_
& PLUGIN_QUIRK_EMULATE_IME
)) {
415 g_iat_patch_get_proc_address
.Pointer()->Patch(
416 GetPluginPath().value().c_str(), "kernel32.dll", "GetProcAddress",
417 GetProcAddressPatch
);
420 if (windowless_
&& !g_iat_patch_window_from_point
.Pointer()->is_patched() &&
421 (quirks_
& PLUGIN_QUIRK_FAKE_WINDOW_FROM_POINT
)) {
422 g_iat_patch_window_from_point
.Pointer()->Patch(
423 GetPluginPath().value().c_str(), "user32.dll", "WindowFromPoint",
424 WebPluginDelegateImpl::WindowFromPointPatch
);
430 void WebPluginDelegateImpl::PlatformDestroyInstance() {
431 if (!instance_
->plugin_lib())
434 // Unpatch if this is the last plugin instance.
435 if (instance_
->plugin_lib()->instance_count() != 1)
438 if (g_iat_patch_set_cursor
.Pointer()->is_patched())
439 g_iat_patch_set_cursor
.Pointer()->Unpatch();
441 if (g_iat_patch_track_popup_menu
.Pointer()->is_patched())
442 g_iat_patch_track_popup_menu
.Pointer()->Unpatch();
444 if (g_iat_patch_reg_enum_key_ex_w
.Pointer()->is_patched())
445 g_iat_patch_reg_enum_key_ex_w
.Pointer()->Unpatch();
447 if (g_iat_patch_window_from_point
.Pointer()->is_patched())
448 g_iat_patch_window_from_point
.Pointer()->Unpatch();
451 UnhookWindowsHookEx(mouse_hook_
);
456 void WebPluginDelegateImpl::Paint(SkCanvas
* canvas
, const gfx::Rect
& rect
) {
457 if (windowless_
&& skia::SupportsPlatformPaint(canvas
)) {
458 skia::ScopedPlatformPaint
scoped_platform_paint(canvas
);
459 HDC hdc
= scoped_platform_paint
.GetPlatformSurface();
460 WindowlessPaint(hdc
, rect
);
464 bool WebPluginDelegateImpl::WindowedCreatePlugin() {
465 DCHECK(!windowed_handle_
);
467 RegisterNativeWindowClass();
469 // The window will be sized and shown later.
470 windowed_handle_
= CreateWindowEx(
471 WS_EX_LEFT
| WS_EX_LTRREADING
| WS_EX_RIGHTSCROLLBAR
,
472 kNativeWindowClassName
,
474 WS_POPUP
| WS_CLIPCHILDREN
| WS_CLIPSIBLINGS
,
481 GetModuleHandle(NULL
),
483 if (windowed_handle_
== 0)
486 // This is a tricky workaround for Issue 2673 in chromium "Flash: IME not
487 // available". To use IMEs in this window, we have to make Windows attach
488 // IMEs to this window (i.e. load IME DLLs, attach them to this process, and
489 // add their message hooks to this window). Windows attaches IMEs while this
490 // process creates a top-level window. On the other hand, to layout this
491 // window correctly in the given parent window (RenderWidgetHostViewWin or
492 // RenderWidgetHostViewAura), this window should be a child window of the
493 // parent window. To satisfy both of the above conditions, this code once
494 // creates a top-level window and change it to a child window of the parent
495 // window (in the browser process).
496 SetWindowLongPtr(windowed_handle_
, GWL_STYLE
,
497 WS_CHILD
| WS_CLIPCHILDREN
| WS_CLIPSIBLINGS
);
499 BOOL result
= SetProp(windowed_handle_
, kWebPluginDelegateProperty
, this);
500 DCHECK(result
== TRUE
) << "SetProp failed, last error = " << GetLastError();
502 // Calling SetWindowLongPtrA here makes the window proc ASCII, which is
503 // required by at least the Shockwave Director plugin.
504 SetWindowLongPtrA(windowed_handle_
,
506 reinterpret_cast<LONG_PTR
>(DefWindowProcA
));
511 void WebPluginDelegateImpl::WindowedDestroyWindow() {
512 if (windowed_handle_
!= NULL
) {
513 // Unsubclass the window.
514 WNDPROC current_wnd_proc
= reinterpret_cast<WNDPROC
>(
515 GetWindowLongPtr(windowed_handle_
, GWLP_WNDPROC
));
516 if (current_wnd_proc
== NativeWndProc
) {
517 SetWindowLongPtr(windowed_handle_
,
519 reinterpret_cast<LONG_PTR
>(plugin_wnd_proc_
));
522 plugin_
->WillDestroyWindow(windowed_handle_
);
524 DestroyWindow(windowed_handle_
);
525 windowed_handle_
= 0;
529 // Erase all messages in the queue destined for a particular window.
530 // When windows are closing, callers should use this function to clear
533 void WebPluginDelegateImpl::ClearThrottleQueueForWindow(HWND window
) {
534 ThrottleQueue
* throttle_queue
= g_throttle_queue
.Pointer();
536 ThrottleQueue::iterator it
;
537 for (it
= throttle_queue
->begin(); it
!= throttle_queue
->end(); ) {
538 if (it
->hwnd
== window
) {
539 it
= throttle_queue
->erase(it
);
546 // Delayed callback for processing throttled messages.
547 // Throttled messages are aggregated globally across all plugins.
549 void WebPluginDelegateImpl::OnThrottleMessage() {
550 // The current algorithm walks the list and processes the first
551 // message it finds for each plugin. It is important to service
552 // all active plugins with each pass through the throttle, otherwise
553 // we see video jankiness. Copy the set to notify before notifying
554 // since we may re-enter OnThrottleMessage from CallWindowProc!
555 ThrottleQueue
* throttle_queue
= g_throttle_queue
.Pointer();
556 ThrottleQueue notify_queue
;
557 std::set
<HWND
> processed
;
559 ThrottleQueue::iterator it
= throttle_queue
->begin();
560 while (it
!= throttle_queue
->end()) {
561 const MSG
& msg
= *it
;
562 if (processed
.find(msg
.hwnd
) == processed
.end()) {
563 processed
.insert(msg
.hwnd
);
564 notify_queue
.push_back(msg
);
565 it
= throttle_queue
->erase(it
);
571 // Due to re-entrancy, we must save our queue state now. Otherwise, we may
572 // self-post below, and *also* start up another delayed task when the first
573 // entry is pushed onto the queue in ThrottleMessage().
574 bool throttle_queue_was_empty
= throttle_queue
->empty();
576 for (it
= notify_queue
.begin(); it
!= notify_queue
.end(); ++it
) {
577 const MSG
& msg
= *it
;
578 WNDPROC proc
= reinterpret_cast<WNDPROC
>(msg
.time
);
579 // It is possible that the window was closed after we queued
580 // this message. This is a rare event; just verify the window
581 // is alive. (see also bug 1259488)
582 if (IsWindow(msg
.hwnd
))
583 CallWindowProc(proc
, msg
.hwnd
, msg
.message
, msg
.wParam
, msg
.lParam
);
586 if (!throttle_queue_was_empty
) {
587 base::MessageLoop::current()->PostDelayedTask(
589 base::Bind(&WebPluginDelegateImpl::OnThrottleMessage
),
590 base::TimeDelta::FromMilliseconds(kFlashWMUSERMessageThrottleDelayMs
));
594 // Schedule a windows message for delivery later.
596 void WebPluginDelegateImpl::ThrottleMessage(WNDPROC proc
, HWND hwnd
,
597 UINT message
, WPARAM wParam
,
600 msg
.time
= reinterpret_cast<DWORD
>(proc
);
602 msg
.message
= message
;
606 ThrottleQueue
* throttle_queue
= g_throttle_queue
.Pointer();
608 throttle_queue
->push_back(msg
);
610 if (throttle_queue
->size() == 1) {
611 base::MessageLoop::current()->PostDelayedTask(
613 base::Bind(&WebPluginDelegateImpl::OnThrottleMessage
),
614 base::TimeDelta::FromMilliseconds(kFlashWMUSERMessageThrottleDelayMs
));
618 // We go out of our way to find the hidden windows created by Flash for
619 // windowless plugins. We throttle the rate at which they deliver messages
620 // so that they will not consume outrageous amounts of CPU.
622 LRESULT CALLBACK
WebPluginDelegateImpl::FlashWindowlessWndProc(
623 HWND hwnd
, UINT message
, WPARAM wparam
, LPARAM lparam
) {
624 std::map
<HWND
, WNDPROC
>::iterator index
=
625 g_window_handle_proc_map
.Get().find(hwnd
);
627 WNDPROC old_proc
= (*index
).second
;
632 WebPluginDelegateImpl::ClearThrottleQueueForWindow(hwnd
);
633 g_window_handle_proc_map
.Get().erase(index
);
636 // Flash may flood the message queue with WM_USER+1 message causing 100% CPU
637 // usage. See https://bugzilla.mozilla.org/show_bug.cgi?id=132759. We
638 // prevent this by throttling the messages.
640 WebPluginDelegateImpl::ThrottleMessage(old_proc
, hwnd
, message
, wparam
,
649 return CallWindowProc(old_proc
, hwnd
, message
, wparam
, lparam
);
652 LRESULT CALLBACK
WebPluginDelegateImpl::DummyWindowProc(
653 HWND hwnd
, UINT message
, WPARAM w_param
, LPARAM l_param
) {
654 WebPluginDelegateImpl
* delegate
= reinterpret_cast<WebPluginDelegateImpl
*>(
655 GetProp(hwnd
, kWebPluginDelegateProperty
));
657 if (message
== WM_WINDOWPOSCHANGING
) {
658 // We need to know when the dummy window is parented because windowless
659 // plugins need the parent window for things like menus. There's no message
660 // for a parent being changed, but a WM_WINDOWPOSCHANGING is sent so we
661 // check every time we get it.
662 // For non-aura builds, this never changes since RenderWidgetHostViewWin's
663 // window is constant. For aura builds, this changes every time the tab gets
664 // dragged to a new window.
665 HWND parent
= GetParent(hwnd
);
666 if (parent
!= delegate
->dummy_window_parent_
) {
667 delegate
->dummy_window_parent_
= parent
;
669 // Set the containing window handle as the instance window handle. This is
670 // what Safari does. Not having a valid window handle causes subtle bugs
671 // with plugins which retrieve the window handle and use it for things
672 // like context menus. The window handle can be retrieved via
673 // NPN_GetValue of NPNVnetscapeWindow.
674 delegate
->instance_
->set_window_handle(parent
);
676 // The plugin caches the result of NPNVnetscapeWindow when we originally
677 // called NPP_SetWindow, so force it to get the new value.
678 delegate
->WindowlessSetWindow();
680 } else if (message
== WM_NCDESTROY
) {
681 RemoveProp(hwnd
, kWebPluginDelegateProperty
);
683 return CallWindowProc(
684 delegate
->old_dummy_window_proc_
, hwnd
, message
, w_param
, l_param
);
687 // Callback for enumerating the Flash windows.
688 BOOL CALLBACK
EnumFlashWindows(HWND window
, LPARAM arg
) {
689 WNDPROC wnd_proc
= reinterpret_cast<WNDPROC
>(arg
);
690 TCHAR class_name
[1024];
691 if (!RealGetWindowClass(window
, class_name
,
692 sizeof(class_name
)/sizeof(TCHAR
))) {
693 LOG(ERROR
) << "RealGetWindowClass failure: " << GetLastError();
697 if (wcscmp(class_name
, L
"SWFlash_PlaceholderX"))
700 WNDPROC current_wnd_proc
= reinterpret_cast<WNDPROC
>(
701 GetWindowLongPtr(window
, GWLP_WNDPROC
));
702 if (current_wnd_proc
!= wnd_proc
) {
703 WNDPROC old_flash_proc
= reinterpret_cast<WNDPROC
>(SetWindowLongPtr(
704 window
, GWLP_WNDPROC
,
705 reinterpret_cast<LONG_PTR
>(wnd_proc
)));
706 DCHECK(old_flash_proc
);
707 g_window_handle_proc_map
.Get()[window
] = old_flash_proc
;
713 bool WebPluginDelegateImpl::CreateDummyWindowForActivation() {
714 DCHECK(!dummy_window_for_activation_
);
716 dummy_window_for_activation_
= CreateWindowEx(
719 kDummyActivationWindowName
,
725 // We don't know the parent of the dummy window yet, so just set it to the
726 // desktop and it'll get parented by the browser.
729 GetModuleHandle(NULL
),
732 if (dummy_window_for_activation_
== 0)
735 BOOL result
= SetProp(dummy_window_for_activation_
,
736 kWebPluginDelegateProperty
, this);
737 DCHECK(result
== TRUE
) << "SetProp failed, last error = " << GetLastError();
738 old_dummy_window_proc_
= reinterpret_cast<WNDPROC
>(SetWindowLongPtr(
739 dummy_window_for_activation_
, GWLP_WNDPROC
,
740 reinterpret_cast<LONG_PTR
>(DummyWindowProc
)));
742 // Flash creates background windows which use excessive CPU in our
743 // environment; we wrap these windows and throttle them so that they don't
745 if (!EnumThreadWindows(::GetCurrentThreadId(), EnumFlashWindows
,
746 reinterpret_cast<LPARAM
>(
747 &WebPluginDelegateImpl::FlashWindowlessWndProc
))) {
748 // Log that this happened. Flash will still work; it just means the
749 // throttle isn't installed (and Flash will use more CPU).
751 LOG(ERROR
) << "Failed to wrap all windowless Flash windows";
756 bool WebPluginDelegateImpl::WindowedReposition(
757 const gfx::Rect
& window_rect_in_dip
,
758 const gfx::Rect
& clip_rect_in_dip
) {
759 if (!windowed_handle_
) {
764 gfx::Rect window_rect
= gfx::win::DIPToScreenRect(window_rect_in_dip
);
765 gfx::Rect clip_rect
= gfx::win::DIPToScreenRect(clip_rect_in_dip
);
766 if (window_rect_
== window_rect
&& clip_rect_
== clip_rect
)
769 // We only set the plugin's size here. Its position is moved elsewhere, which
770 // allows the window moves/scrolling/clipping to be synchronized with the page
771 // and other windows.
772 // If the plugin window has no parent, then don't focus it because it isn't
773 // being displayed anywhere. See:
774 // http://code.google.com/p/chromium/issues/detail?id=32658
775 if (window_rect
.size() != window_rect_
.size()) {
776 UINT flags
= SWP_NOMOVE
| SWP_NOZORDER
;
777 if (!GetParent(windowed_handle_
))
778 flags
|= SWP_NOACTIVATE
;
779 ::SetWindowPos(windowed_handle_
,
784 window_rect
.height(),
788 window_rect_
= window_rect
;
789 clip_rect_
= clip_rect
;
791 // Ensure that the entire window gets repainted.
792 ::InvalidateRect(windowed_handle_
, NULL
, FALSE
);
797 void WebPluginDelegateImpl::WindowedSetWindow() {
798 if (!instance_
.get())
801 if (!windowed_handle_
) {
806 instance()->set_window_handle(windowed_handle_
);
808 DCHECK(!instance()->windowless());
810 window_
.clipRect
.top
= std::max(0, clip_rect_
.y());
811 window_
.clipRect
.left
= std::max(0, clip_rect_
.x());
812 window_
.clipRect
.bottom
= std::max(0, clip_rect_
.y() + clip_rect_
.height());
813 window_
.clipRect
.right
= std::max(0, clip_rect_
.x() + clip_rect_
.width());
814 window_
.height
= window_rect_
.height();
815 window_
.width
= window_rect_
.width();
819 window_
.window
= windowed_handle_
;
820 window_
.type
= NPWindowTypeWindow
;
822 // Reset this flag before entering the instance in case of side-effects.
823 windowed_did_set_window_
= true;
825 instance()->NPP_SetWindow(&window_
);
826 if (quirks_
& PLUGIN_QUIRK_SETWINDOW_TWICE
)
827 instance()->NPP_SetWindow(&window_
);
829 WNDPROC current_wnd_proc
= reinterpret_cast<WNDPROC
>(
830 GetWindowLongPtr(windowed_handle_
, GWLP_WNDPROC
));
831 if (current_wnd_proc
!= NativeWndProc
) {
832 plugin_wnd_proc_
= reinterpret_cast<WNDPROC
>(
833 SetWindowLongPtr(windowed_handle_
,
835 reinterpret_cast<LONG_PTR
>(NativeWndProc
)));
839 ATOM
WebPluginDelegateImpl::RegisterNativeWindowClass() {
840 static bool have_registered_window_class
= false;
841 if (have_registered_window_class
== true)
844 have_registered_window_class
= true;
847 wcex
.cbSize
= sizeof(WNDCLASSEX
);
848 wcex
.style
= CS_DBLCLKS
;
849 wcex
.lpfnWndProc
= WrapperWindowProc
;
852 wcex
.hInstance
= GetModuleHandle(NULL
);
855 // Some plugins like windows media player 11 create child windows parented
856 // by our plugin window, where the media content is rendered. These plugins
857 // dont implement WM_ERASEBKGND, which causes painting issues, when the
858 // window where the media is rendered is moved around. DefWindowProc does
859 // implement WM_ERASEBKGND correctly if we have a valid background brush.
860 wcex
.hbrBackground
= reinterpret_cast<HBRUSH
>(COLOR_WINDOW
+1);
861 wcex
.lpszMenuName
= 0;
862 wcex
.lpszClassName
= kNativeWindowClassName
;
865 return RegisterClassEx(&wcex
);
868 LRESULT CALLBACK
WebPluginDelegateImpl::WrapperWindowProc(
869 HWND hWnd
, UINT message
, WPARAM wParam
, LPARAM lParam
) {
870 // This is another workaround for Issue 2673 in chromium "Flash: IME not
871 // available". Somehow, the CallWindowProc() function does not dispatch
872 // window messages when its first parameter is a handle representing the
873 // DefWindowProc() function. To avoid this problem, this code creates a
874 // wrapper function which just encapsulates the DefWindowProc() function
875 // and set it as the window procedure of a windowed plugin.
876 return DefWindowProc(hWnd
, message
, wParam
, lParam
);
879 // Returns true if the message passed in corresponds to a user gesture.
880 static bool IsUserGestureMessage(unsigned int message
) {
899 LRESULT CALLBACK
WebPluginDelegateImpl::NativeWndProc(
900 HWND hwnd
, UINT message
, WPARAM wparam
, LPARAM lparam
) {
901 WebPluginDelegateImpl
* delegate
= reinterpret_cast<WebPluginDelegateImpl
*>(
902 GetProp(hwnd
, kWebPluginDelegateProperty
));
908 if (message
== delegate
->last_message_
&&
909 delegate
->GetQuirks() & PLUGIN_QUIRK_DONT_CALL_WND_PROC_RECURSIVELY
&&
910 delegate
->is_calling_wndproc
) {
911 // Real may go into a state where it recursively dispatches the same event
912 // when subclassed. See https://bugzilla.mozilla.org/show_bug.cgi?id=192914
913 // We only do the recursive check for Real because it's possible and valid
914 // for a plugin to synchronously dispatch a message to itself such that it
915 // looks like it's in recursion.
919 // Flash may flood the message queue with WM_USER+1 message causing 100% CPU
920 // usage. See https://bugzilla.mozilla.org/show_bug.cgi?id=132759. We
921 // prevent this by throttling the messages.
922 if (message
== WM_USER
+ 1 &&
923 delegate
->GetQuirks() & PLUGIN_QUIRK_THROTTLE_WM_USER_PLUS_ONE
) {
924 WebPluginDelegateImpl::ThrottleMessage(delegate
->plugin_wnd_proc_
, hwnd
,
925 message
, wparam
, lparam
);
930 uint32 old_message
= delegate
->last_message_
;
931 delegate
->last_message_
= message
;
933 static UINT custom_msg
= RegisterWindowMessage(kPaintMessageName
);
934 if (message
== custom_msg
) {
935 // Get the invalid rect which is in screen coordinates and convert to
936 // window coordinates.
937 gfx::Rect invalid_rect
;
938 invalid_rect
.set_x(static_cast<short>(LOWORD(wparam
)));
939 invalid_rect
.set_y(static_cast<short>(HIWORD(wparam
)));
940 invalid_rect
.set_width(static_cast<short>(LOWORD(lparam
)));
941 invalid_rect
.set_height(static_cast<short>(HIWORD(lparam
)));
944 GetWindowRect(hwnd
, &window_rect
);
945 invalid_rect
.Offset(-window_rect
.left
, -window_rect
.top
);
947 // The plugin window might have non-client area. If we don't pass in
948 // RDW_FRAME then the children don't receive WM_NCPAINT messages while
949 // scrolling, which causes painting problems (http://b/issue?id=923945).
950 uint32 flags
= RDW_INVALIDATE
| RDW_ALLCHILDREN
| RDW_FRAME
;
952 // If a plugin (like Google Earth or Java) has child windows that are hosted
953 // in a different process, then RedrawWindow with UPDATENOW will
954 // synchronously wait for this call to complete. Some messages are pumped
955 // but not others, which could lead to a deadlock. So avoid reentrancy by
956 // only synchronously calling RedrawWindow once at a time.
957 if (old_message
!= custom_msg
)
958 flags
|= RDW_UPDATENOW
;
959 RECT rect
= invalid_rect
.ToRECT();
960 RedrawWindow(hwnd
, &rect
, NULL
, flags
);
963 delegate
->is_calling_wndproc
= true;
965 if (!delegate
->user_gesture_message_posted_
&&
966 IsUserGestureMessage(message
)) {
967 delegate
->user_gesture_message_posted_
= true;
969 delegate
->instance()->PushPopupsEnabledState(true);
971 base::MessageLoop::current()->PostDelayedTask(
973 base::Bind(&WebPluginDelegateImpl::OnUserGestureEnd
,
974 delegate
->user_gesture_msg_factory_
.GetWeakPtr()),
975 base::TimeDelta::FromMilliseconds(kWindowedPluginPopupTimerMs
));
978 HandleCaptureForMessage(hwnd
, message
);
980 // Maintain a local/global stack for the g_current_plugin_instance variable
981 // as this may be a nested invocation.
982 WebPluginDelegateImpl
* last_plugin_instance
= g_current_plugin_instance
;
984 g_current_plugin_instance
= delegate
;
986 result
= CallWindowProc(
987 delegate
->plugin_wnd_proc_
, hwnd
, message
, wparam
, lparam
);
989 // The plugin instance may have been destroyed in the CallWindowProc call
990 // above. This will also destroy the plugin window. Before attempting to
991 // access the WebPluginDelegateImpl instance we validate if the window is
993 if (::IsWindow(hwnd
))
994 delegate
->is_calling_wndproc
= false;
996 g_current_plugin_instance
= last_plugin_instance
;
998 if (message
== WM_NCDESTROY
) {
999 RemoveProp(hwnd
, kWebPluginDelegateProperty
);
1000 ClearThrottleQueueForWindow(hwnd
);
1003 if (::IsWindow(hwnd
))
1004 delegate
->last_message_
= old_message
;
1008 void WebPluginDelegateImpl::WindowlessUpdateGeometry(
1009 const gfx::Rect
& window_rect
,
1010 const gfx::Rect
& clip_rect
) {
1011 bool window_rect_changed
= (window_rect_
!= window_rect
);
1012 // Only resend to the instance if the geometry has changed.
1013 if (!window_rect_changed
&& clip_rect
== clip_rect_
)
1016 clip_rect_
= clip_rect
;
1017 window_rect_
= window_rect
;
1019 WindowlessSetWindow();
1021 if (window_rect_changed
) {
1022 WINDOWPOS win_pos
= {0};
1023 win_pos
.x
= window_rect_
.x();
1024 win_pos
.y
= window_rect_
.y();
1025 win_pos
.cx
= window_rect_
.width();
1026 win_pos
.cy
= window_rect_
.height();
1028 NPEvent pos_changed_event
;
1029 pos_changed_event
.event
= WM_WINDOWPOSCHANGED
;
1030 pos_changed_event
.wParam
= 0;
1031 pos_changed_event
.lParam
= reinterpret_cast<uintptr_t>(&win_pos
);
1033 instance()->NPP_HandleEvent(&pos_changed_event
);
1037 void WebPluginDelegateImpl::WindowlessPaint(HDC hdc
,
1038 const gfx::Rect
& damage_rect
) {
1041 RECT damage_rect_win
;
1042 damage_rect_win
.left
= damage_rect
.x(); // + window_rect_.x();
1043 damage_rect_win
.top
= damage_rect
.y(); // + window_rect_.y();
1044 damage_rect_win
.right
= damage_rect_win
.left
+ damage_rect
.width();
1045 damage_rect_win
.bottom
= damage_rect_win
.top
+ damage_rect
.height();
1047 // Save away the old HDC as this could be a nested invocation.
1048 void* old_dc
= window_
.window
;
1049 window_
.window
= hdc
;
1051 NPEvent paint_event
;
1052 paint_event
.event
= WM_PAINT
;
1053 paint_event
.wParam
= PtrToUlong(hdc
);
1054 paint_event
.lParam
= reinterpret_cast<uintptr_t>(&damage_rect_win
);
1055 instance()->NPP_HandleEvent(&paint_event
);
1056 window_
.window
= old_dc
;
1059 void WebPluginDelegateImpl::WindowlessSetWindow() {
1063 if (window_rect_
.IsEmpty()) // wait for geometry to be set.
1066 DCHECK(instance()->windowless());
1068 window_
.clipRect
.top
= clip_rect_
.y();
1069 window_
.clipRect
.left
= clip_rect_
.x();
1070 window_
.clipRect
.bottom
= clip_rect_
.y() + clip_rect_
.height();
1071 window_
.clipRect
.right
= clip_rect_
.x() + clip_rect_
.width();
1072 window_
.height
= window_rect_
.height();
1073 window_
.width
= window_rect_
.width();
1074 window_
.x
= window_rect_
.x();
1075 window_
.y
= window_rect_
.y();
1076 window_
.type
= NPWindowTypeDrawable
;
1077 DrawableContextEnforcer
enforcer(&window_
);
1079 NPError err
= instance()->NPP_SetWindow(&window_
);
1080 DCHECK(err
== NPERR_NO_ERROR
);
1083 bool WebPluginDelegateImpl::PlatformSetPluginHasFocus(bool focused
) {
1084 DCHECK(instance()->windowless());
1086 NPEvent focus_event
;
1087 focus_event
.event
= focused
? WM_SETFOCUS
: WM_KILLFOCUS
;
1088 focus_event
.wParam
= 0;
1089 focus_event
.lParam
= 0;
1091 instance()->NPP_HandleEvent(&focus_event
);
1095 static bool NPEventFromWebMouseEvent(const WebMouseEvent
& event
,
1096 NPEvent
* np_event
) {
1097 np_event
->lParam
= static_cast<uint32
>(MAKELPARAM(event
.windowX
,
1099 np_event
->wParam
= 0;
1101 if (event
.modifiers
& WebInputEvent::ControlKey
)
1102 np_event
->wParam
|= MK_CONTROL
;
1103 if (event
.modifiers
& WebInputEvent::ShiftKey
)
1104 np_event
->wParam
|= MK_SHIFT
;
1105 if (event
.modifiers
& WebInputEvent::LeftButtonDown
)
1106 np_event
->wParam
|= MK_LBUTTON
;
1107 if (event
.modifiers
& WebInputEvent::MiddleButtonDown
)
1108 np_event
->wParam
|= MK_MBUTTON
;
1109 if (event
.modifiers
& WebInputEvent::RightButtonDown
)
1110 np_event
->wParam
|= MK_RBUTTON
;
1112 switch (event
.type
) {
1113 case WebInputEvent::MouseMove
:
1114 case WebInputEvent::MouseLeave
:
1115 case WebInputEvent::MouseEnter
:
1116 np_event
->event
= WM_MOUSEMOVE
;
1118 case WebInputEvent::MouseDown
:
1119 switch (event
.button
) {
1120 case WebMouseEvent::ButtonLeft
:
1121 np_event
->event
= WM_LBUTTONDOWN
;
1123 case WebMouseEvent::ButtonMiddle
:
1124 np_event
->event
= WM_MBUTTONDOWN
;
1126 case WebMouseEvent::ButtonRight
:
1127 np_event
->event
= WM_RBUTTONDOWN
;
1131 case WebInputEvent::MouseUp
:
1132 switch (event
.button
) {
1133 case WebMouseEvent::ButtonLeft
:
1134 np_event
->event
= WM_LBUTTONUP
;
1136 case WebMouseEvent::ButtonMiddle
:
1137 np_event
->event
= WM_MBUTTONUP
;
1139 case WebMouseEvent::ButtonRight
:
1140 np_event
->event
= WM_RBUTTONUP
;
1150 static bool NPEventFromWebKeyboardEvent(const WebKeyboardEvent
& event
,
1151 NPEvent
* np_event
) {
1152 np_event
->wParam
= event
.windowsKeyCode
;
1154 switch (event
.type
) {
1155 case WebInputEvent::KeyDown
:
1156 np_event
->event
= WM_KEYDOWN
;
1157 np_event
->lParam
= 0;
1159 case WebInputEvent::Char
:
1160 np_event
->event
= WM_CHAR
;
1161 np_event
->lParam
= 0;
1163 case WebInputEvent::KeyUp
:
1164 np_event
->event
= WM_KEYUP
;
1165 np_event
->lParam
= 0x8000;
1173 static bool NPEventFromWebInputEvent(const WebInputEvent
& event
,
1174 NPEvent
* np_event
) {
1175 switch (event
.type
) {
1176 case WebInputEvent::MouseMove
:
1177 case WebInputEvent::MouseLeave
:
1178 case WebInputEvent::MouseEnter
:
1179 case WebInputEvent::MouseDown
:
1180 case WebInputEvent::MouseUp
:
1181 if (event
.size
< sizeof(WebMouseEvent
)) {
1185 return NPEventFromWebMouseEvent(
1186 *static_cast<const WebMouseEvent
*>(&event
), np_event
);
1187 case WebInputEvent::KeyDown
:
1188 case WebInputEvent::Char
:
1189 case WebInputEvent::KeyUp
:
1190 if (event
.size
< sizeof(WebKeyboardEvent
)) {
1194 return NPEventFromWebKeyboardEvent(
1195 *static_cast<const WebKeyboardEvent
*>(&event
), np_event
);
1201 bool WebPluginDelegateImpl::PlatformHandleInputEvent(
1202 const WebInputEvent
& event
, WebCursor::CursorInfo
* cursor_info
) {
1203 DCHECK(cursor_info
!= NULL
);
1206 if (!NPEventFromWebInputEvent(event
, &np_event
)) {
1210 // Allow this plugin to access this IME emulator through IMM32 API while the
1211 // plugin is processing this event.
1212 if (GetQuirks() & PLUGIN_QUIRK_EMULATE_IME
) {
1214 plugin_ime_
.reset(new WebPluginIMEWin
);
1216 WebPluginIMEWin::ScopedLock
lock(
1217 event
.isKeyboardEventType(event
.type
) ? plugin_ime_
.get() : NULL
);
1219 HWND last_focus_window
= NULL
;
1221 if (ShouldTrackEventForModalLoops(&np_event
)) {
1222 // A windowless plugin can enter a modal loop in a NPP_HandleEvent call.
1223 // For e.g. Flash puts up a context menu when we right click on the
1224 // windowless plugin area. We detect this by setting up a message filter
1225 // hook pror to calling NPP_HandleEvent on the plugin and unhook on
1226 // return from NPP_HandleEvent. If the plugin does enter a modal loop
1227 // in that context we unhook on receiving the first notification in
1228 // the message filter hook.
1229 handle_event_message_filter_hook_
=
1230 SetWindowsHookEx(WH_MSGFILTER
, HandleEventMessageFilterHook
, NULL
,
1231 GetCurrentThreadId());
1232 // To ensure that the plugin receives keyboard events we set focus to the
1234 // TODO(iyengar) We need a framework in the renderer to identify which
1235 // windowless plugin is under the mouse and to handle this. This would
1236 // also require some changes in RenderWidgetHost to detect this in the
1237 // WM_MOUSEACTIVATE handler and inform the renderer accordingly.
1238 bool valid
= GetParent(dummy_window_for_activation_
) != GetDesktopWindow();
1240 last_focus_window
= ::SetFocus(dummy_window_for_activation_
);
1242 NOTREACHED() << "Dummy window not parented";
1246 bool old_task_reentrancy_state
=
1247 base::MessageLoop::current()->NestableTasksAllowed();
1249 // Maintain a local/global stack for the g_current_plugin_instance variable
1250 // as this may be a nested invocation.
1251 WebPluginDelegateImpl
* last_plugin_instance
= g_current_plugin_instance
;
1253 g_current_plugin_instance
= this;
1255 handle_event_depth_
++;
1257 bool popups_enabled
= false;
1259 if (IsUserGestureMessage(np_event
.event
)) {
1260 instance()->PushPopupsEnabledState(true);
1261 popups_enabled
= true;
1264 bool ret
= instance()->NPP_HandleEvent(&np_event
) != 0;
1266 if (popups_enabled
) {
1267 instance()->PopPopupsEnabledState();
1270 // Flash and SilverLight always return false, even when they swallow the
1271 // event. Flash does this because it passes the event to its window proc,
1272 // which is supposed to return 0 if an event was handled. There are few
1273 // exceptions, such as IME, where it sometimes returns true.
1276 if (np_event
.event
== WM_MOUSEMOVE
) {
1277 current_windowless_cursor_
.InitFromExternalCursor(GetCursor());
1278 // Snag a reference to the current cursor ASAP in case the plugin modified
1279 // it. There is a nasty race condition here with the multiprocess browser
1280 // as someone might be setting the cursor in the main process as well.
1281 current_windowless_cursor_
.GetCursorInfo(cursor_info
);
1284 handle_event_depth_
--;
1286 g_current_plugin_instance
= last_plugin_instance
;
1288 // We could have multiple NPP_HandleEvent calls nested together in case
1289 // the plugin enters a modal loop. Reset the pump messages event when
1290 // the outermost NPP_HandleEvent call unwinds.
1291 if (handle_event_depth_
== 0) {
1292 ResetEvent(handle_event_pump_messages_event_
);
1295 // If we didn't enter a modal loop, need to unhook the filter.
1296 if (handle_event_message_filter_hook_
) {
1297 UnhookWindowsHookEx(handle_event_message_filter_hook_
);
1298 handle_event_message_filter_hook_
= NULL
;
1301 if (::IsWindow(last_focus_window
)) {
1302 // Restore the nestable tasks allowed state in the message loop and reset
1303 // the os modal loop state as the plugin returned from the TrackPopupMenu
1305 base::MessageLoop::current()->SetNestableTasksAllowed(
1306 old_task_reentrancy_state
);
1307 base::MessageLoop::current()->set_os_modal_loop(false);
1308 // The Flash plugin at times sets focus to its hidden top level window
1309 // with class name SWFlash_PlaceholderX. This causes the chrome browser
1310 // window to receive a WM_ACTIVATEAPP message as a top level window from
1311 // another thread is now active. We end up in a state where the chrome
1312 // browser window is not active even though the user clicked on it.
1313 // Our workaround for this is to send over a raw
1314 // WM_LBUTTONDOWN/WM_LBUTTONUP combination to the last focus window, which
1316 if (dummy_window_for_activation_
!= ::GetFocus()) {
1317 INPUT input_info
= {0};
1318 input_info
.type
= INPUT_MOUSE
;
1319 input_info
.mi
.dwFlags
= MOUSEEVENTF_LEFTDOWN
;
1320 ::SendInput(1, &input_info
, sizeof(INPUT
));
1322 input_info
.type
= INPUT_MOUSE
;
1323 input_info
.mi
.dwFlags
= MOUSEEVENTF_LEFTUP
;
1324 ::SendInput(1, &input_info
, sizeof(INPUT
));
1326 ::SetFocus(last_focus_window
);
1333 void WebPluginDelegateImpl::OnModalLoopEntered() {
1334 DCHECK(handle_event_pump_messages_event_
!= NULL
);
1335 SetEvent(handle_event_pump_messages_event_
);
1337 base::MessageLoop::current()->SetNestableTasksAllowed(true);
1338 base::MessageLoop::current()->set_os_modal_loop(true);
1340 UnhookWindowsHookEx(handle_event_message_filter_hook_
);
1341 handle_event_message_filter_hook_
= NULL
;
1344 bool WebPluginDelegateImpl::ShouldTrackEventForModalLoops(NPEvent
* event
) {
1345 if (event
->event
== WM_RBUTTONDOWN
)
1350 void WebPluginDelegateImpl::OnUserGestureEnd() {
1351 user_gesture_message_posted_
= false;
1352 instance()->PopPopupsEnabledState();
1355 BOOL WINAPI
WebPluginDelegateImpl::TrackPopupMenuPatch(
1356 HMENU menu
, unsigned int flags
, int x
, int y
, int reserved
,
1357 HWND window
, const RECT
* rect
) {
1359 if (g_current_plugin_instance
) {
1360 unsigned long window_process_id
= 0;
1361 unsigned long window_thread_id
=
1362 GetWindowThreadProcessId(window
, &window_process_id
);
1363 // TrackPopupMenu fails if the window passed in belongs to a different
1365 if (::GetCurrentThreadId() != window_thread_id
) {
1367 GetParent(g_current_plugin_instance
->dummy_window_for_activation_
) !=
1370 window
= g_current_plugin_instance
->dummy_window_for_activation_
;
1372 NOTREACHED() << "Dummy window not parented";
1377 BOOL result
= TrackPopupMenu(menu
, flags
, x
, y
, reserved
, window
, rect
);
1381 HCURSOR WINAPI
WebPluginDelegateImpl::SetCursorPatch(HCURSOR cursor
) {
1382 // The windowless flash plugin periodically calls SetCursor in a wndproc
1383 // instantiated on the plugin thread. This causes annoying cursor flicker
1384 // when the mouse is moved on a foreground tab, with a windowless plugin
1385 // instance in a background tab. We just ignore the call here.
1386 if (!g_current_plugin_instance
) {
1387 HCURSOR current_cursor
= GetCursor();
1388 if (current_cursor
!= cursor
) {
1389 ::SetCursor(cursor
);
1391 return current_cursor
;
1393 return ::SetCursor(cursor
);
1396 LONG WINAPI
WebPluginDelegateImpl::RegEnumKeyExWPatch(
1397 HKEY key
, DWORD index
, LPWSTR name
, LPDWORD name_size
, LPDWORD reserved
,
1398 LPWSTR class_name
, LPDWORD class_size
, PFILETIME last_write_time
) {
1399 DWORD orig_size
= *name_size
;
1400 LONG rv
= RegEnumKeyExW(key
, index
, name
, name_size
, reserved
, class_name
,
1401 class_size
, last_write_time
);
1402 if (rv
== ERROR_SUCCESS
&&
1403 GetKeyPath(key
).find(L
"Microsoft\\MediaPlayer\\ShimInclusionList") !=
1404 std::wstring::npos
) {
1405 static const wchar_t kChromeExeName
[] = L
"chrome.exe";
1406 wcsncpy_s(name
, orig_size
, kChromeExeName
, arraysize(kChromeExeName
));
1408 std::min(orig_size
, static_cast<DWORD
>(arraysize(kChromeExeName
)));
1414 void WebPluginDelegateImpl::ImeCompositionUpdated(
1415 const base::string16
& text
,
1416 const std::vector
<int>& clauses
,
1417 const std::vector
<int>& target
,
1418 int cursor_position
) {
1420 plugin_ime_
.reset(new WebPluginIMEWin
);
1422 plugin_ime_
->CompositionUpdated(text
, clauses
, target
, cursor_position
);
1423 plugin_ime_
->SendEvents(instance());
1426 void WebPluginDelegateImpl::ImeCompositionCompleted(
1427 const base::string16
& text
) {
1429 plugin_ime_
.reset(new WebPluginIMEWin
);
1430 plugin_ime_
->CompositionCompleted(text
);
1431 plugin_ime_
->SendEvents(instance());
1434 bool WebPluginDelegateImpl::GetIMEStatus(int* input_type
,
1435 gfx::Rect
* caret_rect
) {
1438 return plugin_ime_
->GetStatus(input_type
, caret_rect
);
1442 FARPROC WINAPI
WebPluginDelegateImpl::GetProcAddressPatch(HMODULE module
,
1444 FARPROC imm_function
= WebPluginIMEWin::GetProcAddress(name
);
1446 return imm_function
;
1447 return ::GetProcAddress(module
, name
);
1450 HWND WINAPI
WebPluginDelegateImpl::WindowFromPointPatch(POINT point
) {
1451 HWND window
= WindowFromPoint(point
);
1452 if (::ScreenToClient(window
, &point
)) {
1453 HWND child
= ChildWindowFromPoint(window
, point
);
1454 if (::IsWindow(child
) &&
1455 ::GetProp(child
, content::kPluginDummyParentProperty
))
1461 void WebPluginDelegateImpl::HandleCaptureForMessage(HWND window
,
1463 if (gfx::GetClassName(window
) != base::string16(kNativeWindowClassName
))
1467 case WM_LBUTTONDOWN
:
1468 case WM_MBUTTONDOWN
:
1469 case WM_RBUTTONDOWN
:
1470 ::SetCapture(window
);
1471 // As per documentation the WM_PARENTNOTIFY message is sent to the parent
1472 // window chain if mouse input is received by the child window. However
1473 // the parent receives the WM_PARENTNOTIFY message only if we doubleclick
1474 // on the window. We send the WM_PARENTNOTIFY message for mouse input
1475 // messages to the parent to indicate that user action is expected.
1476 ::SendMessage(::GetParent(window
), WM_PARENTNOTIFY
, message
, 0);
1490 } // namespace content