Changeset 84638 in webkit
- Timestamp:
- Apr 22, 2011 9:48:35 AM (13 years ago)
- Location:
- trunk/Source/WebKit2
- Files:
-
- 6 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/Source/WebKit2/ChangeLog
r84635 r84638 1 2011-04-21 Adam Roben <aroben@apple.com> 2 3 Give windowless plugins' context menus an owner window in the same thread as the plugin 4 5 In some cases, plugins pass the WKView's HWND to the ::TrackPopupMenu API. ::TrackPopupMenu 6 fails in this case because the WKView's HWND is owned by another process/thread. We work 7 around this by hooking the ::TrackPopupMenu API and providing our own window that's in the 8 same thread as the plugin instead. 9 10 I couldn't figure out how to write a test for this. :-( 11 12 Fixes <http://webkit.org/b/51063> <rdar://problem/8769281> REGRESSION (WebKit2): No context 13 menu appears when right-clicking on windowless Flash plugin 14 15 Reviewed by Brian Weinstein, and looked at suspiciously by Jeff Miller. 16 17 * Platform/Module.h: Added installIATHook on Windows. 18 19 * Platform/win/ModuleWin.cpp: 20 (WebKit::overwriteReadOnlyMemory): New helper function. Basically a wrapper around memcpy, 21 but uses ::VirtualProtect to allow writing to otherwise-read-only memory. 22 (WebKit::findFunctionPointerAddress): New helper function. The first overload iterates over 23 the imported modules and functions looking for the given one. If found, returns the address 24 of the function pointer that is used when calling that function. The second overload calls 25 the first, first passing it an enumerator for the image's imported modules, then passing it 26 an iterator for the image's delay-loaded modules. 27 (WebKit::Module::installIATHook): Added. Finds the address of the function pointer that's 28 used when code in this module calls the given function, then overwrites the function pointer 29 with the passed-in one. Future calls to the given function made by code in this module 30 should then end up calling to the passed-in function instead of the original one. 31 32 * Shared/Plugins/Netscape/NetscapePluginModule.h: 33 (WebKit::NetscapePluginModule::module): Added this simple getter. 34 35 * WebProcess/Plugins/Netscape/NetscapePlugin.h: Added some new Windows-only members. 36 37 * WebProcess/Plugins/Netscape/win/NetscapePluginWin.cpp: 38 (WebKit::CurrentPluginSetter::CurrentPluginSetter): 39 (WebKit::CurrentPluginSetter::~CurrentPluginSetter): 40 This RAII class sets/unsets the currentPlugin global. 41 42 (WebKit::registerPluginView): Changed to use instanceHandle() instead of the mysterious 43 gInstance. 44 (WebKit::NetscapePlugin::platformPostInitialize): For windowless plugins, hook the 45 ::TrackPopupMenu API and create a window to be used as the owner for context menus created 46 by this plugin. (Also changed the ::CreateWindowExW call for windowed plugins to pass an 47 instance handle so that the window will be associated with WebKit.dll instead of 48 WebKit2WebProcess.exe. This should have no visible effect, but is more correct.) 49 (WebKit::NetscapePlugin::platformDestroy): For windowless plugins, destroy the context menu 50 owner window we created above. 51 52 (WebKit::NetscapePlugin::platformPaint): 53 (WebKit::NetscapePlugin::platformHandleMouseEvent): 54 (WebKit::NetscapePlugin::platformHandleWheelEvent): 55 (WebKit::NetscapePlugin::platformSetFocus): 56 (WebKit::NetscapePlugin::platformHandleMouseEnterEvent): 57 (WebKit::NetscapePlugin::platformHandleMouseLeaveEvent): 58 (WebKit::NetscapePlugin::platformHandleKeyboardEvent): 59 Set ourselves as the current plugin. 60 61 (WebKit::NetscapePlugin::hookedTrackPopupMenu): Added. This is the function that actually 62 gets called when windowless plugins call ::TrackPopupMenu. If the passed-in owner window is 63 owned by the current thread, we have nothing to do; ::TrackPopupMenu should work just fine. 64 Otherwise, we use the current plugin's context menu owner window as the context menu's 65 owner. We also set focus to the new owner window because otherwise weird behavior results 66 (e.g., it's possible to scroll the WKView while the context menu is up). 67 1 68 2011-04-21 Adam Roben <aroben@apple.com> 2 69 -
trunk/Source/WebKit2/Platform/Module.h
r81617 r84638 61 61 #endif 62 62 63 #if PLATFORM(WIN) 64 void installIATHook(const char* importDLLName, const char* importFunctionName, const void* hookFunction); 65 #endif 66 63 67 private: 64 68 void* platformFunctionPointer(const char* functionName) const; -
trunk/Source/WebKit2/Platform/win/ModuleWin.cpp
r76916 r84638 27 27 #include "Module.h" 28 28 29 #include <WebCore/DelayLoadedModulesEnumerator.h> 30 #include <WebCore/ImportedFunctionsEnumerator.h> 31 #include <WebCore/ImportedModulesEnumerator.h> 29 32 #include <shlwapi.h> 33 34 using namespace WebCore; 30 35 31 36 namespace WebKit { … … 46 51 } 47 52 53 static void memcpyToReadOnlyMemory(void* destination, const void* source, size_t size) 54 { 55 DWORD originalProtection; 56 if (!::VirtualProtect(destination, size, PAGE_READWRITE, &originalProtection)) 57 return; 58 59 memcpy(destination, source, size); 60 61 ::VirtualProtect(destination, size, originalProtection, &originalProtection); 62 } 63 64 static const void* const* findFunctionPointerAddress(ImportedModulesEnumeratorBase& modules, const char* importDLLName, const char* importFunctionName) 65 { 66 for (; !modules.isAtEnd(); modules.next()) { 67 if (_stricmp(importDLLName, modules.currentModuleName())) 68 continue; 69 70 for (ImportedFunctionsEnumerator functions = modules.functionsEnumerator(); !functions.isAtEnd(); functions.next()) { 71 const char* currentFunctionName = functions.currentFunctionName(); 72 if (!currentFunctionName || strcmp(importFunctionName, currentFunctionName)) 73 continue; 74 75 return functions.addressOfCurrentFunctionPointer(); 76 } 77 78 break; 79 } 80 81 return 0; 82 } 83 84 static const void* const* findFunctionPointerAddress(HMODULE module, const char* importDLLName, const char* importFunctionName) 85 { 86 PEImage image(module); 87 88 ImportedModulesEnumerator importedModules(image); 89 if (const void* const* functionPointerAddress = findFunctionPointerAddress(importedModules, importDLLName, importFunctionName)) 90 return functionPointerAddress; 91 92 DelayLoadedModulesEnumerator delayLoadedModules(image); 93 return findFunctionPointerAddress(delayLoadedModules, importDLLName, importFunctionName); 94 } 95 96 void Module::installIATHook(const char* importDLLName, const char* importFunctionName, const void* hookFunction) 97 { 98 if (!m_module) 99 return; 100 101 // The Import Address Table (IAT) contains one function pointer for each function imported by 102 // this module. When code in this module calls that function, the function pointer from the IAT 103 // is used. We find that function pointer and overwrite it with hookFunction so that 104 // hookFunction will be called instead. 105 106 const void* const* functionPointerAddress = findFunctionPointerAddress(m_module, importDLLName, importFunctionName); 107 if (!functionPointerAddress || *functionPointerAddress == hookFunction) 108 return; 109 110 memcpyToReadOnlyMemory(const_cast<const void**>(functionPointerAddress), &hookFunction, sizeof(hookFunction)); 111 } 112 48 113 void* Module::platformFunctionPointer(const char* functionName) const 49 114 { -
trunk/Source/WebKit2/Shared/Plugins/Netscape/NetscapePluginModule.h
r81157 r84638 60 60 bool clearSiteData(const String& site, uint64_t flags, uint64_t maxAge); 61 61 62 Module* module() const { return m_module.get(); } 63 62 64 private: 63 65 explicit NetscapePluginModule(const String& pluginPath); -
trunk/Source/WebKit2/WebProcess/Plugins/Netscape/NetscapePlugin.h
r81705 r84638 186 186 virtual PluginController* controller(); 187 187 188 #if PLUGIN_ARCHITECTURE(WIN) 189 static BOOL WINAPI hookedTrackPopupMenu(HMENU, UINT uFlags, int x, int y, int nReserved, HWND, const RECT*); 190 #endif 191 188 192 PluginController* m_pluginController; 189 193 uint64_t m_nextRequestID; … … 235 239 #elif PLUGIN_ARCHITECTURE(WIN) 236 240 HWND m_window; 241 HWND m_contextMenuOwnerWindow; 237 242 #elif PLUGIN_ARCHITECTURE(X11) 238 243 Pixmap m_drawable; -
trunk/Source/WebKit2/WebProcess/Plugins/Netscape/win/NetscapePluginWin.cpp
r79335 r84638 29 29 #include "PluginController.h" 30 30 #include "WebEvent.h" 31 #include <WebCore/DefWndProcWindowClass.h> 31 32 #include <WebCore/GraphicsContext.h> 32 33 #include <WebCore/LocalWindowsContext.h> 33 34 #include <WebCore/NotImplemented.h> 35 #include <WebCore/WebCoreInstanceHandle.h> 34 36 35 37 using namespace WebCore; 36 38 37 extern "C" HINSTANCE gInstance;38 39 39 namespace WebKit { 40 41 static NetscapePlugin* currentPlugin; 42 43 class CurrentPluginSetter { 44 WTF_MAKE_NONCOPYABLE(CurrentPluginSetter); 45 public: 46 explicit CurrentPluginSetter(NetscapePlugin* plugin) 47 : m_plugin(plugin) 48 , m_formerCurrentPlugin(currentPlugin) 49 { 50 currentPlugin = m_plugin; 51 } 52 53 ~CurrentPluginSetter() 54 { 55 ASSERT(currentPlugin == m_plugin); 56 currentPlugin = m_formerCurrentPlugin; 57 } 58 59 private: 60 NetscapePlugin* m_plugin; 61 NetscapePlugin* m_formerCurrentPlugin; 62 }; 40 63 41 64 static LPCWSTR windowClassName = L"org.webkit.NetscapePluginWindow"; … … 51 74 windowClass.style = CS_DBLCLKS; 52 75 windowClass.lpfnWndProc = ::DefWindowProcW; 53 windowClass.hInstance = gInstance;76 windowClass.hInstance = instanceHandle(); 54 77 windowClass.hCursor = ::LoadCursorW(0, IDC_ARROW); 55 78 windowClass.hbrBackground = reinterpret_cast<HBRUSH>(COLOR_WINDOW + 1); … … 68 91 if (!m_isWindowed) { 69 92 m_window = 0; 93 94 // Windowless plugins need a little help showing context menus since our containingWindow() 95 // is in a different process. See <http://webkit.org/b/51063>. 96 m_pluginModule->module()->installIATHook("user32.dll", "TrackPopupMenu", hookedTrackPopupMenu); 97 m_contextMenuOwnerWindow = ::CreateWindowExW(0, defWndProcWindowClassName(), 0, WS_CHILD, 0, 0, 0, 0, containingWindow(), 0, instanceHandle(), 0); 98 70 99 return true; 71 100 } … … 73 102 registerPluginView(); 74 103 75 m_window = ::CreateWindowExW(0, windowClassName, 0, WS_CHILD | WS_VISIBLE, 0, 0, 0, 0, containingWindow(), 0, 0, 0);104 m_window = ::CreateWindowExW(0, windowClassName, 0, WS_CHILD | WS_VISIBLE, 0, 0, 0, 0, containingWindow(), 0, instanceHandle(), 0); 76 105 if (!m_window) 77 106 return false; … … 89 118 { 90 119 if (!m_isWindowed) { 120 ASSERT(m_contextMenuOwnerWindow); 121 ::DestroyWindow(m_contextMenuOwnerWindow); 91 122 ASSERT(!m_window); 92 123 return; … … 138 169 void NetscapePlugin::platformPaint(GraphicsContext* context, const IntRect& dirtyRect, bool) 139 170 { 171 CurrentPluginSetter setCurrentPlugin(this); 172 140 173 // FIXME: Call SetWindow here if we haven't called it yet (see r59904). 141 174 … … 247 280 bool NetscapePlugin::platformHandleMouseEvent(const WebMouseEvent& event) 248 281 { 282 CurrentPluginSetter setCurrentPlugin(this); 283 249 284 if (m_isWindowed) 250 285 return false; … … 257 292 bool NetscapePlugin::platformHandleWheelEvent(const WebWheelEvent&) 258 293 { 294 CurrentPluginSetter setCurrentPlugin(this); 295 259 296 notImplemented(); 260 297 return false; … … 263 300 void NetscapePlugin::platformSetFocus(bool) 264 301 { 302 CurrentPluginSetter setCurrentPlugin(this); 303 265 304 notImplemented(); 266 305 } … … 268 307 bool NetscapePlugin::platformHandleMouseEnterEvent(const WebMouseEvent& event) 269 308 { 309 CurrentPluginSetter setCurrentPlugin(this); 310 270 311 if (m_isWindowed) 271 312 return false; … … 278 319 bool NetscapePlugin::platformHandleMouseLeaveEvent(const WebMouseEvent& event) 279 320 { 321 CurrentPluginSetter setCurrentPlugin(this); 322 280 323 if (m_isWindowed) 281 324 return false; … … 288 331 bool NetscapePlugin::platformHandleKeyboardEvent(const WebKeyboardEvent&) 289 332 { 333 CurrentPluginSetter setCurrentPlugin(this); 334 290 335 notImplemented(); 291 336 return false; 292 337 } 293 338 339 BOOL NetscapePlugin::hookedTrackPopupMenu(HMENU hMenu, UINT uFlags, int x, int y, int nReserved, HWND hWnd, const RECT* prcRect) 340 { 341 // ::TrackPopupMenu fails when it is passed a window that is owned by another thread. If this 342 // happens, we substitute a dummy window that is owned by this thread. 343 344 if (::GetWindowThreadProcessId(hWnd, 0) == ::GetCurrentThreadId()) 345 return ::TrackPopupMenu(hMenu, uFlags, x, y, nReserved, hWnd, prcRect); 346 347 HWND originalFocusWindow = 0; 348 349 ASSERT(currentPlugin); 350 if (currentPlugin) { 351 ASSERT(!currentPlugin->m_isWindowed); 352 ASSERT(currentPlugin->m_contextMenuOwnerWindow); 353 ASSERT(::GetWindowThreadProcessId(currentPlugin->m_contextMenuOwnerWindow, 0) == ::GetCurrentThreadId()); 354 hWnd = currentPlugin->m_contextMenuOwnerWindow; 355 356 // If we don't focus the dummy window, the user will be able to scroll the page while the 357 // context menu is up, e.g. 358 originalFocusWindow = ::SetFocus(hWnd); 359 } 360 361 BOOL result = ::TrackPopupMenu(hMenu, uFlags, x, y, nReserved, hWnd, prcRect); 362 363 if (originalFocusWindow) 364 ::SetFocus(originalFocusWindow); 365 366 return result; 367 } 368 294 369 } // namespace WebKit
Note: See TracChangeset
for help on using the changeset viewer.