| OLD | NEW |
| 1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "webkit/glue/plugins/webplugin_delegate_impl.h" | 5 #include "webkit/glue/plugins/webplugin_delegate_impl.h" |
| 6 | 6 |
| 7 #include <string> | 7 #include <string> |
| 8 #include <vector> | 8 #include <vector> |
| 9 | 9 |
| 10 #include "base/file_util.h" | 10 #include "base/file_util.h" |
| 11 #include "base/iat_patch.h" | 11 #include "base/iat_patch.h" |
| 12 #include "base/lazy_instance.h" | 12 #include "base/lazy_instance.h" |
| 13 #include "base/message_loop.h" | 13 #include "base/message_loop.h" |
| 14 #include "base/registry.h" |
| 15 #include "base/scoped_ptr.h" |
| 14 #include "base/stats_counters.h" | 16 #include "base/stats_counters.h" |
| 15 #include "base/string_util.h" | 17 #include "base/string_util.h" |
| 18 #include "base/win_util.h" |
| 16 #include "webkit/api/public/WebInputEvent.h" | 19 #include "webkit/api/public/WebInputEvent.h" |
| 17 #include "webkit/default_plugin/plugin_impl.h" | 20 #include "webkit/default_plugin/plugin_impl.h" |
| 18 #include "webkit/glue/glue_util.h" | 21 #include "webkit/glue/glue_util.h" |
| 19 #include "webkit/glue/plugins/plugin_constants_win.h" | 22 #include "webkit/glue/plugins/plugin_constants_win.h" |
| 20 #include "webkit/glue/plugins/plugin_instance.h" | 23 #include "webkit/glue/plugins/plugin_instance.h" |
| 21 #include "webkit/glue/plugins/plugin_lib.h" | 24 #include "webkit/glue/plugins/plugin_lib.h" |
| 22 #include "webkit/glue/plugins/plugin_list.h" | 25 #include "webkit/glue/plugins/plugin_list.h" |
| 23 #include "webkit/glue/plugins/plugin_stream_url.h" | 26 #include "webkit/glue/plugins/plugin_stream_url.h" |
| 24 #include "webkit/glue/webkit_glue.h" | 27 #include "webkit/glue/webkit_glue.h" |
| 25 #include "webkit/glue/webplugin.h" | 28 #include "webkit/glue/webplugin.h" |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 60 base::LazyInstance<std::list<MSG> > g_throttle_queue(base::LINKER_INITIALIZED); | 63 base::LazyInstance<std::list<MSG> > g_throttle_queue(base::LINKER_INITIALIZED); |
| 61 | 64 |
| 62 // Helper object for patching the TrackPopupMenu API. | 65 // Helper object for patching the TrackPopupMenu API. |
| 63 base::LazyInstance<iat_patch::IATPatchFunction> g_iat_patch_track_popup_menu( | 66 base::LazyInstance<iat_patch::IATPatchFunction> g_iat_patch_track_popup_menu( |
| 64 base::LINKER_INITIALIZED); | 67 base::LINKER_INITIALIZED); |
| 65 | 68 |
| 66 // Helper object for patching the SetCursor API. | 69 // Helper object for patching the SetCursor API. |
| 67 base::LazyInstance<iat_patch::IATPatchFunction> g_iat_patch_set_cursor( | 70 base::LazyInstance<iat_patch::IATPatchFunction> g_iat_patch_set_cursor( |
| 68 base::LINKER_INITIALIZED); | 71 base::LINKER_INITIALIZED); |
| 69 | 72 |
| 73 // Helper object for patching the RegEnumKeyExW API. |
| 74 base::LazyInstance<iat_patch::IATPatchFunction> g_iat_patch_reg_enum_key_ex_w( |
| 75 base::LINKER_INITIALIZED); |
| 76 |
| 70 // http://crbug.com/16114 | 77 // http://crbug.com/16114 |
| 71 // Enforces providing a valid device context in NPWindow, so that NPP_SetWindow | 78 // Enforces providing a valid device context in NPWindow, so that NPP_SetWindow |
| 72 // is never called with NPNWindoTypeDrawable and NPWindow set to NULL. | 79 // is never called with NPNWindoTypeDrawable and NPWindow set to NULL. |
| 73 // Doing so allows removing NPP_SetWindow call during painting a windowless | 80 // Doing so allows removing NPP_SetWindow call during painting a windowless |
| 74 // plugin, which otherwise could trigger layout change while painting by | 81 // plugin, which otherwise could trigger layout change while painting by |
| 75 // invoking NPN_Evaluate. Which would cause bad, bad crashes. Bad crashes. | 82 // invoking NPN_Evaluate. Which would cause bad, bad crashes. Bad crashes. |
| 76 // TODO(dglazkov): If this approach doesn't produce regressions, move class to | 83 // TODO(dglazkov): If this approach doesn't produce regressions, move class to |
| 77 // webplugin_delegate_impl.h and implement for other platforms. | 84 // webplugin_delegate_impl.h and implement for other platforms. |
| 78 class DrawableContextEnforcer { | 85 class DrawableContextEnforcer { |
| 79 public: | 86 public: |
| (...skipping 12 matching lines...) Expand all Loading... |
| 92 | 99 |
| 93 DeleteDC(static_cast<HDC>(window_->window)); | 100 DeleteDC(static_cast<HDC>(window_->window)); |
| 94 window_->window = NULL; | 101 window_->window = NULL; |
| 95 } | 102 } |
| 96 | 103 |
| 97 private: | 104 private: |
| 98 NPWindow* window_; | 105 NPWindow* window_; |
| 99 bool disposable_dc_; | 106 bool disposable_dc_; |
| 100 }; | 107 }; |
| 101 | 108 |
| 109 // These are from ntddk.h |
| 110 typedef LONG NTSTATUS; |
| 111 |
| 112 #ifndef STATUS_SUCCESS |
| 113 #define STATUS_SUCCESS ((NTSTATUS)0x00000000L) |
| 114 #endif |
| 115 |
| 116 #ifndef STATUS_BUFFER_TOO_SMALL |
| 117 #define STATUS_BUFFER_TOO_SMALL ((NTSTATUS)0xC0000023L) |
| 118 #endif |
| 119 |
| 120 typedef enum _KEY_INFORMATION_CLASS { |
| 121 KeyBasicInformation, |
| 122 KeyNodeInformation, |
| 123 KeyFullInformation, |
| 124 KeyNameInformation, |
| 125 KeyCachedInformation, |
| 126 KeyVirtualizationInformation |
| 127 } KEY_INFORMATION_CLASS; |
| 128 |
| 129 typedef struct _KEY_NAME_INFORMATION { |
| 130 ULONG NameLength; |
| 131 WCHAR Name[1]; |
| 132 } KEY_NAME_INFORMATION, *PKEY_NAME_INFORMATION; |
| 133 |
| 134 typedef DWORD (__stdcall *ZwQueryKeyType)( |
| 135 HANDLE key_handle, |
| 136 int key_information_class, |
| 137 PVOID key_information, |
| 138 ULONG length, |
| 139 PULONG result_length); |
| 140 |
| 141 // Returns a key's full path. |
| 142 std::wstring GetKeyPath(HKEY key) { |
| 143 if (key == NULL) |
| 144 return L""; |
| 145 |
| 146 HMODULE dll = GetModuleHandle(L"ntdll.dll"); |
| 147 if (dll == NULL) |
| 148 return L""; |
| 149 |
| 150 ZwQueryKeyType func = reinterpret_cast<ZwQueryKeyType>( |
| 151 ::GetProcAddress(dll, "ZwQueryKey")); |
| 152 if (func == NULL) |
| 153 return L""; |
| 154 |
| 155 DWORD size = 0; |
| 156 DWORD result = 0; |
| 157 result = func(key, KeyNameInformation, 0, 0, &size); |
| 158 if (result != STATUS_BUFFER_TOO_SMALL) |
| 159 return L""; |
| 160 |
| 161 scoped_array<char> buffer(new char[size]); |
| 162 if (buffer.get() == NULL) |
| 163 return L""; |
| 164 |
| 165 result = func(key, KeyNameInformation, buffer.get(), size, &size); |
| 166 if (result != STATUS_SUCCESS) |
| 167 return L""; |
| 168 |
| 169 KEY_NAME_INFORMATION* info = |
| 170 reinterpret_cast<KEY_NAME_INFORMATION*>(buffer.get()); |
| 171 return std::wstring(info->Name, info->NameLength / sizeof(wchar_t)); |
| 172 } |
| 102 } // namespace | 173 } // namespace |
| 103 | 174 |
| 104 bool WebPluginDelegateImpl::IsPluginDelegateWindow(HWND window) { | 175 bool WebPluginDelegateImpl::IsPluginDelegateWindow(HWND window) { |
| 105 // We use a buffer that is one char longer than we need to detect cases where | 176 // We use a buffer that is one char longer than we need to detect cases where |
| 106 // kNativeWindowClassName is a prefix of the given window's class name. It | 177 // kNativeWindowClassName is a prefix of the given window's class name. It |
| 107 // happens that GetClassNameW will just silently truncate the class name to | 178 // happens that GetClassNameW will just silently truncate the class name to |
| 108 // fit into the given buffer. | 179 // fit into the given buffer. |
| 109 wchar_t class_name[arraysize(kNativeWindowClassName) + 1]; | 180 wchar_t class_name[arraysize(kNativeWindowClassName) + 1]; |
| 110 if (!GetClassNameW(window, class_name, arraysize(class_name))) | 181 if (!GetClassNameW(window, class_name, arraysize(class_name))) |
| 111 return false; | 182 return false; |
| (...skipping 87 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 199 } | 270 } |
| 200 } | 271 } |
| 201 quirks_ |= PLUGIN_QUIRK_BLOCK_NONSTANDARD_GETURL_REQUESTS; | 272 quirks_ |= PLUGIN_QUIRK_BLOCK_NONSTANDARD_GETURL_REQUESTS; |
| 202 } else if (plugin_info.name.find(L"Windows Media Player") != | 273 } else if (plugin_info.name.find(L"Windows Media Player") != |
| 203 std::wstring::npos) { | 274 std::wstring::npos) { |
| 204 // Windows Media Player needs two NPP_SetWindow calls. | 275 // Windows Media Player needs two NPP_SetWindow calls. |
| 205 quirks_ |= PLUGIN_QUIRK_SETWINDOW_TWICE; | 276 quirks_ |= PLUGIN_QUIRK_SETWINDOW_TWICE; |
| 206 | 277 |
| 207 // Windowless mode doesn't work in the WMP NPAPI plugin. | 278 // Windowless mode doesn't work in the WMP NPAPI plugin. |
| 208 quirks_ |= PLUGIN_QUIRK_NO_WINDOWLESS; | 279 quirks_ |= PLUGIN_QUIRK_NO_WINDOWLESS; |
| 280 |
| 281 // Non-admin users on XP couldn't modify the key to force the new UI. |
| 282 quirks_ |= PLUGIN_QUIRK_PATCH_REGENUMKEYEXW; |
| 209 } else if (instance_->mime_type() == "audio/x-pn-realaudio-plugin" || | 283 } else if (instance_->mime_type() == "audio/x-pn-realaudio-plugin" || |
| 210 filename == "nppl3260.dll") { | 284 filename == "nppl3260.dll") { |
| 211 quirks_ |= PLUGIN_QUIRK_DONT_CALL_WND_PROC_RECURSIVELY; | 285 quirks_ |= PLUGIN_QUIRK_DONT_CALL_WND_PROC_RECURSIVELY; |
| 212 } else if (plugin_info.name.find(L"VLC Multimedia Plugin") != | 286 } else if (plugin_info.name.find(L"VLC Multimedia Plugin") != |
| 213 std::wstring::npos || | 287 std::wstring::npos || |
| 214 plugin_info.name.find(L"VLC Multimedia Plug-in") != | 288 plugin_info.name.find(L"VLC Multimedia Plug-in") != |
| 215 std::wstring::npos) { | 289 std::wstring::npos) { |
| 216 // VLC hangs on NPP_Destroy if we call NPP_SetWindow with a null window | 290 // VLC hangs on NPP_Destroy if we call NPP_SetWindow with a null window |
| 217 // handle | 291 // handle |
| 218 quirks_ |= PLUGIN_QUIRK_DONT_SET_NULL_WINDOW_HANDLE_ON_DESTROY; | 292 quirks_ |= PLUGIN_QUIRK_DONT_SET_NULL_WINDOW_HANDLE_ON_DESTROY; |
| 219 // VLC 0.8.6d and 0.8.6e crash if multiple instances are created. | 293 // VLC 0.8.6d and 0.8.6e crash if multiple instances are created. |
| 220 quirks_ |= PLUGIN_QUIRK_DONT_ALLOW_MULTIPLE_INSTANCES; | 294 quirks_ |= PLUGIN_QUIRK_DONT_ALLOW_MULTIPLE_INSTANCES; |
| 221 } else if (filename == "npctrl.dll") { | 295 } else if (filename == "npctrl.dll") { // Silverlight |
| 222 // Explanation for this quirk can be found in | 296 // Explanation for this quirk can be found in |
| 223 // WebPluginDelegateImpl::Initialize. | 297 // WebPluginDelegateImpl::Initialize. |
| 224 quirks_ |= PLUGIN_QUIRK_PATCH_SETCURSOR; | 298 quirks_ |= PLUGIN_QUIRK_PATCH_SETCURSOR; |
| 225 } | 299 } |
| 226 } | 300 } |
| 227 | 301 |
| 228 WebPluginDelegateImpl::~WebPluginDelegateImpl() { | 302 WebPluginDelegateImpl::~WebPluginDelegateImpl() { |
| 229 if (::IsWindow(dummy_window_for_activation_)) { | 303 if (::IsWindow(dummy_window_for_activation_)) { |
| 230 ::DestroyWindow(dummy_window_for_activation_); | 304 ::DestroyWindow(dummy_window_for_activation_); |
| 231 } | 305 } |
| (...skipping 17 matching lines...) Expand all Loading... |
| 249 } | 323 } |
| 250 | 324 |
| 251 void WebPluginDelegateImpl::PlatformInitialize() { | 325 void WebPluginDelegateImpl::PlatformInitialize() { |
| 252 plugin_->SetWindow(windowed_handle_); | 326 plugin_->SetWindow(windowed_handle_); |
| 253 if (windowless_) { | 327 if (windowless_) { |
| 254 CreateDummyWindowForActivation(); | 328 CreateDummyWindowForActivation(); |
| 255 handle_event_pump_messages_event_ = CreateEvent(NULL, TRUE, FALSE, NULL); | 329 handle_event_pump_messages_event_ = CreateEvent(NULL, TRUE, FALSE, NULL); |
| 256 plugin_->SetWindowlessPumpEvent(handle_event_pump_messages_event_); | 330 plugin_->SetWindowlessPumpEvent(handle_event_pump_messages_event_); |
| 257 } | 331 } |
| 258 | 332 |
| 259 // The windowless version of the Silverlight plugin calls the | 333 // Windowless plugins call the WindowFromPoint API and passes the result of |
| 260 // WindowFromPoint API and passes the result of that to the | 334 // that to the TrackPopupMenu API call as the owner window. This causes the |
| 261 // TrackPopupMenu API call as the owner window. This causes the API | 335 // API to fail as the API expects the window handle to live on the same thread |
| 262 // to fail as the API expects the window handle to live on the same | 336 // as the caller. It works in the other browsers as the plugin lives on the |
| 263 // thread as the caller. It works in the other browsers as the plugin | 337 // browser thread. Our workaround is to intercept the TrackPopupMenu API and |
| 264 // lives on the browser thread. Our workaround is to intercept the | 338 // replace the window handle with the dummy activation window. |
| 265 // TrackPopupMenu API for Silverlight and replace the window handle | |
| 266 // with the dummy activation window. | |
| 267 if (windowless_ && !g_iat_patch_track_popup_menu.Pointer()->is_patched()) { | 339 if (windowless_ && !g_iat_patch_track_popup_menu.Pointer()->is_patched()) { |
| 268 g_iat_patch_track_popup_menu.Pointer()->Patch( | 340 g_iat_patch_track_popup_menu.Pointer()->Patch( |
| 269 GetPluginPath().value().c_str(), "user32.dll", "TrackPopupMenu", | 341 GetPluginPath().value().c_str(), "user32.dll", "TrackPopupMenu", |
| 270 WebPluginDelegateImpl::TrackPopupMenuPatch); | 342 WebPluginDelegateImpl::TrackPopupMenuPatch); |
| 271 } | 343 } |
| 272 | 344 |
| 273 // Windowless plugins can set cursors by calling the SetCursor API. This | 345 // Windowless plugins can set cursors by calling the SetCursor API. This |
| 274 // works because the thread inputs of the browser UI thread and the plugin | 346 // works because the thread inputs of the browser UI thread and the plugin |
| 275 // thread are attached. We intercept the SetCursor API for windowless plugins | 347 // thread are attached. We intercept the SetCursor API for windowless plugins |
| 276 // and remember the cursor being set. This is shipped over to the browser | 348 // and remember the cursor being set. This is shipped over to the browser |
| 277 // in the HandleEvent call, which ensures that the cursor does not change | 349 // in the HandleEvent call, which ensures that the cursor does not change |
| 278 // when a windowless plugin instance changes the cursor in a background tab. | 350 // when a windowless plugin instance changes the cursor in a background tab. |
| 279 if (windowless_ && !g_iat_patch_set_cursor.Pointer()->is_patched() && | 351 if (windowless_ && !g_iat_patch_set_cursor.Pointer()->is_patched() && |
| 280 (quirks_ & PLUGIN_QUIRK_PATCH_SETCURSOR)) { | 352 (quirks_ & PLUGIN_QUIRK_PATCH_SETCURSOR)) { |
| 281 g_iat_patch_set_cursor.Pointer()->Patch( | 353 g_iat_patch_set_cursor.Pointer()->Patch( |
| 282 GetPluginPath().value().c_str(), "user32.dll", "SetCursor", | 354 GetPluginPath().value().c_str(), "user32.dll", "SetCursor", |
| 283 WebPluginDelegateImpl::SetCursorPatch); | 355 WebPluginDelegateImpl::SetCursorPatch); |
| 284 } | 356 } |
| 357 |
| 358 // On XP, WMP will use its old UI unless a registry key under HKLM has the |
| 359 // name of the current process. We do it in the installer for admin users, |
| 360 // for the rest patch this function. |
| 361 if ((quirks_ & PLUGIN_QUIRK_PATCH_REGENUMKEYEXW) && |
| 362 win_util::GetWinVersion() == win_util::WINVERSION_XP && |
| 363 !RegKey().Open(HKEY_LOCAL_MACHINE, |
| 364 L"SOFTWARE\\Microsoft\\MediaPlayer\\ShimInclusionList\\chrome.exe") && |
| 365 !g_iat_patch_reg_enum_key_ex_w.Pointer()->is_patched()) { |
| 366 g_iat_patch_reg_enum_key_ex_w.Pointer()->Patch( |
| 367 L"wmpdxm.dll", "advapi32.dll", "RegEnumKeyExW", |
| 368 WebPluginDelegateImpl::RegEnumKeyExWPatch); |
| 369 } |
| 285 } | 370 } |
| 286 | 371 |
| 287 void WebPluginDelegateImpl::PlatformDestroyInstance() { | 372 void WebPluginDelegateImpl::PlatformDestroyInstance() { |
| 288 if (instance_->plugin_lib()) { | 373 if (!instance_->plugin_lib()) |
| 289 // Unpatch if this is the last plugin instance. | 374 return; |
| 290 if (instance_->plugin_lib()->instance_count() == 1) { | |
| 291 if (g_iat_patch_set_cursor.Pointer()->is_patched()) { | |
| 292 g_iat_patch_set_cursor.Pointer()->Unpatch(); | |
| 293 } | |
| 294 | 375 |
| 295 if (g_iat_patch_track_popup_menu.Pointer()->is_patched()) { | 376 // Unpatch if this is the last plugin instance. |
| 296 g_iat_patch_track_popup_menu.Pointer()->Unpatch(); | 377 if (instance_->plugin_lib()->instance_count() != 1) |
| 297 } | 378 return; |
| 298 } | 379 |
| 299 } | 380 if (g_iat_patch_set_cursor.Pointer()->is_patched()) |
| 381 g_iat_patch_set_cursor.Pointer()->Unpatch(); |
| 382 |
| 383 if (g_iat_patch_track_popup_menu.Pointer()->is_patched()) |
| 384 g_iat_patch_track_popup_menu.Pointer()->Unpatch(); |
| 385 |
| 386 if (g_iat_patch_reg_enum_key_ex_w.Pointer()->is_patched()) |
| 387 g_iat_patch_reg_enum_key_ex_w.Pointer()->Unpatch(); |
| 300 } | 388 } |
| 301 | 389 |
| 302 void WebPluginDelegateImpl::Paint(HDC hdc, const gfx::Rect& rect) { | 390 void WebPluginDelegateImpl::Paint(HDC hdc, const gfx::Rect& rect) { |
| 303 if (windowless_) | 391 if (windowless_) |
| 304 WindowlessPaint(hdc, rect); | 392 WindowlessPaint(hdc, rect); |
| 305 } | 393 } |
| 306 | 394 |
| 307 void WebPluginDelegateImpl::Print(HDC hdc) { | 395 void WebPluginDelegateImpl::Print(HDC hdc) { |
| 308 // Disabling the call to NPP_Print as it causes a crash in | 396 // Disabling the call to NPP_Print as it causes a crash in |
| 309 // flash in some cases. In any case this does not work as expected | 397 // flash in some cases. In any case this does not work as expected |
| (...skipping 839 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1149 | 1237 |
| 1150 // It is ok to pass NULL here to GetCursor as we are not looking for cursor | 1238 // It is ok to pass NULL here to GetCursor as we are not looking for cursor |
| 1151 // types defined by Webkit. | 1239 // types defined by Webkit. |
| 1152 HCURSOR previous_cursor = | 1240 HCURSOR previous_cursor = |
| 1153 g_current_plugin_instance->current_windowless_cursor_.GetCursor(NULL); | 1241 g_current_plugin_instance->current_windowless_cursor_.GetCursor(NULL); |
| 1154 | 1242 |
| 1155 g_current_plugin_instance->current_windowless_cursor_.InitFromExternalCursor( | 1243 g_current_plugin_instance->current_windowless_cursor_.InitFromExternalCursor( |
| 1156 cursor); | 1244 cursor); |
| 1157 return previous_cursor; | 1245 return previous_cursor; |
| 1158 } | 1246 } |
| 1247 |
| 1248 LONG WINAPI WebPluginDelegateImpl::RegEnumKeyExWPatch( |
| 1249 HKEY key, DWORD index, LPWSTR name, LPDWORD name_size, LPDWORD reserved, |
| 1250 LPWSTR class_name, LPDWORD class_size, PFILETIME last_write_time) { |
| 1251 DWORD orig_size = *name_size; |
| 1252 LONG rv = RegEnumKeyExW(key, index, name, name_size, reserved, class_name, |
| 1253 class_size, last_write_time); |
| 1254 if (rv == ERROR_SUCCESS && |
| 1255 GetKeyPath(key).find(L"Microsoft\\MediaPlayer\\ShimInclusionList") != |
| 1256 std::wstring::npos) { |
| 1257 static const wchar_t kChromeExeName[] = L"chrome.exe"; |
| 1258 wcsncpy_s(name, orig_size, kChromeExeName, arraysize(kChromeExeName)); |
| 1259 *name_size = |
| 1260 std::min(orig_size, static_cast<DWORD>(arraysize(kChromeExeName))); |
| 1261 } |
| 1262 |
| 1263 return rv; |
| 1264 } |
| OLD | NEW |