mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 545149 - Fix various issues with winless plugin context menus. r=bent.
This commit is contained in:
parent
5de9282f0d
commit
a149cb14ad
@ -58,6 +58,19 @@ using namespace mozilla::plugins;
|
||||
#include <QX11Info>
|
||||
#elif defined(OS_WIN)
|
||||
|
||||
#include "nsWindowsDllInterceptor.h"
|
||||
|
||||
typedef BOOL (WINAPI *User32TrackPopupMenu)(HMENU hMenu,
|
||||
UINT uFlags,
|
||||
int x,
|
||||
int y,
|
||||
int nReserved,
|
||||
HWND hWnd,
|
||||
CONST RECT *prcRect);
|
||||
static WindowsDllInterceptor sUser32Intercept;
|
||||
static HWND sWinlessPopupSurrogateHWND = NULL;
|
||||
static User32TrackPopupMenu sUser32TrackPopupMenuStub = NULL;
|
||||
|
||||
using mozilla::gfx::SharedDIB;
|
||||
|
||||
#include <windows.h>
|
||||
@ -65,13 +78,6 @@ using mozilla::gfx::SharedDIB;
|
||||
|
||||
#define NS_OOPP_DOUBLEPASS_MSGID TEXT("MozDoublePassMsg")
|
||||
|
||||
// During nested ui loops, parent is processing windows events via spin loop,
|
||||
// which results in rpc in-calls to child. If child falls behind in processing
|
||||
// these, an ugly stall condition occurs. To ensure child stays in sync, we use
|
||||
// a timer callback to schedule work on in-calls.
|
||||
#define CHILD_MODALPUMPTIMEOUT 50
|
||||
#define CHILD_MODALLOOPTIMER 654321
|
||||
|
||||
#endif // defined(OS_WIN)
|
||||
|
||||
PluginInstanceChild::PluginInstanceChild(const NPPluginFuncs* aPluginIface,
|
||||
@ -85,11 +91,10 @@ PluginInstanceChild::PluginInstanceChild(const NPPluginFuncs* aPluginIface,
|
||||
, mPluginWndProc(0)
|
||||
, mPluginParentHWND(0)
|
||||
, mNestedEventHook(0)
|
||||
, mNestedPumpHook(0)
|
||||
, mNestedEventLevelDepth(0)
|
||||
, mNestedEventState(false)
|
||||
, mCachedWinlessPluginHWND(0)
|
||||
, mEventPumpTimer(0)
|
||||
, mWinlessPopupSurrogateHWND(0)
|
||||
#endif // OS_WIN
|
||||
{
|
||||
memset(&mWindow, 0, sizeof(mWindow));
|
||||
@ -108,6 +113,9 @@ PluginInstanceChild::PluginInstanceChild(const NPPluginFuncs* aPluginIface,
|
||||
mAlphaExtract.doublePassEvent = ::RegisterWindowMessage(NS_OOPP_DOUBLEPASS_MSGID);
|
||||
#endif // OS_WIN
|
||||
InitQuirksModes(aMimeType);
|
||||
#if defined(OS_WIN)
|
||||
InitPopupMenuHook();
|
||||
#endif // OS_WIN
|
||||
}
|
||||
|
||||
PluginInstanceChild::~PluginInstanceChild()
|
||||
@ -124,8 +132,14 @@ PluginInstanceChild::InitQuirksModes(const nsCString& aMimeType)
|
||||
// application/x-silverlight
|
||||
// application/x-silverlight-2
|
||||
NS_NAMED_LITERAL_CSTRING(silverlight, "application/x-silverlight");
|
||||
// application/x-shockwave-flash
|
||||
NS_NAMED_LITERAL_CSTRING(flash, "application/x-shockwave-flash");
|
||||
if (FindInReadable(silverlight, aMimeType)) {
|
||||
mQuirks |= QUIRK_SILVERLIGHT_WINLESS_INPUT_TRANSLATION;
|
||||
mQuirks |= QUIRK_WINLESS_TRACKPOPUP_HOOK;
|
||||
}
|
||||
else if (FindInReadable(flash, aMimeType)) {
|
||||
mQuirks |= QUIRK_WINLESS_TRACKPOPUP_HOOK;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@ -616,6 +630,8 @@ PluginInstanceChild::AnswerNPP_SetWindow(const NPRemoteWindow& aWindow)
|
||||
break;
|
||||
|
||||
case NPWindowTypeDrawable:
|
||||
if (mQuirks & QUIRK_WINLESS_TRACKPOPUP_HOOK)
|
||||
CreateWinlessPopupSurrogate();
|
||||
return SharedSurfaceSetWindow(aWindow);
|
||||
break;
|
||||
|
||||
@ -836,26 +852,6 @@ PluginInstanceChild::PluginWindowProc(HWND hWnd,
|
||||
|
||||
/* winless modal ui loop logic */
|
||||
|
||||
VOID CALLBACK
|
||||
PluginInstanceChild::PumpTimerProc(HWND hwnd,
|
||||
UINT uMsg,
|
||||
UINT_PTR idEvent,
|
||||
DWORD dwTime)
|
||||
{
|
||||
MessageLoop::current()->ScheduleWork();
|
||||
}
|
||||
|
||||
LRESULT CALLBACK
|
||||
PluginInstanceChild::NestedInputPumpHook(int nCode,
|
||||
WPARAM wParam,
|
||||
LPARAM lParam)
|
||||
{
|
||||
if (nCode >= 0) {
|
||||
MessageLoop::current()->ScheduleWork();
|
||||
}
|
||||
return CallNextHookEx(NULL, nCode, wParam, lParam);
|
||||
}
|
||||
|
||||
// gTempChildPointer is only in use from the time we enter handle event, to the
|
||||
// point where ui might be created by that call. If ui isn't created, there's
|
||||
// no issue. If ui is created, the parent can't start processing messages in
|
||||
@ -875,7 +871,6 @@ PluginInstanceChild::NestedInputEventHook(int nCode,
|
||||
if (nCode >= 0) {
|
||||
NS_ASSERTION(gTempChildPointer, "Never should be null here!");
|
||||
gTempChildPointer->ResetNestedEventHook();
|
||||
gTempChildPointer->SetNestedInputPumpHook();
|
||||
gTempChildPointer->InternalCallSetNestedEventState(true);
|
||||
|
||||
gTempChildPointer = NULL;
|
||||
@ -883,35 +878,6 @@ PluginInstanceChild::NestedInputEventHook(int nCode,
|
||||
return CallNextHookEx(NULL, nCode, wParam, lParam);
|
||||
}
|
||||
|
||||
void
|
||||
PluginInstanceChild::SetNestedInputPumpHook()
|
||||
{
|
||||
NS_ASSERTION(!mNestedPumpHook,
|
||||
"mNestedPumpHook already setup in call to SetNestedInputPumpHook?");
|
||||
|
||||
PLUGIN_LOG_DEBUG(("%s", FULLFUNCTION));
|
||||
|
||||
mNestedPumpHook = SetWindowsHookEx(WH_CALLWNDPROC,
|
||||
NestedInputPumpHook,
|
||||
NULL,
|
||||
GetCurrentThreadId());
|
||||
mEventPumpTimer =
|
||||
SetTimer(NULL,
|
||||
CHILD_MODALLOOPTIMER,
|
||||
CHILD_MODALPUMPTIMEOUT,
|
||||
PumpTimerProc);
|
||||
}
|
||||
|
||||
void
|
||||
PluginInstanceChild::ResetPumpHooks()
|
||||
{
|
||||
if (mNestedPumpHook)
|
||||
UnhookWindowsHookEx(mNestedPumpHook);
|
||||
mNestedPumpHook = NULL;
|
||||
if (mEventPumpTimer)
|
||||
KillTimer(NULL, mEventPumpTimer);
|
||||
}
|
||||
|
||||
void
|
||||
PluginInstanceChild::SetNestedInputEventHook()
|
||||
{
|
||||
@ -951,6 +917,117 @@ PluginInstanceChild::InternalCallSetNestedEventState(bool aState)
|
||||
}
|
||||
}
|
||||
|
||||
/* windowless track popup menu helpers */
|
||||
|
||||
BOOL
|
||||
WINAPI
|
||||
PluginInstanceChild::TrackPopupHookProc(HMENU hMenu,
|
||||
UINT uFlags,
|
||||
int x,
|
||||
int y,
|
||||
int nReserved,
|
||||
HWND hWnd,
|
||||
CONST RECT *prcRect)
|
||||
{
|
||||
if (!sUser32TrackPopupMenuStub) {
|
||||
NS_ERROR("TrackPopupMenu stub isn't set! Badness!");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Only change the parent when we know this is a context on the plugin
|
||||
// surface within the browser. Prevents resetting the parent on child ui
|
||||
// displayed by plugins that have working parent-child relationships.
|
||||
PRUnichar szClass[21];
|
||||
bool haveClass = GetClassNameW(hWnd, szClass, NS_ARRAY_LENGTH(szClass));
|
||||
if (!haveClass ||
|
||||
(wcscmp(szClass, L"MozillaWindowClass") &&
|
||||
wcscmp(szClass, L"SWFlash_Placeholder"))) {
|
||||
// Unrecognized parent
|
||||
return sUser32TrackPopupMenuStub(hMenu, uFlags, x, y, nReserved,
|
||||
hWnd, prcRect);
|
||||
}
|
||||
|
||||
// Called on an unexpected event, warn.
|
||||
if (!sWinlessPopupSurrogateHWND) {
|
||||
NS_WARNING(
|
||||
"Untraced TrackPopupHookProc call! Menu might not work right!");
|
||||
return sUser32TrackPopupMenuStub(hMenu, uFlags, x, y, nReserved,
|
||||
hWnd, prcRect);
|
||||
}
|
||||
|
||||
HWND surrogateHwnd = sWinlessPopupSurrogateHWND;
|
||||
sWinlessPopupSurrogateHWND = NULL;
|
||||
|
||||
// Popups that don't use TPM_RETURNCMD expect a final command message
|
||||
// when an item is selected and the context closes. Since we replace
|
||||
// the parent, we need to forward this back to the real parent so it
|
||||
// can act on the menu item selected.
|
||||
bool isRetCmdCall = (uFlags & TPM_RETURNCMD);
|
||||
|
||||
// A little trick scrounged from chromium's code - set the focus
|
||||
// to our surrogate parent so keyboard nav events go to the menu.
|
||||
HWND focusHwnd = SetFocus(surrogateHwnd);
|
||||
DWORD res = sUser32TrackPopupMenuStub(hMenu, uFlags|TPM_RETURNCMD, x, y,
|
||||
nReserved, surrogateHwnd, prcRect);
|
||||
if (IsWindow(focusHwnd)) {
|
||||
SetFocus(focusHwnd);
|
||||
}
|
||||
|
||||
if (!isRetCmdCall && res) {
|
||||
SendMessage(hWnd, WM_COMMAND, MAKEWPARAM(res, 0), 0);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void
|
||||
PluginInstanceChild::InitPopupMenuHook()
|
||||
{
|
||||
if (!(mQuirks & QUIRK_WINLESS_TRACKPOPUP_HOOK) ||
|
||||
sUser32TrackPopupMenuStub)
|
||||
return;
|
||||
|
||||
// Note, once WindowsDllInterceptor is initialized for a module,
|
||||
// it remains initialized for that particular module for it's
|
||||
// lifetime. Additional instances are needed if other modules need
|
||||
// to be hooked.
|
||||
sUser32Intercept.Init("user32.dll");
|
||||
sUser32Intercept.AddHook("TrackPopupMenu", TrackPopupHookProc,
|
||||
(void**) &sUser32TrackPopupMenuStub);
|
||||
}
|
||||
|
||||
void
|
||||
PluginInstanceChild::CreateWinlessPopupSurrogate()
|
||||
{
|
||||
// already initialized
|
||||
if (mWinlessPopupSurrogateHWND)
|
||||
return;
|
||||
|
||||
HWND hwnd = NULL;
|
||||
NPError result;
|
||||
if (!CallNPN_GetValue_NPNVnetscapeWindow(&hwnd, &result)) {
|
||||
NS_ERROR("CallNPN_GetValue_NPNVnetscapeWindow failed.");
|
||||
return;
|
||||
}
|
||||
|
||||
mWinlessPopupSurrogateHWND =
|
||||
CreateWindowEx(WS_EX_NOPARENTNOTIFY, L"Static", NULL, WS_CHILD, 0, 0,
|
||||
0, 0, hwnd, 0, GetModuleHandle(NULL), 0);
|
||||
if (!mWinlessPopupSurrogateHWND) {
|
||||
NS_ERROR("CreateWindowEx failed for winless placeholder!");
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void
|
||||
PluginInstanceChild::DestroyWinlessPopupSurrogate()
|
||||
{
|
||||
if (mWinlessPopupSurrogateHWND)
|
||||
DestroyWindow(mWinlessPopupSurrogateHWND);
|
||||
mWinlessPopupSurrogateHWND = NULL;
|
||||
}
|
||||
|
||||
/* windowless handle event helpers */
|
||||
|
||||
static bool
|
||||
@ -1032,12 +1109,22 @@ PluginInstanceChild::WinlessHandleEvent(NPEvent& event)
|
||||
SetNestedInputEventHook();
|
||||
}
|
||||
|
||||
// TrackPopupMenu will fail if the parent window is not associated with
|
||||
// our ui thread. So we hook TrackPopupMenu so we can hand in a surrogate
|
||||
// parent created in the child process.
|
||||
if ((mQuirks & QUIRK_WINLESS_TRACKPOPUP_HOOK) && // XXX turn on by default?
|
||||
(event.event == WM_RBUTTONDOWN || // flash
|
||||
event.event == WM_RBUTTONUP)) { // silverlight
|
||||
sWinlessPopupSurrogateHWND = mWinlessPopupSurrogateHWND;
|
||||
}
|
||||
|
||||
bool old_state = MessageLoop::current()->NestableTasksAllowed();
|
||||
MessageLoop::current()->SetNestableTasksAllowed(true);
|
||||
handled = mPluginIface->event(&mData, reinterpret_cast<void*>(&event));
|
||||
MessageLoop::current()->SetNestableTasksAllowed(old_state);
|
||||
|
||||
gTempChildPointer = NULL;
|
||||
sWinlessPopupSurrogateHWND = NULL;
|
||||
|
||||
mNestedEventLevelDepth--;
|
||||
PLUGIN_LOG_DEBUG(("WinlessHandleEvent end depth: %i", mNestedEventLevelDepth));
|
||||
@ -1045,7 +1132,6 @@ PluginInstanceChild::WinlessHandleEvent(NPEvent& event)
|
||||
NS_ASSERTION(!(mNestedEventLevelDepth < 0), "mNestedEventLevelDepth < 0?");
|
||||
if (mNestedEventLevelDepth <= 0) {
|
||||
ResetNestedEventHook();
|
||||
ResetPumpHooks();
|
||||
InternalCallSetNestedEventState(false);
|
||||
}
|
||||
return handled;
|
||||
@ -1596,7 +1682,7 @@ PluginInstanceChild::AnswerNPP_Destroy(NPError* aResult)
|
||||
#if defined(OS_WIN)
|
||||
SharedSurfaceRelease();
|
||||
ResetNestedEventHook();
|
||||
ResetPumpHooks();
|
||||
DestroyWinlessPopupSurrogate();
|
||||
#endif
|
||||
|
||||
return true;
|
||||
|
@ -197,7 +197,14 @@ private:
|
||||
|
||||
// Quirks mode support for various plugin mime types
|
||||
enum PluginQuirks {
|
||||
QUIRK_SILVERLIGHT_WINLESS_INPUT_TRANSLATION = 1, // Win32
|
||||
// Win32: Translate mouse input based on WM_WINDOWPOSCHANGED
|
||||
// windowing events due to winless shared dib rendering. See
|
||||
// WinlessHandleEvent for details.
|
||||
QUIRK_SILVERLIGHT_WINLESS_INPUT_TRANSLATION = 1,
|
||||
// Win32: Hook TrackPopupMenu api so that we can swap out parent
|
||||
// hwnds. The api will fail with parents not associated with our
|
||||
// child ui thread. See WinlessHandleEvent for details.
|
||||
QUIRK_WINLESS_TRACKPOPUP_HOOK = 2,
|
||||
};
|
||||
|
||||
void InitQuirksModes(const nsCString& aMimeType);
|
||||
@ -217,6 +224,9 @@ private:
|
||||
void ResetNestedEventHook();
|
||||
void SetNestedInputPumpHook();
|
||||
void ResetPumpHooks();
|
||||
void CreateWinlessPopupSurrogate();
|
||||
void DestroyWinlessPopupSurrogate();
|
||||
void InitPopupMenuHook();
|
||||
void InternalCallSetNestedEventState(bool aState);
|
||||
static LRESULT CALLBACK DummyWindowProc(HWND hWnd,
|
||||
UINT message,
|
||||
@ -236,6 +246,13 @@ private:
|
||||
static LRESULT CALLBACK NestedInputPumpHook(int code,
|
||||
WPARAM wParam,
|
||||
LPARAM lParam);
|
||||
static BOOL WINAPI TrackPopupHookProc(HMENU hMenu,
|
||||
UINT uFlags,
|
||||
int x,
|
||||
int y,
|
||||
int nReserved,
|
||||
HWND hWnd,
|
||||
CONST RECT *prcRect);
|
||||
#endif
|
||||
|
||||
const NPPluginFuncs* mPluginIface;
|
||||
@ -254,11 +271,10 @@ private:
|
||||
WNDPROC mPluginWndProc;
|
||||
HWND mPluginParentHWND;
|
||||
HHOOK mNestedEventHook;
|
||||
HHOOK mNestedPumpHook;
|
||||
int mNestedEventLevelDepth;
|
||||
bool mNestedEventState;
|
||||
HWND mCachedWinlessPluginHWND;
|
||||
UINT_PTR mEventPumpTimer;
|
||||
HWND mWinlessPopupSurrogateHWND;
|
||||
nsIntPoint mPluginSize;
|
||||
nsIntPoint mPluginOffset;
|
||||
#endif
|
||||
|
@ -92,6 +92,7 @@ endif
|
||||
ifeq ($(MOZ_WIDGET_TOOLKIT),windows)
|
||||
CPPSRCS += nsNativeAppSupportWin.cpp
|
||||
DEFINES += -DWIN32_LEAN_AND_MEAN -DUNICODE -D_UNICODE
|
||||
EXPORTS = nsWindowsDllInterceptor.h
|
||||
else
|
||||
ifeq ($(MOZ_WIDGET_TOOLKIT),cocoa)
|
||||
CMMSRCS = nsNativeAppSupportCocoa.mm
|
||||
|
@ -76,7 +76,7 @@ public:
|
||||
if (mModule)
|
||||
return;
|
||||
|
||||
mModule = LoadLibraryEx(modulename, NULL, 0);
|
||||
mModule = LoadLibraryExA(modulename, NULL, 0);
|
||||
if (!mModule) {
|
||||
//printf("LoadLibraryEx for '%s' failed\n", modulename);
|
||||
return;
|
||||
|
@ -3733,6 +3733,7 @@ nsWindow::IsAsyncResponseEvent(UINT aMsg, LRESULT& aResult)
|
||||
case WM_SHOWWINDOW:
|
||||
case WM_CANCELMODE:
|
||||
case WM_MOUSEACTIVATE:
|
||||
case WM_CONTEXTMENU:
|
||||
aResult = 0;
|
||||
return true;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user