Bug 558629 - Meter WM_USER events for Flash in PluginInstanceChild to improve responsiveness and lower CPU utilization. r=bsmedberg, a=developers.

This commit is contained in:
Jim Mathies 2010-04-26 11:29:11 -05:00
Родитель 7366c8ae2e
Коммит 374d8cbc09
4 изменённых файлов: 228 добавлений и 6 удалений

Просмотреть файл

@ -61,12 +61,19 @@ ChildAsyncCall::Cancel()
}
void
ChildAsyncCall::Run()
ChildAsyncCall::RemoveFromAsyncList()
{
if (mFunc) {
if (mInstance) {
MutexAutoLock lock(mInstance->mAsyncCallMutex);
mInstance->mPendingAsyncCalls.RemoveElement(this);
}
}
void
ChildAsyncCall::Run()
{
RemoveFromAsyncList();
if (mFunc)
mFunc(mData);
}

Просмотреть файл

@ -59,11 +59,13 @@ public:
NS_OVERRIDE void Run();
NS_OVERRIDE void Cancel();
private:
protected:
PluginInstanceChild* mInstance;
PluginThreadCallback mFunc;
void* mData;
void RemoveFromAsyncList();
};
} // namespace plugins

Просмотреть файл

@ -77,6 +77,11 @@ using mozilla::gfx::SharedDIB;
#include <windows.h>
#include <windowsx.h>
// Flash WM_USER message delay time for PostDelayedTask. Borrowed
// from Chromium's web plugin delegate src. See 'flash msg throttling
// helpers' section for details.
const int kFlashWMUSERMessageThrottleDelayMs = 5;
#define NS_OOPP_DOUBLEPASS_MSGID TEXT("MozDoublePassMsg")
#ifndef WM_MOUSEHWHEEL
@ -101,6 +106,7 @@ PluginInstanceChild::PluginInstanceChild(const NPPluginFuncs* aPluginIface,
, mNestedEventState(false)
, mCachedWinlessPluginHWND(0)
, mWinlessPopupSurrogateHWND(0)
, mWinlessThrottleOldWndProc(0)
#endif // OS_WIN
, mAsyncCallMutex("PluginInstanceChild::mAsyncCallMutex")
#if defined(OS_MACOSX)
@ -161,6 +167,7 @@ PluginInstanceChild::InitQuirksModes(const nsCString& aMimeType)
}
else if (FindInReadable(flash, aMimeType)) {
mQuirks |= QUIRK_WINLESS_TRACKPOPUP_HOOK;
mQuirks |= QUIRK_FLASH_THROTTLE_WMUSER_EVENTS;
}
#endif
}
@ -840,8 +847,11 @@ PluginInstanceChild::AnswerNPP_SetWindow(const NPRemoteWindow& aWindow)
break;
case NPWindowTypeDrawable:
mWindow.type = aWindow.type;
if (mQuirks & QUIRK_WINLESS_TRACKPOPUP_HOOK)
CreateWinlessPopupSurrogate();
if (mQuirks & QUIRK_FLASH_THROTTLE_WMUSER_EVENTS)
SetupFlashMsgThrottle();
return SharedSurfaceSetWindow(aWindow);
break;
@ -1065,6 +1075,12 @@ PluginInstanceChild::PluginWindowProc(HWND hWnd,
}
}
if (message == WM_USER+1 &&
(self->mQuirks & PluginInstanceChild::QUIRK_FLASH_THROTTLE_WMUSER_EVENTS)) {
self->FlashThrottleMessage(hWnd, message, wParam, lParam, true);
return 0;
}
LRESULT res = CallWindowProc(self->mPluginWndProc, hWnd, message, wParam,
lParam);
@ -1543,6 +1559,157 @@ PluginInstanceChild::SharedSurfacePaint(NPEvent& evcopy)
return false;
}
/* flash msg throttling helpers */
// Flash has the unfortunate habit of flooding dispatch loops with custom
// windowing events they use for timing. We throttle these by dropping the
// delivery priority below any other event, including pending ipc io
// notifications. We do this for both windowed and windowless controls.
// static
LRESULT CALLBACK
PluginInstanceChild::WinlessHiddenFlashWndProc(HWND hWnd,
UINT message,
WPARAM wParam,
LPARAM lParam)
{
PluginInstanceChild* self = reinterpret_cast<PluginInstanceChild*>(
GetProp(hWnd, kPluginInstanceChildProperty));
if (!self) {
NS_NOTREACHED("Badness!");
return 0;
}
NS_ASSERTION(self->mWinlessThrottleOldWndProc,
"Missing subclass procedure!!");
// Throttle
if (message == WM_USER+1) {
self->FlashThrottleMessage(hWnd, message, wParam, lParam, false);
return 0;
}
// Unhook
if (message == WM_NCDESTROY) {
WNDPROC tmpProc = self->mWinlessThrottleOldWndProc;
self->mWinlessThrottleOldWndProc = nsnull;
SetWindowLongPtr(hWnd, GWLP_WNDPROC,
reinterpret_cast<LONG>(tmpProc));
LRESULT res = CallWindowProc(tmpProc, hWnd, message, wParam, lParam);
RemoveProp(hWnd, kPluginInstanceChildProperty);
return res;
}
return CallWindowProc(self->mWinlessThrottleOldWndProc,
hWnd, message, wParam, lParam);
}
// Enumerate all thread windows looking for flash's hidden message window.
// Once we find it, sub class it so we can throttle user msgs.
// static
BOOL CALLBACK
PluginInstanceChild::EnumThreadWindowsCallback(HWND hWnd,
LPARAM aParam)
{
PluginInstanceChild* self = reinterpret_cast<PluginInstanceChild*>(aParam);
if (!self) {
NS_NOTREACHED("Enum befuddled!");
return FALSE;
}
PRUnichar className[64];
if (!GetClassNameW(hWnd, className, sizeof(className)/sizeof(PRUnichar)))
return TRUE;
if (!wcscmp(className, L"SWFlash_PlaceholderX")) {
WNDPROC oldWndProc =
reinterpret_cast<WNDPROC>(GetWindowLongPtr(hWnd, GWLP_WNDPROC));
// Only set this if we haven't already.
if (oldWndProc != WinlessHiddenFlashWndProc) {
if (self->mWinlessThrottleOldWndProc) {
NS_WARNING("mWinlessThrottleWndProc already set???");
return FALSE;
}
// Subsclass and store self as a property
self->mWinlessThrottleOldWndProc =
reinterpret_cast<WNDPROC>(SetWindowLongPtr(hWnd, GWLP_WNDPROC,
reinterpret_cast<LONG>(WinlessHiddenFlashWndProc)));
SetProp(hWnd, kPluginInstanceChildProperty, self);
NS_ASSERTION(self->mWinlessThrottleOldWndProc,
"SetWindowLongPtr failed?!");
}
// Return no matter what once we find the right window.
return FALSE;
}
return TRUE;
}
void
PluginInstanceChild::SetupFlashMsgThrottle()
{
if (mWindow.type == NPWindowTypeDrawable) {
// Search for the flash hidden message window and subclass it. Only
// search for flash windows belonging to our ui thread!
if (mWinlessThrottleOldWndProc)
return;
EnumThreadWindows(GetCurrentThreadId(), EnumThreadWindowsCallback,
reinterpret_cast<LPARAM>(this));
}
else {
// Already setup through quirks and the subclass.
return;
}
}
WNDPROC
PluginInstanceChild::FlashThrottleAsyncMsg::GetProc()
{
if (mInstance) {
return mWindowed ? mInstance->mPluginWndProc :
mInstance->mWinlessThrottleOldWndProc;
}
return nsnull;
}
void
PluginInstanceChild::FlashThrottleAsyncMsg::Run()
{
RemoveFromAsyncList();
// GetProc() checks mInstance, and pulls the procedure from
// PluginInstanceChild. We don't transport sub-class procedure
// ptrs around in FlashThrottleAsyncMsg msgs.
if (!GetProc())
return;
// deliver the event to flash
CallWindowProc(GetProc(), GetWnd(), GetMsg(), GetWParam(), GetLParam());
}
void
PluginInstanceChild::FlashThrottleMessage(HWND aWnd,
UINT aMsg,
WPARAM aWParam,
LPARAM aLParam,
bool isWindowed)
{
// We reuse ChildAsyncCall so we get the cancelation work
// that's done in Destroy.
FlashThrottleAsyncMsg* task = new FlashThrottleAsyncMsg(this,
aWnd, aMsg, aWParam, aLParam, isWindowed);
if (!task)
return;
{
MutexAutoLock lock(mAsyncCallMutex);
mPendingAsyncCalls.AppendElement(task);
}
MessageLoop::current()->PostDelayedTask(FROM_HERE,
task, kFlashWMUSERMessageThrottleDelayMs);
}
#endif // OS_WIN
bool

Просмотреть файл

@ -209,11 +209,15 @@ private:
// 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,
QUIRK_SILVERLIGHT_WINLESS_INPUT_TRANSLATION = 1 << 0,
// 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,
QUIRK_WINLESS_TRACKPOPUP_HOOK = 1 << 1,
// Win32: Throttle flash WM_USER+1 heart beat messages to prevent
// flooding chromium's dispatch loop, which can cause ipc traffic
// processing lag.
QUIRK_FLASH_THROTTLE_WMUSER_EVENTS = 1 << 2,
};
void InitQuirksModes(const nsCString& aMimeType);
@ -237,6 +241,8 @@ private:
void DestroyWinlessPopupSurrogate();
void InitPopupMenuHook();
void InternalCallSetNestedEventState(bool aState);
void SetupFlashMsgThrottle();
void FlashThrottleMessage(HWND, UINT, WPARAM, LPARAM, bool);
static LRESULT CALLBACK DummyWindowProc(HWND hWnd,
UINT message,
WPARAM wParam,
@ -262,6 +268,45 @@ private:
int nReserved,
HWND hWnd,
CONST RECT *prcRect);
static BOOL CALLBACK EnumThreadWindowsCallback(HWND hWnd,
LPARAM aParam);
static LRESULT CALLBACK WinlessHiddenFlashWndProc(HWND hWnd,
UINT message,
WPARAM wParam,
LPARAM lParam);
class FlashThrottleAsyncMsg : public ChildAsyncCall
{
public:
FlashThrottleAsyncMsg();
FlashThrottleAsyncMsg(PluginInstanceChild* aInst,
HWND aWnd, UINT aMsg,
WPARAM aWParam, LPARAM aLParam,
bool isWindowed)
: ChildAsyncCall(aInst, nsnull, nsnull),
mWnd(aWnd),
mMsg(aMsg),
mWParam(aWParam),
mLParam(aLParam),
mWindowed(isWindowed)
{}
NS_OVERRIDE void Run();
WNDPROC GetProc();
HWND GetWnd() { return mWnd; }
UINT GetMsg() { return mMsg; }
WPARAM GetWParam() { return mWParam; }
LPARAM GetLParam() { return mLParam; }
private:
HWND mWnd;
UINT mMsg;
WPARAM mWParam;
LPARAM mLParam;
bool mWindowed;
};
#endif
const NPPluginFuncs* mPluginIface;
@ -286,6 +331,7 @@ private:
HWND mWinlessPopupSurrogateHWND;
nsIntPoint mPluginSize;
nsIntPoint mPluginOffset;
WNDPROC mWinlessThrottleOldWndProc;
#endif
friend class ChildAsyncCall;