Bug 531860 - plugin-alpha-zindex ref test fails with oopp enabled. r=jmuizelaar.

This commit is contained in:
Jim Mathies 2009-12-14 18:27:25 -06:00
parent 09766da14c
commit 8334063adb
11 changed files with 273 additions and 86 deletions

View File

@ -51,7 +51,7 @@ struct NPRemoteEvent
{
NPEvent event;
union {
NPRect rect;
RECT rect;
WINDOWPOS windowpos;
} lParamData;
};
@ -87,9 +87,9 @@ struct ParamTraits<mozilla::plugins::NPRemoteEvent>
paramCopy.lParamData.windowpos = *(reinterpret_cast<WINDOWPOS*>(paramCopy.event.lParam));
break;
case WM_PAINT:
// The lParam paramter of WM_PAINT holds a pointer to an NPRect
// The lParam paramter of WM_PAINT holds a pointer to an RECT
// structure specifying the bounding box of the update area.
paramCopy.lParamData.rect = *(reinterpret_cast<NPRect*>(paramCopy.event.lParam));
paramCopy.lParamData.rect = *(reinterpret_cast<RECT*>(paramCopy.event.lParam));
break;
// the white list of events that we will ipc to the client
@ -127,8 +127,12 @@ struct ParamTraits<mozilla::plugins::NPRemoteEvent>
case WM_KILLFOCUS:
break;
// ignore any events we don't expect
default:
// RegisterWindowMessage events should be passed.
if (paramCopy.event.event >= 0xC000 && paramCopy.event.event <= 0xFFFF)
break;
// ignore any events we don't expect
return;
}
@ -145,7 +149,7 @@ struct ParamTraits<mozilla::plugins::NPRemoteEvent>
memcpy(aResult, bytes, sizeof(paramType));
if (aResult->event.event == WM_PAINT) {
// restore the lParam to point at the NPRect
// restore the lParam to point at the RECT
aResult->event.lParam = reinterpret_cast<LPARAM>(&aResult->lParamData.rect);
} else if (aResult->event.event == WM_WINDOWPOSCHANGED) {
// restore the lParam to point at the WINDOWPOS

View File

@ -59,6 +59,7 @@ using mozilla::gfx::SharedDIB;
#include <windows.h>
#define NS_OOPP_DOUBLEPASS_MSGID TEXT("MozDoublePassMsg")
#endif
PluginInstanceChild::PluginInstanceChild(const NPPluginFuncs* aPluginIface) :
@ -77,6 +78,10 @@ PluginInstanceChild::PluginInstanceChild(const NPPluginFuncs* aPluginIface) :
# ifdef MOZ_WIDGET_GTK2
mWsInfo.display = GDK_DISPLAY();
# endif
#endif
#if defined(OS_WIN)
memset(&mAlphaExtract, 0, sizeof(mAlphaExtract));
mAlphaExtract.doublePassEvent = ::RegisterWindowMessage(NS_OOPP_DOUBLEPASS_MSGID);
#endif
}
@ -342,9 +347,21 @@ PluginInstanceChild::AnswerNPP_HandleEvent(const NPRemoteEvent& event,
NPEvent evcopy = event.event;
#ifdef OS_WIN
// Setup the shared dib for painting and update evcopy.
if (NPWindowTypeDrawable == mWindow.type && WM_PAINT == evcopy.event)
SharedSurfaceBeforePaint(evcopy);
// Painting for win32. SharedSurfacePaint handles everything.
if (mWindow.type == NPWindowTypeDrawable) {
if (evcopy.event == WM_PAINT) {
*handled = SharedSurfacePaint(evcopy);
return true;
}
else if (evcopy.event == mAlphaExtract.doublePassEvent) {
// We'll render to mSharedSurfaceDib first, then render to a cached bitmap
// we store locally. The two passes are for alpha extraction, so the second
// pass must be to a flat white surface in order for things to work.
mAlphaExtract.doublePass = RENDER_BACK_ONE;
*handled = true;
return true;
}
}
#endif
*handled = mPluginIface->event(&mData, reinterpret_cast<void*>(&evcopy));
@ -675,6 +692,9 @@ PluginInstanceChild::SharedSurfaceSetWindow(const NPRemoteWindow& aWindow,
if (NS_FAILED(mSharedSurfaceDib.Attach((SharedDIB::Handle)aWindow.surfaceHandle,
aWindow.width, aWindow.height, 32)))
return false;
// Free any alpha extraction resources if needed. This will be reset
// the next time it's used.
AlphaExtractCacheRelease();
}
// NPRemoteWindow's origin is the origin of our shared dib.
@ -694,20 +714,137 @@ void
PluginInstanceChild::SharedSurfaceRelease()
{
mSharedSurfaceDib.Close();
AlphaExtractCacheRelease();
}
/* double pass cache buffer - (rarely) used in cases where alpha extraction
* occurs for windowless plugins. */
bool
PluginInstanceChild::AlphaExtractCacheSetup()
{
AlphaExtractCacheRelease();
mAlphaExtract.hdc = ::CreateCompatibleDC(NULL);
if (!mAlphaExtract.hdc)
return false;
BITMAPINFOHEADER bmih;
memset((void*)&bmih, 0, sizeof(BITMAPINFOHEADER));
bmih.biSize = sizeof(BITMAPINFOHEADER);
bmih.biWidth = mWindow.width;
bmih.biHeight = mWindow.height;
bmih.biPlanes = 1;
bmih.biBitCount = 32;
bmih.biCompression = BI_RGB;
void* ppvBits = nsnull;
mAlphaExtract.bmp = ::CreateDIBSection(mAlphaExtract.hdc,
(BITMAPINFO*)&bmih,
DIB_RGB_COLORS,
(void**)&ppvBits,
NULL,
(unsigned long)sizeof(BITMAPINFOHEADER));
if (!mAlphaExtract.bmp)
return false;
DeleteObject(::SelectObject(mAlphaExtract.hdc, mAlphaExtract.bmp));
return true;
}
void
PluginInstanceChild::SharedSurfaceBeforePaint(NPEvent& evcopy)
PluginInstanceChild::AlphaExtractCacheRelease()
{
// Update the clip rect on our internal hdc
RECT* pRect = reinterpret_cast<RECT*>(evcopy.lParam);
if (pRect) {
HRGN clip = ::CreateRectRgnIndirect(pRect);
::SelectClipRgn(mSharedSurfaceDib.GetHDC(), clip);
::DeleteObject(clip);
if (mAlphaExtract.bmp)
::DeleteObject(mAlphaExtract.bmp);
if (mAlphaExtract.hdc)
::DeleteObject(mAlphaExtract.hdc);
mAlphaExtract.bmp = NULL;
mAlphaExtract.hdc = NULL;
}
void
PluginInstanceChild::UpdatePaintClipRect(RECT* aRect)
{
if (aRect) {
// Update the clip rect on our internal hdc
HRGN clip = ::CreateRectRgnIndirect(aRect);
::SelectClipRgn(mSharedSurfaceDib.GetHDC(), clip);
::DeleteObject(clip);
}
// pass the internal hdc to the plugin
evcopy.wParam = WPARAM(mSharedSurfaceDib.GetHDC());
}
int16_t
PluginInstanceChild::SharedSurfacePaint(NPEvent& evcopy)
{
RECT* pRect = reinterpret_cast<RECT*>(evcopy.lParam);
switch(mAlphaExtract.doublePass) {
case RENDER_NATIVE:
// pass the internal hdc to the plugin
UpdatePaintClipRect(pRect);
evcopy.wParam = WPARAM(mSharedSurfaceDib.GetHDC());
return mPluginIface->event(&mData, reinterpret_cast<void*>(&evcopy));
break;
case RENDER_BACK_ONE:
// Handle a double pass render used in alpha extraction for transparent
// plugins. (See nsObjectFrame and gfxWindowsNativeDrawing for details.)
// We render twice, once to the shared dib, and once to a cache which
// we copy back on a second paint. These paints can't be spread across
// multiple rpc messages as delays cause animation frame changes.
if (!mAlphaExtract.bmp && !AlphaExtractCacheSetup()) {
mAlphaExtract.doublePass = RENDER_NATIVE;
return false;
}
// See gfxWindowsNativeDrawing, color order doesn't have to match.
::FillRect(mSharedSurfaceDib.GetHDC(), pRect, (HBRUSH)GetStockObject(WHITE_BRUSH));
UpdatePaintClipRect(pRect);
evcopy.wParam = WPARAM(mSharedSurfaceDib.GetHDC());
if (!mPluginIface->event(&mData, reinterpret_cast<void*>(&evcopy))) {
mAlphaExtract.doublePass = RENDER_NATIVE;
return false;
}
// Copy to cache. We render to shared dib so we don't have to call
// setwindow between calls (flash issue).
::BitBlt(mAlphaExtract.hdc,
pRect->left,
pRect->top,
pRect->right - pRect->left,
pRect->bottom - pRect->top,
mSharedSurfaceDib.GetHDC(),
pRect->left,
pRect->top,
SRCCOPY);
::FillRect(mSharedSurfaceDib.GetHDC(), pRect, (HBRUSH)GetStockObject(BLACK_BRUSH));
if (!mPluginIface->event(&mData, reinterpret_cast<void*>(&evcopy))) {
mAlphaExtract.doublePass = RENDER_NATIVE;
return false;
}
mAlphaExtract.doublePass = RENDER_BACK_TWO;
return true;
break;
case RENDER_BACK_TWO:
// copy our cached surface back
::BitBlt(mSharedSurfaceDib.GetHDC(),
pRect->left,
pRect->top,
pRect->right - pRect->left,
pRect->bottom - pRect->top,
mAlphaExtract.hdc,
pRect->left,
pRect->top,
SRCCOPY);
mAlphaExtract.doublePass = RENDER_NATIVE;
return true;
break;
}
return false;
}
#endif // OS_WIN

View File

@ -198,11 +198,25 @@ private:
private:
// Shared dib rendering management for windowless plugins.
bool SharedSurfaceSetWindow(const NPRemoteWindow& aWindow, NPError* rv);
void SharedSurfaceBeforePaint(NPEvent& evcopy);
int16_t SharedSurfacePaint(NPEvent& evcopy);
void SharedSurfaceRelease();
bool AlphaExtractCacheSetup();
void AlphaExtractCacheRelease();
void UpdatePaintClipRect(RECT* aRect);
private:
enum {
RENDER_NATIVE,
RENDER_BACK_ONE,
RENDER_BACK_TWO
};
gfx::SharedDIBWin mSharedSurfaceDib;
struct {
PRUint32 doublePassEvent;
PRUint16 doublePass;
HDC hdc;
HBITMAP bmp;
} mAlphaExtract;
#endif // defined(OS_WIN)
};

View File

@ -47,10 +47,6 @@
#include "npfunctions.h"
#include "nsAutoPtr.h"
#if defined(OS_WIN)
#define NS_OOPP_DOUBLEPASS_MSGID TEXT("MozDoublePassMsg")
#endif
using namespace mozilla::plugins;
PluginInstanceParent::PluginInstanceParent(PluginModuleParent* parent,
@ -61,14 +57,6 @@ PluginInstanceParent::PluginInstanceParent(PluginModuleParent* parent,
mNPNIface(npniface),
mWindowType(NPWindowTypeWindow)
{
#if defined(OS_WIN)
// Event sent from nsObjectFrame indicating double pass rendering for
// windowless plugins. RegisterWindowMessage makes it easy sync event
// values, and insures we never conflict with windowing events we allow
// for windowless plugins.
mDoublePassEvent = ::RegisterWindowMessage(NS_OOPP_DOUBLEPASS_MSGID);
mLocalCopyRender = false;
#endif
}
PluginInstanceParent::~PluginInstanceParent()
@ -467,16 +455,9 @@ PluginInstanceParent::NPP_HandleEvent(void* event)
#if defined(OS_WIN)
RECT rect;
if (mWindowType == NPWindowTypeDrawable) {
if (mDoublePassEvent && mDoublePassEvent == npevent->event) {
// Sent from nsObjectFrame to let us know a double pass render is in progress.
mLocalCopyRender = PR_TRUE;
return true;
} else if (WM_PAINT == npevent->event) {
// Don't forward on the second pass, otherwise, fall through.
if (!SharedSurfaceBeforePaint(rect, npremoteevent))
return true;
}
if (mWindowType == NPWindowTypeDrawable &&
npevent->event == WM_PAINT) {
SharedSurfaceBeforePaint(rect, npremoteevent);
}
#endif
@ -503,7 +484,7 @@ PluginInstanceParent::NPP_HandleEvent(void* event)
}
#if defined(OS_WIN)
if (handled && mWindowType == NPWindowTypeDrawable && WM_PAINT == npevent->event)
if (handled && mWindowType == NPWindowTypeDrawable && npevent->event == WM_PAINT)
SharedSurfaceAfterPaint(npevent);
#endif
@ -730,26 +711,13 @@ PluginInstanceParent::SharedSurfaceSetWindow(const NPWindow* aWindow,
return true;
}
bool
void
PluginInstanceParent::SharedSurfaceBeforePaint(RECT& rect,
NPRemoteEvent& npremoteevent)
{
RECT* dr = (RECT*)npremoteevent.event.lParam;
HDC parentHdc = (HDC)npremoteevent.event.wParam;
// We render twice per frame for windowless plugins that sit in transparent
// frames. (See nsObjectFrame and gfxWindowsNativeDrawing for details.) IPC
// message delays in OOP plugin painting can result in two passes yeilding
// different animation frames. The second rendering doesn't need to go over
// the wire (we already have a copy of the frame in mSharedSurfaceDib) so we
// skip off requesting the second. This also gives us a nice perf boost.
if (mLocalCopyRender) {
mLocalCopyRender = false;
// Reuse the old render.
SharedSurfaceAfterPaint(&npremoteevent.event);
return false;
}
nsIntRect dirtyRect(dr->left, dr->top, dr->right-dr->left, dr->bottom-dr->top);
dirtyRect.MoveBy(-mPluginPort.x, -mPluginPort.y); // should always be smaller than dirtyRect
@ -766,14 +734,11 @@ PluginInstanceParent::SharedSurfaceBeforePaint(RECT& rect,
// setup the translated dirty rect we'll send to the child
rect.left = dirtyRect.x;
rect.top = dirtyRect.y;
rect.right = dirtyRect.width;
rect.bottom = dirtyRect.height;
rect.right = dirtyRect.x + dirtyRect.width;
rect.bottom = dirtyRect.y + dirtyRect.height;
npremoteevent.event.wParam = WPARAM(0);
npremoteevent.event.lParam = LPARAM(&rect);
// Send the event to the plugin
return true;
}
void

View File

@ -217,7 +217,7 @@ private:
private:
// Used in rendering windowless plugins in other processes.
bool SharedSurfaceSetWindow(const NPWindow* aWindow, NPRemoteWindow& aRemoteWindow);
bool SharedSurfaceBeforePaint(RECT &rect, NPRemoteEvent& npremoteevent);
void SharedSurfaceBeforePaint(RECT &rect, NPRemoteEvent& npremoteevent);
void SharedSurfaceAfterPaint(NPEvent* npevent);
void SharedSurfaceRelease();
@ -225,8 +225,6 @@ private:
gfx::SharedDIBWin mSharedSurfaceDib;
nsIntRect mPluginPort;
nsIntRect mSharedSize;
PRUint32 mDoublePassEvent;
bool mLocalCopyRender;
#endif // defined(XP_WIN)
};

View File

@ -111,6 +111,9 @@ public:
/* Returns PR_TRUE if the native drawing should be executed again */
PRBool ShouldRenderAgain();
/* Returns PR_TRUE if double pass alpha extraction is taking place. */
PRBool IsDoublePass();
/* Places the result to the context, if necessary */
void PaintToContext();

View File

@ -205,6 +205,22 @@ gfxWindowsNativeDrawing::BeginNativeDrawing()
}
}
PRBool
gfxWindowsNativeDrawing::IsDoublePass()
{
// this is the same test we use in BeginNativeDrawing.
nsRefPtr<gfxASurface> surf = mContext->CurrentSurface(&mDeviceOffset.x, &mDeviceOffset.y);
if (!surf || surf->CairoStatus())
return false;
if ((surf->GetType() == gfxASurface::SurfaceTypeWin32 ||
surf->GetType() == gfxASurface::SurfaceTypeWin32Printing) &&
(surf->GetContentType() != gfxASurface::CONTENT_COLOR ||
(surf->GetContentType() == gfxASurface::CONTENT_COLOR_ALPHA &&
!(mNativeDrawFlags & CAN_DRAW_TO_COLOR_ALPHA))))
return PR_TRUE;
return PR_FALSE;
}
PRBool
gfxWindowsNativeDrawing::ShouldRenderAgain()
{

View File

@ -1677,7 +1677,24 @@ nsObjectFrame::PaintPlugin(nsIRenderingContext& aRenderingContext,
nsPoint origin;
gfxWindowsNativeDrawing nativeDraw(ctx, frameGfxRect);
PRBool doublePass = PR_FALSE;
#ifdef MOZ_IPC
if (nativeDraw.IsDoublePass()) {
// OOP plugin specific: let the shim know before we paint if we are doing a
// double pass render. If this plugin isn't oop, the register window message
// will be ignored.
if (!mDoublePassEvent)
mDoublePassEvent = ::RegisterWindowMessage(NS_OOPP_DOUBLEPASS_MSGID);
if (mDoublePassEvent) {
NPEvent pluginEvent;
pluginEvent.event = mDoublePassEvent;
pluginEvent.wParam = 0;
pluginEvent.lParam = 0;
PRBool eventHandled = PR_FALSE;
inst->HandleEvent(&pluginEvent, &eventHandled);
}
}
#endif
do {
HDC hdc = nativeDraw.BeginNativeDrawing();
if (!hdc)
@ -1737,30 +1754,9 @@ nsObjectFrame::PaintPlugin(nsIRenderingContext& aRenderingContext,
inst->SetWindow(window);
}
mInstanceOwner->Paint(dirty, hdc);
nativeDraw.EndNativeDrawing();
doublePass = nativeDraw.ShouldRenderAgain();
#ifdef MOZ_IPC
if (doublePass) {
// OOP plugin specific: let the shim know we are in the middle of a double pass
// render. The second pass will reuse the previous rendering without going over
// the wire.
if (!mDoublePassEvent)
mDoublePassEvent = ::RegisterWindowMessage(NS_OOPP_DOUBLEPASS_MSGID);
if (mDoublePassEvent) {
NPEvent pluginEvent;
pluginEvent.event = mDoublePassEvent;
pluginEvent.wParam = 0;
pluginEvent.lParam = 0;
PRBool eventHandled = PR_FALSE;
inst->HandleEvent(&pluginEvent, &eventHandled);
}
}
#endif
} while (doublePass);
} while (nativeDraw.ShouldRenderAgain());
nativeDraw.PaintToContext();
} else if (!(ctx->GetFlags() & gfxContext::FLAG_DESTINED_FOR_SCREEN)) {
// Get PrintWindow dynamically since it's not present on Win2K,

View File

@ -0,0 +1,27 @@
<!doctype html>
<html>
<head>
<style type="text/css">
#one {
position:absolute;
left:0px; top:0px;
width:400px; height:400px;
background-color: rgb(160,160,160);
opacity:0.5;
z-index:1;
}
#two {
position:absolute;
top:100px; left:100px;
width:200px; height:200px;
z-index:0;
background-color: rgb(255,0,0);
}
</style>
</head>
<body>
<div id="two"></div>
<div id="one"></div>
</body>
</html>

View File

@ -0,0 +1,26 @@
<!doctype html>
<html>
<head>
<style type="text/css">
#one {
position:absolute;
left:0px; top:0px;
width:400px; height:400px;
opacity:.5;
z-index:1;
}
#two {
position:absolute;
top:100px; left:100px;
width:200px; height:200px;
z-index:0;
background-color: rgb(255,0,0);
}
</style>
</head>
<body>
<div id="two"></div>
<embed id="one" type="application/x-test" width="400" height="400" drawmode="solid" color="FFa0a0a0"></embed>
</body>
</html>

View File

@ -2,6 +2,7 @@
random-if(!haveTestPlugin) != plugin-sanity.html about:blank
fails-if(!haveTestPlugin) == plugin-sanity.html div-sanity.html
fails-if(!haveTestPlugin) == plugin-alpha-zindex.html div-alpha-zindex.html
fails-if(!haveTestPlugin) == plugin-alpha-opacity.html div-alpha-opacity.html
fails-if(!haveTestPlugin) == windowless-clipping-1.html windowless-clipping-1-ref.html
fails-if(!haveTestPlugin) == border-padding-1.html border-padding-1-ref.html
fails-if(!haveTestPlugin) == border-padding-2.html border-padding-2-ref.html