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";
47 // The fastest we are willing to process WM_USER+1 events for Flash.
48 // Flash can easily exceed the limits of our CPU if we don't throttle it.
49 // The throttle has been chosen by testing various delays and compromising
50 // on acceptable Flash performance and reasonable CPU consumption.
52 // I'd like to make the throttle delay variable, based on the amount of
53 // time currently required to paint Flash plugins. There isn't a good
54 // way to count the time spent in aggregate plugin painting, however, so
55 // this seems to work well enough.
56 const int kFlashWMUSERMessageThrottleDelayMs
= 5;
58 // Flash displays popups in response to user clicks by posting a WM_USER
59 // message to the plugin window. The handler for this message displays
60 // the popup. To ensure that the popups allowed state is sent correctly
61 // to the renderer we reset the popups allowed state in a timer.
62 const int kWindowedPluginPopupTimerMs
= 50;
64 // The current instance of the plugin which entered the modal loop.
65 WebPluginDelegateImpl
* g_current_plugin_instance
= NULL
;
67 typedef std::deque
<MSG
> ThrottleQueue
;
68 base::LazyInstance
<ThrottleQueue
> g_throttle_queue
= LAZY_INSTANCE_INITIALIZER
;
70 base::LazyInstance
<std::map
<HWND
, WNDPROC
> > g_window_handle_proc_map
=
71 LAZY_INSTANCE_INITIALIZER
;
73 // Helper object for patching the TrackPopupMenu API.
74 base::LazyInstance
<base::win::IATPatchFunction
> g_iat_patch_track_popup_menu
=
75 LAZY_INSTANCE_INITIALIZER
;
77 // Helper object for patching the SetCursor API.
78 base::LazyInstance
<base::win::IATPatchFunction
> g_iat_patch_set_cursor
=
79 LAZY_INSTANCE_INITIALIZER
;
81 // Helper object for patching the RegEnumKeyExW API.
82 base::LazyInstance
<base::win::IATPatchFunction
> g_iat_patch_reg_enum_key_ex_w
=
83 LAZY_INSTANCE_INITIALIZER
;
85 // Helper object for patching the GetProcAddress API.
86 base::LazyInstance
<base::win::IATPatchFunction
> g_iat_patch_get_proc_address
=
87 LAZY_INSTANCE_INITIALIZER
;
89 base::LazyInstance
<base::win::IATPatchFunction
> g_iat_patch_window_from_point
=
90 LAZY_INSTANCE_INITIALIZER
;
92 // http://crbug.com/16114
93 // Enforces providing a valid device context in NPWindow, so that NPP_SetWindow
94 // is never called with NPNWindoTypeDrawable and NPWindow set to NULL.
95 // Doing so allows removing NPP_SetWindow call during painting a windowless
96 // plugin, which otherwise could trigger layout change while painting by
97 // invoking NPN_Evaluate. Which would cause bad, bad crashes. Bad crashes.
98 // TODO(dglazkov): If this approach doesn't produce regressions, move class to
99 // webplugin_delegate_impl.h and implement for other platforms.
100 class DrawableContextEnforcer
{
102 explicit DrawableContextEnforcer(NPWindow
* window
)
104 disposable_dc_(window
&& !window
->window
) {
105 // If NPWindow is NULL, create a device context with monochrome 1x1 surface
106 // and stuff it to NPWindow.
108 window_
->window
= CreateCompatibleDC(NULL
);
111 ~DrawableContextEnforcer() {
115 DeleteDC(static_cast<HDC
>(window_
->window
));
116 window_
->window
= NULL
;
124 // These are from ntddk.h
125 typedef LONG NTSTATUS
;
127 #ifndef STATUS_SUCCESS
128 #define STATUS_SUCCESS ((NTSTATUS)0x00000000L)
131 #ifndef STATUS_BUFFER_TOO_SMALL
132 #define STATUS_BUFFER_TOO_SMALL ((NTSTATUS)0xC0000023L)
135 typedef enum _KEY_INFORMATION_CLASS
{
140 KeyCachedInformation
,
141 KeyVirtualizationInformation
142 } KEY_INFORMATION_CLASS
;
144 typedef struct _KEY_NAME_INFORMATION
{
147 } KEY_NAME_INFORMATION
, *PKEY_NAME_INFORMATION
;
149 typedef DWORD (__stdcall
*ZwQueryKeyType
)(
151 int key_information_class
,
152 PVOID key_information
,
154 PULONG result_length
);
156 // Returns a key's full path.
157 std::wstring
GetKeyPath(HKEY key
) {
161 HMODULE dll
= GetModuleHandle(L
"ntdll.dll");
165 ZwQueryKeyType func
= reinterpret_cast<ZwQueryKeyType
>(
166 ::GetProcAddress(dll
, "ZwQueryKey"));
172 result
= func(key
, KeyNameInformation
, 0, 0, &size
);
173 if (result
!= STATUS_BUFFER_TOO_SMALL
)
176 scoped_ptr
<char[]> buffer(new char[size
]);
177 if (buffer
.get() == NULL
)
180 result
= func(key
, KeyNameInformation
, buffer
.get(), size
, &size
);
181 if (result
!= STATUS_SUCCESS
)
184 KEY_NAME_INFORMATION
* info
=
185 reinterpret_cast<KEY_NAME_INFORMATION
*>(buffer
.get());
186 return std::wstring(info
->Name
, info
->NameLength
/ sizeof(wchar_t));
189 uint32_t GetPluginMajorVersion(const WebPluginInfo
& plugin_info
) {
190 Version plugin_version
;
191 WebPluginInfo::CreateVersionFromString(plugin_info
.version
, &plugin_version
);
193 uint32_t major_version
= 0;
194 if (plugin_version
.IsValid())
195 major_version
= plugin_version
.components()[0];
197 return major_version
;
202 LRESULT CALLBACK
WebPluginDelegateImpl::HandleEventMessageFilterHook(
203 int code
, WPARAM wParam
, LPARAM lParam
) {
204 if (g_current_plugin_instance
) {
205 g_current_plugin_instance
->OnModalLoopEntered();
209 return CallNextHookEx(NULL
, code
, wParam
, lParam
);
212 LRESULT CALLBACK
WebPluginDelegateImpl::MouseHookProc(
213 int code
, WPARAM wParam
, LPARAM lParam
) {
214 if (code
== HC_ACTION
) {
215 MOUSEHOOKSTRUCT
* hook_struct
= reinterpret_cast<MOUSEHOOKSTRUCT
*>(lParam
);
217 HandleCaptureForMessage(hook_struct
->hwnd
, wParam
);
220 return CallNextHookEx(NULL
, code
, wParam
, lParam
);
223 WebPluginDelegateImpl::WebPluginDelegateImpl(WebPlugin
* plugin
,
224 PluginInstance
* instance
)
225 : windowed_handle_(NULL
),
226 windowed_did_set_window_(false),
230 plugin_wnd_proc_(NULL
),
232 is_calling_wndproc(false),
234 dummy_window_for_activation_(NULL
),
235 dummy_window_parent_(NULL
),
236 old_dummy_window_proc_(NULL
),
237 handle_event_message_filter_hook_(NULL
),
238 handle_event_pump_messages_event_(NULL
),
239 user_gesture_message_posted_(false),
241 handle_event_depth_(0),
242 first_set_window_call_(true),
243 plugin_has_focus_(false),
244 has_webkit_focus_(false),
245 containing_view_has_focus_(true),
246 creation_succeeded_(false),
247 user_gesture_msg_factory_(this) {
248 memset(&window_
, 0, sizeof(window_
));
250 const WebPluginInfo
& plugin_info
= instance_
->plugin_lib()->plugin_info();
251 base::string16 filename
=
252 base::ToLowerASCII(plugin_info
.path
.BaseName().value());
254 if (instance_
->mime_type() == kFlashPluginSwfMimeType
||
255 filename
== kFlashPlugin
) {
256 // Flash only requests windowless plugins if we return a Mozilla user
258 instance_
->set_use_mozilla_user_agent();
259 quirks_
|= PLUGIN_QUIRK_THROTTLE_WM_USER_PLUS_ONE
;
260 quirks_
|= PLUGIN_QUIRK_PATCH_SETCURSOR
;
261 quirks_
|= PLUGIN_QUIRK_ALWAYS_NOTIFY_SUCCESS
;
262 quirks_
|= PLUGIN_QUIRK_HANDLE_MOUSE_CAPTURE
;
263 quirks_
|= PLUGIN_QUIRK_EMULATE_IME
;
264 quirks_
|= PLUGIN_QUIRK_FAKE_WINDOW_FROM_POINT
;
265 } else if (filename
== kAcrobatReaderPlugin
) {
266 // Check for the version number above or equal 9.
267 uint32_t major_version
= GetPluginMajorVersion(plugin_info
);
268 if (major_version
>= 9) {
269 quirks_
|= PLUGIN_QUIRK_DIE_AFTER_UNLOAD
;
271 quirks_
|= PLUGIN_QUIRK_SETWINDOW_TWICE
;
273 quirks_
|= PLUGIN_QUIRK_BLOCK_NONSTANDARD_GETURL_REQUESTS
;
274 } else if (plugin_info
.name
.find(L
"Windows Media Player") !=
275 std::wstring::npos
) {
276 // Windows Media Player needs two NPP_SetWindow calls.
277 quirks_
|= PLUGIN_QUIRK_SETWINDOW_TWICE
;
279 // Windowless mode doesn't work in the WMP NPAPI plugin.
280 quirks_
|= PLUGIN_QUIRK_NO_WINDOWLESS
;
282 // The media player plugin sets its size on the first NPP_SetWindow call
283 // and never updates its size. We should call the underlying NPP_SetWindow
284 // only when we have the correct size.
285 quirks_
|= PLUGIN_QUIRK_IGNORE_FIRST_SETWINDOW_CALL
;
287 if (filename
== kOldWMPPlugin
) {
288 // Non-admin users on XP couldn't modify the key to force the new UI.
289 quirks_
|= PLUGIN_QUIRK_PATCH_REGENUMKEYEXW
;
291 } else if (instance_
->mime_type() == "audio/x-pn-realaudio-plugin" ||
292 filename
== kRealPlayerPlugin
) {
293 quirks_
|= PLUGIN_QUIRK_DONT_CALL_WND_PROC_RECURSIVELY
;
294 } else if (plugin_info
.name
.find(L
"VLC Multimedia Plugin") !=
295 std::wstring::npos
||
296 plugin_info
.name
.find(L
"VLC Multimedia Plug-in") !=
297 std::wstring::npos
) {
298 // VLC hangs on NPP_Destroy if we call NPP_SetWindow with a null window
300 quirks_
|= PLUGIN_QUIRK_DONT_SET_NULL_WINDOW_HANDLE_ON_DESTROY
;
301 uint32_t major_version
= GetPluginMajorVersion(plugin_info
);
302 if (major_version
== 0) {
303 // VLC 0.8.6d and 0.8.6e crash if multiple instances are created.
304 quirks_
|= PLUGIN_QUIRK_DONT_ALLOW_MULTIPLE_INSTANCES
;
306 } else if (filename
== kSilverlightPlugin
) {
307 // Explanation for this quirk can be found in
308 // WebPluginDelegateImpl::Initialize.
309 quirks_
|= PLUGIN_QUIRK_PATCH_SETCURSOR
;
310 } else if (plugin_info
.name
.find(L
"DivX Web Player") !=
311 std::wstring::npos
) {
312 // The divx plugin sets its size on the first NPP_SetWindow call and never
313 // updates its size. We should call the underlying NPP_SetWindow only when
314 // we have the correct size.
315 quirks_
|= PLUGIN_QUIRK_IGNORE_FIRST_SETWINDOW_CALL
;
319 WebPluginDelegateImpl::~WebPluginDelegateImpl() {
320 if (::IsWindow(dummy_window_for_activation_
)) {
321 WNDPROC current_wnd_proc
= reinterpret_cast<WNDPROC
>(
322 GetWindowLongPtr(dummy_window_for_activation_
, GWLP_WNDPROC
));
323 if (current_wnd_proc
== DummyWindowProc
) {
324 SetWindowLongPtr(dummy_window_for_activation_
,
326 reinterpret_cast<LONG_PTR
>(old_dummy_window_proc_
));
328 ::DestroyWindow(dummy_window_for_activation_
);
334 WindowedDestroyWindow();
336 if (handle_event_pump_messages_event_
) {
337 CloseHandle(handle_event_pump_messages_event_
);
341 bool WebPluginDelegateImpl::PlatformInitialize() {
342 plugin_
->SetWindow(windowed_handle_
);
345 CreateDummyWindowForActivation();
346 handle_event_pump_messages_event_
= CreateEvent(NULL
, TRUE
, FALSE
, NULL
);
347 plugin_
->SetWindowlessData(
348 handle_event_pump_messages_event_
,
349 reinterpret_cast<gfx::NativeViewId
>(dummy_window_for_activation_
));
352 // Windowless plugins call the WindowFromPoint API and passes the result of
353 // that to the TrackPopupMenu API call as the owner window. This causes the
354 // API to fail as the API expects the window handle to live on the same
355 // thread as the caller. It works in the other browsers as the plugin lives
356 // on the browser thread. Our workaround is to intercept the TrackPopupMenu
357 // API and replace the window handle with the dummy activation window.
358 if (windowless_
&& !g_iat_patch_track_popup_menu
.Pointer()->is_patched()) {
359 g_iat_patch_track_popup_menu
.Pointer()->Patch(
360 GetPluginPath().value().c_str(), "user32.dll", "TrackPopupMenu",
361 WebPluginDelegateImpl::TrackPopupMenuPatch
);
364 // Windowless plugins can set cursors by calling the SetCursor API. This
365 // works because the thread inputs of the browser UI thread and the plugin
366 // thread are attached. We intercept the SetCursor API for windowless
367 // plugins and remember the cursor being set. This is shipped over to the
368 // browser in the HandleEvent call, which ensures that the cursor does not
369 // change when a windowless plugin instance changes the cursor
370 // in a background tab.
371 if (windowless_
&& !g_iat_patch_set_cursor
.Pointer()->is_patched() &&
372 (quirks_
& PLUGIN_QUIRK_PATCH_SETCURSOR
)) {
373 g_iat_patch_set_cursor
.Pointer()->Patch(
374 GetPluginPath().value().c_str(), "user32.dll", "SetCursor",
375 WebPluginDelegateImpl::SetCursorPatch
);
378 // The windowed flash plugin has a bug which occurs when the plugin enters
379 // fullscreen mode. It basically captures the mouse on WM_LBUTTONDOWN and
380 // does not release capture correctly causing it to stop receiving
381 // subsequent mouse events. This problem is also seen in Safari where there
382 // is code to handle this in the wndproc. However the plugin subclasses the
383 // window again in WM_LBUTTONDOWN before entering full screen. As a result
384 // Safari does not receive the WM_LBUTTONUP message. To workaround this
385 // issue we use a per thread mouse hook. This bug does not occur in Firefox
386 // and opera. Firefox has code similar to Safari. It could well be a bug in
387 // the flash plugin, which only occurs in webkit based browsers.
388 if (quirks_
& PLUGIN_QUIRK_HANDLE_MOUSE_CAPTURE
) {
389 mouse_hook_
= SetWindowsHookEx(WH_MOUSE
, MouseHookProc
, NULL
,
390 GetCurrentThreadId());
393 // On XP, WMP will use its old UI unless a registry key under HKLM has the
394 // name of the current process. We do it in the installer for admin users,
395 // for the rest patch this function.
396 if ((quirks_
& PLUGIN_QUIRK_PATCH_REGENUMKEYEXW
) &&
397 base::win::GetVersion() == base::win::VERSION_XP
&&
398 (base::win::RegKey().Open(HKEY_LOCAL_MACHINE
,
399 L
"SOFTWARE\\Microsoft\\MediaPlayer\\ShimInclusionList\\chrome.exe",
400 KEY_READ
) != ERROR_SUCCESS
) &&
401 !g_iat_patch_reg_enum_key_ex_w
.Pointer()->is_patched()) {
402 g_iat_patch_reg_enum_key_ex_w
.Pointer()->Patch(
403 L
"wmpdxm.dll", "advapi32.dll", "RegEnumKeyExW",
404 WebPluginDelegateImpl::RegEnumKeyExWPatch
);
407 // Flash retrieves the pointers to IMM32 functions with GetProcAddress() calls
408 // and use them to retrieve IME data. We add a patch to this function so we
409 // can dispatch these IMM32 calls to the WebPluginIMEWin class, which emulates
410 // IMM32 functions for Flash.
411 if (!g_iat_patch_get_proc_address
.Pointer()->is_patched() &&
412 (quirks_
& PLUGIN_QUIRK_EMULATE_IME
)) {
413 g_iat_patch_get_proc_address
.Pointer()->Patch(
414 GetPluginPath().value().c_str(), "kernel32.dll", "GetProcAddress",
415 GetProcAddressPatch
);
418 if (windowless_
&& !g_iat_patch_window_from_point
.Pointer()->is_patched() &&
419 (quirks_
& PLUGIN_QUIRK_FAKE_WINDOW_FROM_POINT
)) {
420 g_iat_patch_window_from_point
.Pointer()->Patch(
421 GetPluginPath().value().c_str(), "user32.dll", "WindowFromPoint",
422 WebPluginDelegateImpl::WindowFromPointPatch
);
428 void WebPluginDelegateImpl::PlatformDestroyInstance() {
429 if (!instance_
->plugin_lib())
432 // Unpatch if this is the last plugin instance.
433 if (instance_
->plugin_lib()->instance_count() != 1)
436 if (g_iat_patch_set_cursor
.Pointer()->is_patched())
437 g_iat_patch_set_cursor
.Pointer()->Unpatch();
439 if (g_iat_patch_track_popup_menu
.Pointer()->is_patched())
440 g_iat_patch_track_popup_menu
.Pointer()->Unpatch();
442 if (g_iat_patch_reg_enum_key_ex_w
.Pointer()->is_patched())
443 g_iat_patch_reg_enum_key_ex_w
.Pointer()->Unpatch();
445 if (g_iat_patch_window_from_point
.Pointer()->is_patched())
446 g_iat_patch_window_from_point
.Pointer()->Unpatch();
449 UnhookWindowsHookEx(mouse_hook_
);
454 void WebPluginDelegateImpl::Paint(SkCanvas
* canvas
, const gfx::Rect
& rect
) {
455 if (windowless_
&& skia::SupportsPlatformPaint(canvas
)) {
456 skia::ScopedPlatformPaint
scoped_platform_paint(canvas
);
457 HDC hdc
= scoped_platform_paint
.GetPlatformSurface();
458 WindowlessPaint(hdc
, rect
);
462 bool WebPluginDelegateImpl::WindowedCreatePlugin() {
463 DCHECK(!windowed_handle_
);
465 RegisterNativeWindowClass();
467 // The window will be sized and shown later.
468 windowed_handle_
= CreateWindowEx(
469 WS_EX_LEFT
| WS_EX_LTRREADING
| WS_EX_RIGHTSCROLLBAR
,
470 kNativeWindowClassName
,
472 WS_POPUP
| WS_CLIPCHILDREN
| WS_CLIPSIBLINGS
,
479 GetModuleHandle(NULL
),
481 if (windowed_handle_
== 0)
484 // This is a tricky workaround for Issue 2673 in chromium "Flash: IME not
485 // available". To use IMEs in this window, we have to make Windows attach
486 // IMEs to this window (i.e. load IME DLLs, attach them to this process, and
487 // add their message hooks to this window). Windows attaches IMEs while this
488 // process creates a top-level window. On the other hand, to layout this
489 // window correctly in the given parent window (RenderWidgetHostViewWin or
490 // RenderWidgetHostViewAura), this window should be a child window of the
491 // parent window. To satisfy both of the above conditions, this code once
492 // creates a top-level window and change it to a child window of the parent
493 // window (in the browser process).
494 SetWindowLongPtr(windowed_handle_
, GWL_STYLE
,
495 WS_CHILD
| WS_CLIPCHILDREN
| WS_CLIPSIBLINGS
);
497 BOOL result
= SetProp(windowed_handle_
, kWebPluginDelegateProperty
, this);
498 DCHECK(result
== TRUE
) << "SetProp failed, last error = " << GetLastError();
500 // Calling SetWindowLongPtrA here makes the window proc ASCII, which is
501 // required by at least the Shockwave Director plugin.
502 SetWindowLongPtrA(windowed_handle_
,
504 reinterpret_cast<LONG_PTR
>(DefWindowProcA
));
509 void WebPluginDelegateImpl::WindowedDestroyWindow() {
510 if (windowed_handle_
!= NULL
) {
511 // Unsubclass the window.
512 WNDPROC current_wnd_proc
= reinterpret_cast<WNDPROC
>(
513 GetWindowLongPtr(windowed_handle_
, GWLP_WNDPROC
));
514 if (current_wnd_proc
== NativeWndProc
) {
515 SetWindowLongPtr(windowed_handle_
,
517 reinterpret_cast<LONG_PTR
>(plugin_wnd_proc_
));
520 plugin_
->WillDestroyWindow(windowed_handle_
);
522 DestroyWindow(windowed_handle_
);
523 windowed_handle_
= 0;
527 // Erase all messages in the queue destined for a particular window.
528 // When windows are closing, callers should use this function to clear
531 void WebPluginDelegateImpl::ClearThrottleQueueForWindow(HWND window
) {
532 ThrottleQueue
* throttle_queue
= g_throttle_queue
.Pointer();
534 ThrottleQueue::iterator it
;
535 for (it
= throttle_queue
->begin(); it
!= throttle_queue
->end(); ) {
536 if (it
->hwnd
== window
) {
537 it
= throttle_queue
->erase(it
);
544 // Delayed callback for processing throttled messages.
545 // Throttled messages are aggregated globally across all plugins.
547 void WebPluginDelegateImpl::OnThrottleMessage() {
548 // The current algorithm walks the list and processes the first
549 // message it finds for each plugin. It is important to service
550 // all active plugins with each pass through the throttle, otherwise
551 // we see video jankiness. Copy the set to notify before notifying
552 // since we may re-enter OnThrottleMessage from CallWindowProc!
553 ThrottleQueue
* throttle_queue
= g_throttle_queue
.Pointer();
554 ThrottleQueue notify_queue
;
555 std::set
<HWND
> processed
;
557 ThrottleQueue::iterator it
= throttle_queue
->begin();
558 while (it
!= throttle_queue
->end()) {
559 const MSG
& msg
= *it
;
560 if (processed
.find(msg
.hwnd
) == processed
.end()) {
561 processed
.insert(msg
.hwnd
);
562 notify_queue
.push_back(msg
);
563 it
= throttle_queue
->erase(it
);
569 // Due to re-entrancy, we must save our queue state now. Otherwise, we may
570 // self-post below, and *also* start up another delayed task when the first
571 // entry is pushed onto the queue in ThrottleMessage().
572 bool throttle_queue_was_empty
= throttle_queue
->empty();
574 for (it
= notify_queue
.begin(); it
!= notify_queue
.end(); ++it
) {
575 const MSG
& msg
= *it
;
576 WNDPROC proc
= reinterpret_cast<WNDPROC
>(msg
.time
);
577 // It is possible that the window was closed after we queued
578 // this message. This is a rare event; just verify the window
579 // is alive. (see also bug 1259488)
580 if (IsWindow(msg
.hwnd
))
581 CallWindowProc(proc
, msg
.hwnd
, msg
.message
, msg
.wParam
, msg
.lParam
);
584 if (!throttle_queue_was_empty
) {
585 base::MessageLoop::current()->PostDelayedTask(
587 base::Bind(&WebPluginDelegateImpl::OnThrottleMessage
),
588 base::TimeDelta::FromMilliseconds(kFlashWMUSERMessageThrottleDelayMs
));
592 // Schedule a windows message for delivery later.
594 void WebPluginDelegateImpl::ThrottleMessage(WNDPROC proc
, HWND hwnd
,
595 UINT message
, WPARAM wParam
,
598 msg
.time
= reinterpret_cast<DWORD
>(proc
);
600 msg
.message
= message
;
604 ThrottleQueue
* throttle_queue
= g_throttle_queue
.Pointer();
606 throttle_queue
->push_back(msg
);
608 if (throttle_queue
->size() == 1) {
609 base::MessageLoop::current()->PostDelayedTask(
611 base::Bind(&WebPluginDelegateImpl::OnThrottleMessage
),
612 base::TimeDelta::FromMilliseconds(kFlashWMUSERMessageThrottleDelayMs
));
616 // We go out of our way to find the hidden windows created by Flash for
617 // windowless plugins. We throttle the rate at which they deliver messages
618 // so that they will not consume outrageous amounts of CPU.
620 LRESULT CALLBACK
WebPluginDelegateImpl::FlashWindowlessWndProc(
621 HWND hwnd
, UINT message
, WPARAM wparam
, LPARAM lparam
) {
622 std::map
<HWND
, WNDPROC
>::iterator index
=
623 g_window_handle_proc_map
.Get().find(hwnd
);
625 WNDPROC old_proc
= (*index
).second
;
630 WebPluginDelegateImpl::ClearThrottleQueueForWindow(hwnd
);
631 g_window_handle_proc_map
.Get().erase(index
);
634 // Flash may flood the message queue with WM_USER+1 message causing 100% CPU
635 // usage. See https://bugzilla.mozilla.org/show_bug.cgi?id=132759. We
636 // prevent this by throttling the messages.
638 WebPluginDelegateImpl::ThrottleMessage(old_proc
, hwnd
, message
, wparam
,
647 return CallWindowProc(old_proc
, hwnd
, message
, wparam
, lparam
);
650 LRESULT CALLBACK
WebPluginDelegateImpl::DummyWindowProc(
651 HWND hwnd
, UINT message
, WPARAM w_param
, LPARAM l_param
) {
652 WebPluginDelegateImpl
* delegate
= reinterpret_cast<WebPluginDelegateImpl
*>(
653 GetProp(hwnd
, kWebPluginDelegateProperty
));
655 if (message
== WM_WINDOWPOSCHANGING
) {
656 // We need to know when the dummy window is parented because windowless
657 // plugins need the parent window for things like menus. There's no message
658 // for a parent being changed, but a WM_WINDOWPOSCHANGING is sent so we
659 // check every time we get it.
660 // For non-aura builds, this never changes since RenderWidgetHostViewWin's
661 // window is constant. For aura builds, this changes every time the tab gets
662 // dragged to a new window.
663 HWND parent
= GetParent(hwnd
);
664 if (parent
!= delegate
->dummy_window_parent_
) {
665 delegate
->dummy_window_parent_
= parent
;
667 // Set the containing window handle as the instance window handle. This is
668 // what Safari does. Not having a valid window handle causes subtle bugs
669 // with plugins which retrieve the window handle and use it for things
670 // like context menus. The window handle can be retrieved via
671 // NPN_GetValue of NPNVnetscapeWindow.
672 delegate
->instance_
->set_window_handle(parent
);
674 // The plugin caches the result of NPNVnetscapeWindow when we originally
675 // called NPP_SetWindow, so force it to get the new value.
676 delegate
->WindowlessSetWindow();
678 } else if (message
== WM_NCDESTROY
) {
679 RemoveProp(hwnd
, kWebPluginDelegateProperty
);
681 return CallWindowProc(
682 delegate
->old_dummy_window_proc_
, hwnd
, message
, w_param
, l_param
);
685 // Callback for enumerating the Flash windows.
686 BOOL CALLBACK
EnumFlashWindows(HWND window
, LPARAM arg
) {
687 WNDPROC wnd_proc
= reinterpret_cast<WNDPROC
>(arg
);
688 TCHAR class_name
[1024];
689 if (!RealGetWindowClass(window
, class_name
,
690 sizeof(class_name
)/sizeof(TCHAR
))) {
691 LOG(ERROR
) << "RealGetWindowClass failure: " << GetLastError();
695 if (wcscmp(class_name
, L
"SWFlash_PlaceholderX"))
698 WNDPROC current_wnd_proc
= reinterpret_cast<WNDPROC
>(
699 GetWindowLongPtr(window
, GWLP_WNDPROC
));
700 if (current_wnd_proc
!= wnd_proc
) {
701 WNDPROC old_flash_proc
= reinterpret_cast<WNDPROC
>(SetWindowLongPtr(
702 window
, GWLP_WNDPROC
,
703 reinterpret_cast<LONG_PTR
>(wnd_proc
)));
704 DCHECK(old_flash_proc
);
705 g_window_handle_proc_map
.Get()[window
] = old_flash_proc
;
711 bool WebPluginDelegateImpl::CreateDummyWindowForActivation() {
712 DCHECK(!dummy_window_for_activation_
);
714 dummy_window_for_activation_
= CreateWindowEx(
717 kDummyActivationWindowName
,
723 // We don't know the parent of the dummy window yet, so just set it to the
724 // desktop and it'll get parented by the browser.
727 GetModuleHandle(NULL
),
730 if (dummy_window_for_activation_
== 0)
733 BOOL result
= SetProp(dummy_window_for_activation_
,
734 kWebPluginDelegateProperty
, this);
735 DCHECK(result
== TRUE
) << "SetProp failed, last error = " << GetLastError();
736 old_dummy_window_proc_
= reinterpret_cast<WNDPROC
>(SetWindowLongPtr(
737 dummy_window_for_activation_
, GWLP_WNDPROC
,
738 reinterpret_cast<LONG_PTR
>(DummyWindowProc
)));
740 // Flash creates background windows which use excessive CPU in our
741 // environment; we wrap these windows and throttle them so that they don't
743 if (!EnumThreadWindows(::GetCurrentThreadId(), EnumFlashWindows
,
744 reinterpret_cast<LPARAM
>(
745 &WebPluginDelegateImpl::FlashWindowlessWndProc
))) {
746 // Log that this happened. Flash will still work; it just means the
747 // throttle isn't installed (and Flash will use more CPU).
749 LOG(ERROR
) << "Failed to wrap all windowless Flash windows";
754 bool WebPluginDelegateImpl::WindowedReposition(
755 const gfx::Rect
& window_rect_in_dip
,
756 const gfx::Rect
& clip_rect_in_dip
) {
757 if (!windowed_handle_
) {
762 gfx::Rect window_rect
= gfx::win::DIPToScreenRect(window_rect_in_dip
);
763 gfx::Rect clip_rect
= gfx::win::DIPToScreenRect(clip_rect_in_dip
);
764 if (window_rect_
== window_rect
&& clip_rect_
== clip_rect
)
767 // We only set the plugin's size here. Its position is moved elsewhere, which
768 // allows the window moves/scrolling/clipping to be synchronized with the page
769 // and other windows.
770 // If the plugin window has no parent, then don't focus it because it isn't
771 // being displayed anywhere. See:
772 // http://code.google.com/p/chromium/issues/detail?id=32658
773 if (window_rect
.size() != window_rect_
.size()) {
774 UINT flags
= SWP_NOMOVE
| SWP_NOZORDER
;
775 if (!GetParent(windowed_handle_
))
776 flags
|= SWP_NOACTIVATE
;
777 ::SetWindowPos(windowed_handle_
,
782 window_rect
.height(),
786 window_rect_
= window_rect
;
787 clip_rect_
= clip_rect
;
789 // Ensure that the entire window gets repainted.
790 ::InvalidateRect(windowed_handle_
, NULL
, FALSE
);
795 void WebPluginDelegateImpl::WindowedSetWindow() {
796 if (!instance_
.get())
799 if (!windowed_handle_
) {
804 instance()->set_window_handle(windowed_handle_
);
806 DCHECK(!instance()->windowless());
808 window_
.clipRect
.top
= std::max(0, clip_rect_
.y());
809 window_
.clipRect
.left
= std::max(0, clip_rect_
.x());
810 window_
.clipRect
.bottom
= std::max(0, clip_rect_
.y() + clip_rect_
.height());
811 window_
.clipRect
.right
= std::max(0, clip_rect_
.x() + clip_rect_
.width());
812 window_
.height
= window_rect_
.height();
813 window_
.width
= window_rect_
.width();
817 window_
.window
= windowed_handle_
;
818 window_
.type
= NPWindowTypeWindow
;
820 // Reset this flag before entering the instance in case of side-effects.
821 windowed_did_set_window_
= true;
823 instance()->NPP_SetWindow(&window_
);
824 if (quirks_
& PLUGIN_QUIRK_SETWINDOW_TWICE
)
825 instance()->NPP_SetWindow(&window_
);
827 WNDPROC current_wnd_proc
= reinterpret_cast<WNDPROC
>(
828 GetWindowLongPtr(windowed_handle_
, GWLP_WNDPROC
));
829 if (current_wnd_proc
!= NativeWndProc
) {
830 plugin_wnd_proc_
= reinterpret_cast<WNDPROC
>(
831 SetWindowLongPtr(windowed_handle_
,
833 reinterpret_cast<LONG_PTR
>(NativeWndProc
)));
837 ATOM
WebPluginDelegateImpl::RegisterNativeWindowClass() {
838 static bool have_registered_window_class
= false;
839 if (have_registered_window_class
== true)
842 have_registered_window_class
= true;
845 wcex
.cbSize
= sizeof(WNDCLASSEX
);
846 wcex
.style
= CS_DBLCLKS
;
847 wcex
.lpfnWndProc
= WrapperWindowProc
;
850 wcex
.hInstance
= GetModuleHandle(NULL
);
853 // Some plugins like windows media player 11 create child windows parented
854 // by our plugin window, where the media content is rendered. These plugins
855 // dont implement WM_ERASEBKGND, which causes painting issues, when the
856 // window where the media is rendered is moved around. DefWindowProc does
857 // implement WM_ERASEBKGND correctly if we have a valid background brush.
858 wcex
.hbrBackground
= reinterpret_cast<HBRUSH
>(COLOR_WINDOW
+1);
859 wcex
.lpszMenuName
= 0;
860 wcex
.lpszClassName
= kNativeWindowClassName
;
863 return RegisterClassEx(&wcex
);
866 LRESULT CALLBACK
WebPluginDelegateImpl::WrapperWindowProc(
867 HWND hWnd
, UINT message
, WPARAM wParam
, LPARAM lParam
) {
868 // This is another workaround for Issue 2673 in chromium "Flash: IME not
869 // available". Somehow, the CallWindowProc() function does not dispatch
870 // window messages when its first parameter is a handle representing the
871 // DefWindowProc() function. To avoid this problem, this code creates a
872 // wrapper function which just encapsulates the DefWindowProc() function
873 // and set it as the window procedure of a windowed plugin.
874 return DefWindowProc(hWnd
, message
, wParam
, lParam
);
877 // Returns true if the message passed in corresponds to a user gesture.
878 static bool IsUserGestureMessage(unsigned int message
) {
897 LRESULT CALLBACK
WebPluginDelegateImpl::NativeWndProc(
898 HWND hwnd
, UINT message
, WPARAM wparam
, LPARAM lparam
) {
899 WebPluginDelegateImpl
* delegate
= reinterpret_cast<WebPluginDelegateImpl
*>(
900 GetProp(hwnd
, kWebPluginDelegateProperty
));
906 if (message
== delegate
->last_message_
&&
907 delegate
->GetQuirks() & PLUGIN_QUIRK_DONT_CALL_WND_PROC_RECURSIVELY
&&
908 delegate
->is_calling_wndproc
) {
909 // Real may go into a state where it recursively dispatches the same event
910 // when subclassed. See https://bugzilla.mozilla.org/show_bug.cgi?id=192914
911 // We only do the recursive check for Real because it's possible and valid
912 // for a plugin to synchronously dispatch a message to itself such that it
913 // looks like it's in recursion.
917 // Flash may flood the message queue with WM_USER+1 message causing 100% CPU
918 // usage. See https://bugzilla.mozilla.org/show_bug.cgi?id=132759. We
919 // prevent this by throttling the messages.
920 if (message
== WM_USER
+ 1 &&
921 delegate
->GetQuirks() & PLUGIN_QUIRK_THROTTLE_WM_USER_PLUS_ONE
) {
922 WebPluginDelegateImpl::ThrottleMessage(delegate
->plugin_wnd_proc_
, hwnd
,
923 message
, wparam
, lparam
);
928 uint32 old_message
= delegate
->last_message_
;
929 delegate
->last_message_
= message
;
931 static UINT custom_msg
= RegisterWindowMessage(kPaintMessageName
);
932 if (message
== custom_msg
) {
933 // Get the invalid rect which is in screen coordinates and convert to
934 // window coordinates.
935 gfx::Rect invalid_rect
;
936 invalid_rect
.set_x(static_cast<short>(LOWORD(wparam
)));
937 invalid_rect
.set_y(static_cast<short>(HIWORD(wparam
)));
938 invalid_rect
.set_width(static_cast<short>(LOWORD(lparam
)));
939 invalid_rect
.set_height(static_cast<short>(HIWORD(lparam
)));
942 GetWindowRect(hwnd
, &window_rect
);
943 invalid_rect
.Offset(-window_rect
.left
, -window_rect
.top
);
945 // The plugin window might have non-client area. If we don't pass in
946 // RDW_FRAME then the children don't receive WM_NCPAINT messages while
947 // scrolling, which causes painting problems (http://b/issue?id=923945).
948 uint32 flags
= RDW_INVALIDATE
| RDW_ALLCHILDREN
| RDW_FRAME
;
950 // If a plugin (like Google Earth or Java) has child windows that are hosted
951 // in a different process, then RedrawWindow with UPDATENOW will
952 // synchronously wait for this call to complete. Some messages are pumped
953 // but not others, which could lead to a deadlock. So avoid reentrancy by
954 // only synchronously calling RedrawWindow once at a time.
955 if (old_message
!= custom_msg
)
956 flags
|= RDW_UPDATENOW
;
957 RECT rect
= invalid_rect
.ToRECT();
958 RedrawWindow(hwnd
, &rect
, NULL
, flags
);
961 delegate
->is_calling_wndproc
= true;
963 if (!delegate
->user_gesture_message_posted_
&&
964 IsUserGestureMessage(message
)) {
965 delegate
->user_gesture_message_posted_
= true;
967 delegate
->instance()->PushPopupsEnabledState(true);
969 base::MessageLoop::current()->PostDelayedTask(
971 base::Bind(&WebPluginDelegateImpl::OnUserGestureEnd
,
972 delegate
->user_gesture_msg_factory_
.GetWeakPtr()),
973 base::TimeDelta::FromMilliseconds(kWindowedPluginPopupTimerMs
));
976 HandleCaptureForMessage(hwnd
, message
);
978 // Maintain a local/global stack for the g_current_plugin_instance variable
979 // as this may be a nested invocation.
980 WebPluginDelegateImpl
* last_plugin_instance
= g_current_plugin_instance
;
982 g_current_plugin_instance
= delegate
;
984 result
= CallWindowProc(
985 delegate
->plugin_wnd_proc_
, hwnd
, message
, wparam
, lparam
);
987 // The plugin instance may have been destroyed in the CallWindowProc call
988 // above. This will also destroy the plugin window. Before attempting to
989 // access the WebPluginDelegateImpl instance we validate if the window is
991 if (::IsWindow(hwnd
))
992 delegate
->is_calling_wndproc
= false;
994 g_current_plugin_instance
= last_plugin_instance
;
996 if (message
== WM_NCDESTROY
) {
997 RemoveProp(hwnd
, kWebPluginDelegateProperty
);
998 ClearThrottleQueueForWindow(hwnd
);
1001 if (::IsWindow(hwnd
))
1002 delegate
->last_message_
= old_message
;
1006 void WebPluginDelegateImpl::WindowlessUpdateGeometry(
1007 const gfx::Rect
& window_rect
,
1008 const gfx::Rect
& clip_rect
) {
1009 bool window_rect_changed
= (window_rect_
!= window_rect
);
1010 // Only resend to the instance if the geometry has changed.
1011 if (!window_rect_changed
&& clip_rect
== clip_rect_
)
1014 clip_rect_
= clip_rect
;
1015 window_rect_
= window_rect
;
1017 WindowlessSetWindow();
1019 if (window_rect_changed
) {
1020 WINDOWPOS win_pos
= {0};
1021 win_pos
.x
= window_rect_
.x();
1022 win_pos
.y
= window_rect_
.y();
1023 win_pos
.cx
= window_rect_
.width();
1024 win_pos
.cy
= window_rect_
.height();
1026 NPEvent pos_changed_event
;
1027 pos_changed_event
.event
= WM_WINDOWPOSCHANGED
;
1028 pos_changed_event
.wParam
= 0;
1029 pos_changed_event
.lParam
= reinterpret_cast<uintptr_t>(&win_pos
);
1031 instance()->NPP_HandleEvent(&pos_changed_event
);
1035 void WebPluginDelegateImpl::WindowlessPaint(HDC hdc
,
1036 const gfx::Rect
& damage_rect
) {
1039 RECT damage_rect_win
;
1040 damage_rect_win
.left
= damage_rect
.x(); // + window_rect_.x();
1041 damage_rect_win
.top
= damage_rect
.y(); // + window_rect_.y();
1042 damage_rect_win
.right
= damage_rect_win
.left
+ damage_rect
.width();
1043 damage_rect_win
.bottom
= damage_rect_win
.top
+ damage_rect
.height();
1045 // Save away the old HDC as this could be a nested invocation.
1046 void* old_dc
= window_
.window
;
1047 window_
.window
= hdc
;
1049 NPEvent paint_event
;
1050 paint_event
.event
= WM_PAINT
;
1051 paint_event
.wParam
= PtrToUlong(hdc
);
1052 paint_event
.lParam
= reinterpret_cast<uintptr_t>(&damage_rect_win
);
1053 instance()->NPP_HandleEvent(&paint_event
);
1054 window_
.window
= old_dc
;
1057 void WebPluginDelegateImpl::WindowlessSetWindow() {
1061 if (window_rect_
.IsEmpty()) // wait for geometry to be set.
1064 DCHECK(instance()->windowless());
1066 window_
.clipRect
.top
= clip_rect_
.y();
1067 window_
.clipRect
.left
= clip_rect_
.x();
1068 window_
.clipRect
.bottom
= clip_rect_
.y() + clip_rect_
.height();
1069 window_
.clipRect
.right
= clip_rect_
.x() + clip_rect_
.width();
1070 window_
.height
= window_rect_
.height();
1071 window_
.width
= window_rect_
.width();
1072 window_
.x
= window_rect_
.x();
1073 window_
.y
= window_rect_
.y();
1074 window_
.type
= NPWindowTypeDrawable
;
1075 DrawableContextEnforcer
enforcer(&window_
);
1077 NPError err
= instance()->NPP_SetWindow(&window_
);
1078 DCHECK(err
== NPERR_NO_ERROR
);
1081 bool WebPluginDelegateImpl::PlatformSetPluginHasFocus(bool focused
) {
1082 DCHECK(instance()->windowless());
1084 NPEvent focus_event
;
1085 focus_event
.event
= focused
? WM_SETFOCUS
: WM_KILLFOCUS
;
1086 focus_event
.wParam
= 0;
1087 focus_event
.lParam
= 0;
1089 instance()->NPP_HandleEvent(&focus_event
);
1093 static bool NPEventFromWebMouseEvent(const WebMouseEvent
& event
,
1094 NPEvent
* np_event
) {
1096 static_cast<uint32
>(MAKELPARAM(event
.windowX
, event
.windowY
));
1097 np_event
->wParam
= 0;
1099 if (event
.modifiers
& WebInputEvent::ControlKey
)
1100 np_event
->wParam
|= MK_CONTROL
;
1101 if (event
.modifiers
& WebInputEvent::ShiftKey
)
1102 np_event
->wParam
|= MK_SHIFT
;
1103 if (event
.modifiers
& WebInputEvent::LeftButtonDown
)
1104 np_event
->wParam
|= MK_LBUTTON
;
1105 if (event
.modifiers
& WebInputEvent::MiddleButtonDown
)
1106 np_event
->wParam
|= MK_MBUTTON
;
1107 if (event
.modifiers
& WebInputEvent::RightButtonDown
)
1108 np_event
->wParam
|= MK_RBUTTON
;
1110 switch (event
.type
) {
1111 case WebInputEvent::MouseMove
:
1112 case WebInputEvent::MouseLeave
:
1113 case WebInputEvent::MouseEnter
:
1114 np_event
->event
= WM_MOUSEMOVE
;
1116 case WebInputEvent::MouseDown
:
1117 switch (event
.button
) {
1118 case WebMouseEvent::ButtonLeft
:
1119 np_event
->event
= WM_LBUTTONDOWN
;
1121 case WebMouseEvent::ButtonMiddle
:
1122 np_event
->event
= WM_MBUTTONDOWN
;
1124 case WebMouseEvent::ButtonRight
:
1125 np_event
->event
= WM_RBUTTONDOWN
;
1127 case WebMouseEvent::ButtonNone
:
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
;
1142 case WebMouseEvent::ButtonNone
:
1152 static bool NPEventFromWebKeyboardEvent(const WebKeyboardEvent
& event
,
1153 NPEvent
* np_event
) {
1154 np_event
->wParam
= event
.windowsKeyCode
;
1156 switch (event
.type
) {
1157 case WebInputEvent::KeyDown
:
1158 np_event
->event
= WM_KEYDOWN
;
1159 np_event
->lParam
= 0;
1161 case WebInputEvent::Char
:
1162 np_event
->event
= WM_CHAR
;
1163 np_event
->lParam
= 0;
1165 case WebInputEvent::KeyUp
:
1166 np_event
->event
= WM_KEYUP
;
1167 np_event
->lParam
= 0x8000;
1175 static bool NPEventFromWebInputEvent(const WebInputEvent
& event
,
1176 NPEvent
* np_event
) {
1177 switch (event
.type
) {
1178 case WebInputEvent::MouseMove
:
1179 case WebInputEvent::MouseLeave
:
1180 case WebInputEvent::MouseEnter
:
1181 case WebInputEvent::MouseDown
:
1182 case WebInputEvent::MouseUp
:
1183 if (event
.size
< sizeof(WebMouseEvent
)) {
1187 return NPEventFromWebMouseEvent(
1188 *static_cast<const WebMouseEvent
*>(&event
), np_event
);
1189 case WebInputEvent::KeyDown
:
1190 case WebInputEvent::Char
:
1191 case WebInputEvent::KeyUp
:
1192 if (event
.size
< sizeof(WebKeyboardEvent
)) {
1196 return NPEventFromWebKeyboardEvent(
1197 *static_cast<const WebKeyboardEvent
*>(&event
), np_event
);
1203 bool WebPluginDelegateImpl::PlatformHandleInputEvent(
1204 const WebInputEvent
& event
, WebCursor::CursorInfo
* cursor_info
) {
1205 DCHECK(cursor_info
!= NULL
);
1208 if (!NPEventFromWebInputEvent(event
, &np_event
)) {
1212 // Allow this plugin to access this IME emulator through IMM32 API while the
1213 // plugin is processing this event.
1214 if (GetQuirks() & PLUGIN_QUIRK_EMULATE_IME
) {
1216 plugin_ime_
.reset(new WebPluginIMEWin
);
1218 WebPluginIMEWin::ScopedLock
lock(
1219 event
.isKeyboardEventType(event
.type
) ? plugin_ime_
.get() : NULL
);
1221 HWND last_focus_window
= NULL
;
1223 if (ShouldTrackEventForModalLoops(&np_event
)) {
1224 // A windowless plugin can enter a modal loop in a NPP_HandleEvent call.
1225 // For e.g. Flash puts up a context menu when we right click on the
1226 // windowless plugin area. We detect this by setting up a message filter
1227 // hook pror to calling NPP_HandleEvent on the plugin and unhook on
1228 // return from NPP_HandleEvent. If the plugin does enter a modal loop
1229 // in that context we unhook on receiving the first notification in
1230 // the message filter hook.
1231 handle_event_message_filter_hook_
=
1232 SetWindowsHookEx(WH_MSGFILTER
, HandleEventMessageFilterHook
, NULL
,
1233 GetCurrentThreadId());
1234 // To ensure that the plugin receives keyboard events we set focus to the
1236 // TODO(iyengar) We need a framework in the renderer to identify which
1237 // windowless plugin is under the mouse and to handle this. This would
1238 // also require some changes in RenderWidgetHost to detect this in the
1239 // WM_MOUSEACTIVATE handler and inform the renderer accordingly.
1240 bool valid
= GetParent(dummy_window_for_activation_
) != GetDesktopWindow();
1242 last_focus_window
= ::SetFocus(dummy_window_for_activation_
);
1244 NOTREACHED() << "Dummy window not parented";
1248 bool old_task_reentrancy_state
=
1249 base::MessageLoop::current()->NestableTasksAllowed();
1251 // Maintain a local/global stack for the g_current_plugin_instance variable
1252 // as this may be a nested invocation.
1253 WebPluginDelegateImpl
* last_plugin_instance
= g_current_plugin_instance
;
1255 g_current_plugin_instance
= this;
1257 handle_event_depth_
++;
1259 bool popups_enabled
= false;
1261 if (IsUserGestureMessage(np_event
.event
)) {
1262 instance()->PushPopupsEnabledState(true);
1263 popups_enabled
= true;
1266 bool ret
= instance()->NPP_HandleEvent(&np_event
) != 0;
1268 if (popups_enabled
) {
1269 instance()->PopPopupsEnabledState();
1272 // Flash and SilverLight always return false, even when they swallow the
1273 // event. Flash does this because it passes the event to its window proc,
1274 // which is supposed to return 0 if an event was handled. There are few
1275 // exceptions, such as IME, where it sometimes returns true.
1278 if (np_event
.event
== WM_MOUSEMOVE
) {
1279 current_windowless_cursor_
.InitFromExternalCursor(GetCursor());
1280 // Snag a reference to the current cursor ASAP in case the plugin modified
1281 // it. There is a nasty race condition here with the multiprocess browser
1282 // as someone might be setting the cursor in the main process as well.
1283 current_windowless_cursor_
.GetCursorInfo(cursor_info
);
1286 handle_event_depth_
--;
1288 g_current_plugin_instance
= last_plugin_instance
;
1290 // We could have multiple NPP_HandleEvent calls nested together in case
1291 // the plugin enters a modal loop. Reset the pump messages event when
1292 // the outermost NPP_HandleEvent call unwinds.
1293 if (handle_event_depth_
== 0) {
1294 ResetEvent(handle_event_pump_messages_event_
);
1297 // If we didn't enter a modal loop, need to unhook the filter.
1298 if (handle_event_message_filter_hook_
) {
1299 UnhookWindowsHookEx(handle_event_message_filter_hook_
);
1300 handle_event_message_filter_hook_
= NULL
;
1303 if (::IsWindow(last_focus_window
)) {
1304 // Restore the nestable tasks allowed state in the message loop and reset
1305 // the os modal loop state as the plugin returned from the TrackPopupMenu
1307 base::MessageLoop::current()->SetNestableTasksAllowed(
1308 old_task_reentrancy_state
);
1309 base::MessageLoop::current()->set_os_modal_loop(false);
1310 // The Flash plugin at times sets focus to its hidden top level window
1311 // with class name SWFlash_PlaceholderX. This causes the chrome browser
1312 // window to receive a WM_ACTIVATEAPP message as a top level window from
1313 // another thread is now active. We end up in a state where the chrome
1314 // browser window is not active even though the user clicked on it.
1315 // Our workaround for this is to send over a raw
1316 // WM_LBUTTONDOWN/WM_LBUTTONUP combination to the last focus window, which
1318 if (dummy_window_for_activation_
!= ::GetFocus()) {
1319 INPUT input_info
= {0};
1320 input_info
.type
= INPUT_MOUSE
;
1321 input_info
.mi
.dwFlags
= MOUSEEVENTF_LEFTDOWN
;
1322 ::SendInput(1, &input_info
, sizeof(INPUT
));
1324 input_info
.type
= INPUT_MOUSE
;
1325 input_info
.mi
.dwFlags
= MOUSEEVENTF_LEFTUP
;
1326 ::SendInput(1, &input_info
, sizeof(INPUT
));
1328 ::SetFocus(last_focus_window
);
1335 void WebPluginDelegateImpl::OnModalLoopEntered() {
1336 DCHECK(handle_event_pump_messages_event_
!= NULL
);
1337 SetEvent(handle_event_pump_messages_event_
);
1339 base::MessageLoop::current()->SetNestableTasksAllowed(true);
1340 base::MessageLoop::current()->set_os_modal_loop(true);
1342 UnhookWindowsHookEx(handle_event_message_filter_hook_
);
1343 handle_event_message_filter_hook_
= NULL
;
1346 bool WebPluginDelegateImpl::ShouldTrackEventForModalLoops(NPEvent
* event
) {
1347 if (event
->event
== WM_RBUTTONDOWN
)
1352 void WebPluginDelegateImpl::OnUserGestureEnd() {
1353 user_gesture_message_posted_
= false;
1354 instance()->PopPopupsEnabledState();
1357 BOOL WINAPI
WebPluginDelegateImpl::TrackPopupMenuPatch(
1358 HMENU menu
, unsigned int flags
, int x
, int y
, int reserved
,
1359 HWND window
, const RECT
* rect
) {
1361 if (g_current_plugin_instance
) {
1362 unsigned long window_process_id
= 0;
1363 unsigned long window_thread_id
=
1364 GetWindowThreadProcessId(window
, &window_process_id
);
1365 // TrackPopupMenu fails if the window passed in belongs to a different
1367 if (::GetCurrentThreadId() != window_thread_id
) {
1369 GetParent(g_current_plugin_instance
->dummy_window_for_activation_
) !=
1372 window
= g_current_plugin_instance
->dummy_window_for_activation_
;
1374 NOTREACHED() << "Dummy window not parented";
1379 BOOL result
= TrackPopupMenu(menu
, flags
, x
, y
, reserved
, window
, rect
);
1383 HCURSOR WINAPI
WebPluginDelegateImpl::SetCursorPatch(HCURSOR cursor
) {
1384 // The windowless flash plugin periodically calls SetCursor in a wndproc
1385 // instantiated on the plugin thread. This causes annoying cursor flicker
1386 // when the mouse is moved on a foreground tab, with a windowless plugin
1387 // instance in a background tab. We just ignore the call here.
1388 if (!g_current_plugin_instance
) {
1389 HCURSOR current_cursor
= GetCursor();
1390 if (current_cursor
!= cursor
) {
1391 ::SetCursor(cursor
);
1393 return current_cursor
;
1395 return ::SetCursor(cursor
);
1398 LONG WINAPI
WebPluginDelegateImpl::RegEnumKeyExWPatch(
1399 HKEY key
, DWORD index
, LPWSTR name
, LPDWORD name_size
, LPDWORD reserved
,
1400 LPWSTR class_name
, LPDWORD class_size
, PFILETIME last_write_time
) {
1401 DWORD orig_size
= *name_size
;
1402 LONG rv
= RegEnumKeyExW(key
, index
, name
, name_size
, reserved
, class_name
,
1403 class_size
, last_write_time
);
1404 if (rv
== ERROR_SUCCESS
&&
1405 GetKeyPath(key
).find(L
"Microsoft\\MediaPlayer\\ShimInclusionList") !=
1406 std::wstring::npos
) {
1407 static const wchar_t kChromeExeName
[] = L
"chrome.exe";
1408 wcsncpy_s(name
, orig_size
, kChromeExeName
, arraysize(kChromeExeName
));
1410 std::min(orig_size
, static_cast<DWORD
>(arraysize(kChromeExeName
)));
1416 void WebPluginDelegateImpl::ImeCompositionUpdated(
1417 const base::string16
& text
,
1418 const std::vector
<int>& clauses
,
1419 const std::vector
<int>& target
,
1420 int cursor_position
) {
1422 plugin_ime_
.reset(new WebPluginIMEWin
);
1424 plugin_ime_
->CompositionUpdated(text
, clauses
, target
, cursor_position
);
1425 plugin_ime_
->SendEvents(instance());
1428 void WebPluginDelegateImpl::ImeCompositionCompleted(
1429 const base::string16
& text
) {
1431 plugin_ime_
.reset(new WebPluginIMEWin
);
1432 plugin_ime_
->CompositionCompleted(text
);
1433 plugin_ime_
->SendEvents(instance());
1436 bool WebPluginDelegateImpl::GetIMEStatus(int* input_type
,
1437 gfx::Rect
* caret_rect
) {
1440 return plugin_ime_
->GetStatus(input_type
, caret_rect
);
1444 FARPROC WINAPI
WebPluginDelegateImpl::GetProcAddressPatch(HMODULE module
,
1446 FARPROC imm_function
= WebPluginIMEWin::GetProcAddress(name
);
1448 return imm_function
;
1449 return ::GetProcAddress(module
, name
);
1452 HWND WINAPI
WebPluginDelegateImpl::WindowFromPointPatch(POINT point
) {
1453 HWND window
= WindowFromPoint(point
);
1454 if (::ScreenToClient(window
, &point
)) {
1455 HWND child
= ChildWindowFromPoint(window
, point
);
1456 if (::IsWindow(child
) &&
1457 ::GetProp(child
, content::kPluginDummyParentProperty
))
1463 void WebPluginDelegateImpl::HandleCaptureForMessage(HWND window
,
1465 if (gfx::GetClassName(window
) != base::string16(kNativeWindowClassName
))
1469 case WM_LBUTTONDOWN
:
1470 case WM_MBUTTONDOWN
:
1471 case WM_RBUTTONDOWN
:
1472 ::SetCapture(window
);
1473 // As per documentation the WM_PARENTNOTIFY message is sent to the parent
1474 // window chain if mouse input is received by the child window. However
1475 // the parent receives the WM_PARENTNOTIFY message only if we doubleclick
1476 // on the window. We send the WM_PARENTNOTIFY message for mouse input
1477 // messages to the parent to indicate that user action is expected.
1478 ::SendMessage(::GetParent(window
), WM_PARENTNOTIFY
, message
, 0);
1492 } // namespace content