Bug 531860 - plugin-alpha-zindex ref test fails with oopp enabled. r=jmuizelaar.
authorJim Mathies <jmathies@mozilla.com>
Mon, 14 Dec 2009 18:27:25 -0600
changeset 36270 10f2bc57c8041fa07b13db5412f01132b0ce71fd
parent 36269 f8fa55f60a969f7a5724a6b9beccbb5901dec6a4
child 36271 913708d05f0fd7f3f462b864763ca2c413a648e7
push idunknown
push userunknown
push dateunknown
reviewersjmuizelaar
bugs531860
milestone1.9.3a1pre
Bug 531860 - plugin-alpha-zindex ref test fails with oopp enabled. r=jmuizelaar.
dom/plugins/NPEventWindows.h
dom/plugins/PluginInstanceChild.cpp
dom/plugins/PluginInstanceChild.h
dom/plugins/PluginInstanceParent.cpp
dom/plugins/PluginInstanceParent.h
gfx/thebes/public/gfxWindowsNativeDrawing.h
gfx/thebes/src/gfxWindowsNativeDrawing.cpp
layout/generic/nsObjectFrame.cpp
modules/plugin/test/reftest/div-alpha-opacity.html
modules/plugin/test/reftest/plugin-alpha-opacity.html
modules/plugin/test/reftest/reftest.list
--- a/dom/plugins/NPEventWindows.h
+++ b/dom/plugins/NPEventWindows.h
@@ -46,17 +46,17 @@ namespace mozilla {
 namespace plugins {
 
 // We use an NPRemoteEvent struct so that we can store the extra data on
 // the stack so that we don't need to worry about managing the memory.
 struct NPRemoteEvent
 {
     NPEvent event;
     union {
-        NPRect rect;
+        RECT rect;
         WINDOWPOS windowpos;
     } lParamData;
 };
 
 }
 
 }
 
@@ -82,19 +82,19 @@ struct ParamTraits<mozilla::plugins::NPR
         switch (paramCopy.event.event) {
             case WM_WINDOWPOSCHANGED:
                 // The lParam paramter of WM_WINDOWPOSCHANGED holds a pointer to
                 // a WINDOWPOS structure that contains information about the
                 // window's new size and position
                 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
             case WM_CHAR:
             case WM_SYSCHAR:
 
             case WM_KEYUP:
             case WM_SYSKEYUP:
@@ -122,35 +122,39 @@ struct ParamTraits<mozilla::plugins::NPR
             case WM_LBUTTONDBLCLK:
             case WM_MBUTTONDBLCLK:
             case WM_RBUTTONDBLCLK:
 
             case WM_SETFOCUS:
             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;
         }
 
         aMsg->WriteBytes(&paramCopy, sizeof(paramType));
     }
 
     static bool Read(const Message* aMsg, void** aIter, paramType* aResult)
     {
         const char* bytes = 0;
 
         if (!aMsg->ReadBytes(aIter, &bytes, sizeof(paramType))) {
             return false;
         }
         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
             aResult->event.lParam = reinterpret_cast<LPARAM>(&aResult->lParamData.windowpos);
         }
 
         return true;
     }
--- a/dom/plugins/PluginInstanceChild.cpp
+++ b/dom/plugins/PluginInstanceChild.cpp
@@ -54,16 +54,17 @@ using namespace mozilla::plugins;
 #include <gdk/gdk.h>
 #include "gtk2xtbin.h"
 
 #elif defined(OS_WIN)
 using mozilla::gfx::SharedDIB;
 
 #include <windows.h>
 
+#define NS_OOPP_DOUBLEPASS_MSGID TEXT("MozDoublePassMsg")
 #endif
 
 PluginInstanceChild::PluginInstanceChild(const NPPluginFuncs* aPluginIface) :
         mPluginIface(aPluginIface)
 #if defined(OS_WIN)
         , mPluginWindowHWND(0)
         , mPluginWndProc(0)
         , mPluginParentHWND(0)
@@ -73,16 +74,20 @@ PluginInstanceChild::PluginInstanceChild
         mData.ndata = (void*) this;
 #if defined(MOZ_X11) && defined(XP_UNIX) && !defined(XP_MACOSX)
         mWindow.ws_info = &mWsInfo;
         memset(&mWsInfo, 0, sizeof(mWsInfo));
 #  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
     }
 
 PluginInstanceChild::~PluginInstanceChild()
 {
 #if defined(OS_WIN)
   DestroyPluginWindow();
 #endif
 }
@@ -337,19 +342,31 @@ PluginInstanceChild::AnswerNPP_HandleEve
         printf("  received drawable 0x%lx\n",
                event.event.xgraphicsexpose.drawable);
 #endif
 
     // Make a copy since we may modify values.
     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));
 
 #ifdef MOZ_X11
     if (GraphicsExpose == event.event.type) {
         // Make sure the X server completes the drawing before the parent
         // draws on top and destroys the Drawable.
@@ -670,16 +687,19 @@ PluginInstanceChild::SharedSurfaceSetWin
             return false;
         }
     }
     else {
         // Attach to the new shared surface parent handed us.
         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.
     mWindow.x      = 0;
     mWindow.y      = 0;
     mWindow.width  = aWindow.width;
     mWindow.height = aWindow.height;
     mWindow.type   = aWindow.type;
@@ -689,30 +709,147 @@ PluginInstanceChild::SharedSurfaceSetWin
 
     return true;
 }
 
 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::AlphaExtractCacheRelease()
+{
+    if (mAlphaExtract.bmp)
+        ::DeleteObject(mAlphaExtract.bmp);
+
+    if (mAlphaExtract.hdc)
+        ::DeleteObject(mAlphaExtract.hdc);
+
+    mAlphaExtract.bmp = NULL;
+    mAlphaExtract.hdc = NULL;
 }
 
 void
-PluginInstanceChild::SharedSurfaceBeforePaint(NPEvent& evcopy)
+PluginInstanceChild::UpdatePaintClipRect(RECT* aRect)
 {
-    // Update the clip rect on our internal hdc
+    if (aRect) {
+        // Update the clip rect on our internal hdc
+        HRGN clip = ::CreateRectRgnIndirect(aRect);
+        ::SelectClipRgn(mSharedSurfaceDib.GetHDC(), clip);
+        ::DeleteObject(clip);
+    }
+}
+
+int16_t
+PluginInstanceChild::SharedSurfacePaint(NPEvent& evcopy)
+{
     RECT* pRect = reinterpret_cast<RECT*>(evcopy.lParam);
-    if (pRect) {
-      HRGN clip = ::CreateRectRgnIndirect(pRect);
-      ::SelectClipRgn(mSharedSurfaceDib.GetHDC(), clip);
-      ::DeleteObject(clip);
+
+    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;
     }
-    // pass the internal hdc to the plugin
-    evcopy.wParam = WPARAM(mSharedSurfaceDib.GetHDC());
+    return false;
 }
 
 #endif // OS_WIN
 
 PPluginScriptableObjectChild*
 PluginInstanceChild::AllocPPluginScriptableObject()
 {
     AssertPluginThread();
--- a/dom/plugins/PluginInstanceChild.h
+++ b/dom/plugins/PluginInstanceChild.h
@@ -193,20 +193,34 @@ private:
 #endif
 
     nsTArray<nsAutoPtr<PluginScriptableObjectChild> > mScriptableObjects;
 
 #if defined(OS_WIN)
 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)
 };
 
 } // namespace plugins
 } // namespace mozilla
 
 #endif // ifndef dom_plugins_PluginInstanceChild_h
--- a/dom/plugins/PluginInstanceParent.cpp
+++ b/dom/plugins/PluginInstanceParent.cpp
@@ -42,38 +42,26 @@
 #include "BrowserStreamParent.h"
 #include "PluginModuleParent.h"
 #include "PluginStreamParent.h"
 #include "StreamNotifyParent.h"
 
 #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,
                                            NPP npp,
                                            const NPNetscapeFuncs* npniface)
   : mParent(parent),
     mNPP(npp),
     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()
 {
     if (mNPP)
         mNPP->pdata = NULL;
 }
 
@@ -462,26 +450,19 @@ PluginInstanceParent::NPP_HandleEvent(vo
     _MOZ_LOG(__FUNCTION__);
 
     NPEvent* npevent = reinterpret_cast<NPEvent*>(event);
     NPRemoteEvent npremoteevent;
     npremoteevent.event = *npevent;
 
 #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
 
 #if defined(MOZ_X11)
     if (GraphicsExpose == npevent->type) {
         printf("  schlepping drawable 0x%lx across the pipe\n",
                npevent->xgraphicsexpose.drawable);
         // Make sure the X server has created the Drawable and completes any
@@ -498,17 +479,17 @@ PluginInstanceParent::NPP_HandleEvent(vo
 #endif
 
     int16_t handled;
     if (!CallNPP_HandleEvent(npremoteevent, &handled)) {
         return 0;               // no good way to handle errors here...
     }
 
 #if defined(OS_WIN)
-    if (handled && mWindowType == NPWindowTypeDrawable && WM_PAINT == npevent->event)
+    if (handled && mWindowType == NPWindowTypeDrawable && npevent->event == WM_PAINT)
         SharedSurfaceAfterPaint(npevent);
 #endif
 
     return handled;
 }
 
 NPError
 PluginInstanceParent::NPP_NewStream(NPMIMEType type, NPStream* stream,
@@ -725,60 +706,44 @@ PluginInstanceParent::SharedSurfaceSetWi
     if (NS_FAILED(mSharedSurfaceDib.ShareToProcess(mParent->ChildProcessHandle(), &handle)))
       return false;
 
     aRemoteWindow.surfaceHandle = handle;
     
     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
 
     ::BitBlt(mSharedSurfaceDib.GetHDC(),
              dirtyRect.x,
              dirtyRect.y,
              dirtyRect.width,
              dirtyRect.height,
              parentHdc,
              dr->left,
              dr->top,
              SRCCOPY);
 
     // 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
 PluginInstanceParent::SharedSurfaceAfterPaint(NPEvent* npevent)
 {
     RECT* dr = (RECT*)npevent->lParam;
     HDC parentHdc = (HDC)npevent->wParam;
 
--- a/dom/plugins/PluginInstanceParent.h
+++ b/dom/plugins/PluginInstanceParent.h
@@ -212,26 +212,24 @@ private:
     NPWindowType mWindowType;
 
     nsTArray<nsAutoPtr<PluginScriptableObjectParent> > mScriptableObjects;
 
 #if defined(OS_WIN)
 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();
 
 private:
     gfx::SharedDIBWin  mSharedSurfaceDib;
     nsIntRect          mPluginPort;
     nsIntRect          mSharedSize;
-    PRUint32           mDoublePassEvent;
-    bool               mLocalCopyRender;
 #endif // defined(XP_WIN)
 };
 
 
 } // namespace plugins
 } // namespace mozilla
 
 #endif // ifndef dom_plugins_PluginInstanceParent_h
--- a/gfx/thebes/public/gfxWindowsNativeDrawing.h
+++ b/gfx/thebes/public/gfxWindowsNativeDrawing.h
@@ -106,16 +106,19 @@ public:
     void TransformToNativeRect(const gfxRect& r, RECT& rout);
 
     /* Marks the end of native drawing */
     void EndNativeDrawing();
 
     /* 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();
 
 private:
 
     nsRefPtr<gfxContext> mContext;
     gfxRect mNativeRect;
     PRUint32 mNativeDrawFlags;
--- a/gfx/thebes/src/gfxWindowsNativeDrawing.cpp
+++ b/gfx/thebes/src/gfxWindowsNativeDrawing.cpp
@@ -201,16 +201,32 @@ gfxWindowsNativeDrawing::BeginNativeDraw
         return mDC;
     } else {
         NS_ERROR("Bogus render state!");
         return nsnull;
     }
 }
 
 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()
 {
     switch (mRenderState) {
         case RENDER_STATE_NATIVE_DRAWING_DONE:
             return PR_FALSE;
 
         case RENDER_STATE_ALPHA_RECOVERY_BLACK_DONE:
             mRenderState = RENDER_STATE_ALPHA_RECOVERY_WHITE;
--- a/layout/generic/nsObjectFrame.cpp
+++ b/layout/generic/nsObjectFrame.cpp
@@ -1672,17 +1672,34 @@ nsObjectFrame::PaintPlugin(nsIRenderingC
 
     if (window->type == NPWindowTypeDrawable) {
       // check if we need to call SetWindow with updated parameters
       PRBool doupdatewindow = PR_FALSE;
       // the offset of the DC
       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)
           return;
 
         RECT dest;
         nativeDraw.TransformToNativeRect(frameGfxRect, dest);
         RECT dirty;
@@ -1732,40 +1749,19 @@ nsObjectFrame::PaintPlugin(nsIRenderingC
             pluginEvent.lParam = (uint32)&winpos;
             PRBool eventHandled = PR_FALSE;
 
             inst->HandleEvent(&pluginEvent, &eventHandled);
           }
 
           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,
       // which we still support
       typedef BOOL (WINAPI * PrintWindowPtr)
           (HWND hwnd, HDC hdcBlt, UINT nFlags);
       PrintWindowPtr printProc = nsnull;
       HMODULE module = ::GetModuleHandleW(L"user32.dll");
new file mode 100644
--- /dev/null
+++ b/modules/plugin/test/reftest/div-alpha-opacity.html
@@ -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>
+
new file mode 100644
--- /dev/null
+++ b/modules/plugin/test/reftest/plugin-alpha-opacity.html
@@ -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>
+
--- a/modules/plugin/test/reftest/reftest.list
+++ b/modules/plugin/test/reftest/reftest.list
@@ -1,9 +1,10 @@
 # basic sanity checking
 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
 # Disabled for now to investigate Windows/Linux test failures
 # fails-if(!haveTestPlugin) == border-padding-3.html border-padding-3-ref.html