Bug 626602, part 9: When possible, copy from a background to an opaque surface and have transparent plugins draw directly on the copied background, instead of doing alpha recovery or hoping plugins give us alpha values. r=bsmedberg,karlt sr=roc
authorChris Jones <jones.chris.g@gmail.com>
Tue, 08 Feb 2011 18:44:14 -0600
changeset 62208 a96c75dbc1518b048819170162ba2c9bec16cd40
parent 62207 9d3141affbe59260de88d44ce9696148a66b6d07
child 62209 a75cef6e752a983991dc8b127925cacb96ae3968
push id1
push userroot
push dateTue, 10 Dec 2013 15:46:25 +0000
reviewersbsmedberg, karlt, roc
bugs626602
milestone2.0b12pre
Bug 626602, part 9: When possible, copy from a background to an opaque surface and have transparent plugins draw directly on the copied background, instead of doing alpha recovery or hoping plugins give us alpha values. r=bsmedberg,karlt sr=roc
dom/plugins/PPluginBackgroundDestroyer.ipdl
dom/plugins/PPluginInstance.ipdl
dom/plugins/PluginBackgroundDestroyer.h
dom/plugins/PluginInstanceChild.cpp
dom/plugins/PluginInstanceChild.h
dom/plugins/PluginInstanceParent.cpp
dom/plugins/PluginInstanceParent.h
dom/plugins/PluginModuleChild.cpp
dom/plugins/PluginModuleChild.h
dom/plugins/ipdl.mk
modules/plugin/test/testplugin/nptest_gtk2.cpp
new file mode 100644
--- /dev/null
+++ b/dom/plugins/PPluginBackgroundDestroyer.ipdl
@@ -0,0 +1,70 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: sw=4 ts=8 et :
+ */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at:
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Code.
+ *
+ * The Initial Developer of the Original Code is
+ *   The Mozilla Foundation
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Chris Jones <jones.chris.g@gmail.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+include protocol PPluginInstance;
+
+namespace mozilla {
+namespace plugins {
+
+/**
+ * This protocol exists to allow us to correctly destroy background
+ * surfaces.  The browser owns the surfaces, but shares a "reference"
+ * with the plugin.  The browser needs to notify the plugin when the
+ * background is going to be destroyed, but it can't rely on the
+ * plugin to destroy it because the plugin may crash at any time.  So
+ * the plugin instance relinquishes destruction of the its old
+ * background to actors of this protocol, which can deal with crashy
+ * corner cases more easily than the instance.
+ */
+protocol PPluginBackgroundDestroyer {
+    manager PPluginInstance;
+
+    // The ctor message for this protocol serves double-duty as
+    // notification that that the background is stale.
+
+parent:
+    __delete__();
+
+state DESTROYING:
+    recv __delete__;
+};
+
+}  // namespace plugins
+}  // namespace mozilla
--- a/dom/plugins/PPluginInstance.ipdl
+++ b/dom/plugins/PPluginInstance.ipdl
@@ -32,16 +32,17 @@
  * use your version of this file under the terms of the MPL, indicate your
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
+include protocol PPluginBackgroundDestroyer;
 include protocol PPluginModule;
 include protocol PPluginScriptableObject;
 include protocol PBrowserStream;
 include protocol PPluginStream;
 include protocol PStreamNotify;
 include protocol PPluginSurface;
 
 include "mozilla/plugins/PluginMessageUtils.h";
@@ -52,16 +53,17 @@ using NPRemoteEvent;
 using NPRect;
 using NPNURLVariable;
 using NPCoordinateSpace;
 using mozilla::plugins::NativeWindowHandle;
 using mozilla::gfxSurfaceType;
 using gfxIntSize;
 using mozilla::null_t;
 using mozilla::plugins::WindowsSharedMemoryHandle;
+using nsIntRect;
 
 namespace mozilla {
 namespace plugins {
 
 struct SurfaceDescriptorX11 {
   int XID;
   int xrenderPictID;
   gfxIntSize size;
@@ -78,16 +80,17 @@ union SurfaceDescriptor {
   //     back without giving new surface
   null_t;
 };
 
 rpc protocol PPluginInstance
 {
   manager PPluginModule;
 
+  manages PPluginBackgroundDestroyer;
   manages PPluginScriptableObject;
   manages PBrowserStream;
   manages PPluginStream;
   manages PStreamNotify;
   manages PPluginSurface;
 
 child:
   rpc __delete__();
@@ -121,16 +124,23 @@ child:
   // **********************
 
   // Async version of SetWindow call
   // @param surfaceType - gfxASurface::gfxSurfaceType
   //        plugin child must create offscreen buffer
   //        with type equals to surfaceType
   async AsyncSetWindow(gfxSurfaceType surfaceType, NPRemoteWindow window);
 
+  // There is now an opaque background behind this instance (or the
+  // background was updated).  The changed area is |rect|.  The
+  // browser owns the background surface, and it's read-only from
+  // within the plugin process.  |background| is either null_t to
+  // refer to the existing background or a fresh descriptor.
+  async UpdateBackground(SurfaceDescriptor background, nsIntRect rect);
+
   rpc NPP_Destroy()
     returns (NPError rv);
 
 parent:
   rpc NPN_GetValue_NPNVjavascriptEnabledBool()
     returns (bool value, NPError result);
   rpc NPN_GetValue_NPNVisOfflineBool()
     returns (bool value, NPError result);
@@ -231,12 +241,14 @@ parent:
     returns (NPError result);
 
 parent:
   rpc PluginFocusChange(bool gotFocus);
 
 child:
   rpc SetPluginFocus();
   rpc UpdateWindow();
+
+  async PPluginBackgroundDestroyer();
 };
 
 } // namespace plugins
 } // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/plugins/PluginBackgroundDestroyer.h
@@ -0,0 +1,108 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: sw=4 ts=8 et :
+ */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at:
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Code.
+ *
+ * The Initial Developer of the Original Code is
+ *   The Mozilla Foundation
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Chris Jones <jones.chris.g@gmail.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef dom_plugins_PluginBackgroundDestroyer
+#define dom_plugins_PluginBackgroundDestroyer
+
+#include "mozilla/plugins/PPluginBackgroundDestroyerChild.h"
+#include "mozilla/plugins/PPluginBackgroundDestroyerParent.h"
+
+#include "gfxASurface.h"
+#include "gfxSharedImageSurface.h"
+
+namespace mozilla {
+namespace plugins {
+
+/**
+ * When instances of this class are destroyed, the old background goes
+ * along with them, completing the destruction process (whether or not
+ * the plugin stayed alive long enough to ack).
+ */
+class PluginBackgroundDestroyerParent : public PPluginBackgroundDestroyerParent {
+public:
+    PluginBackgroundDestroyerParent(gfxASurface* aDyingBackground)
+      : mDyingBackground(aDyingBackground)
+    { }
+
+    virtual ~PluginBackgroundDestroyerParent() { }
+
+private:
+    NS_OVERRIDE
+    virtual void ActorDestroy(ActorDestroyReason why)
+    {
+        switch(why) {
+        case Deletion:
+        case AncestorDeletion:
+            if (gfxSharedImageSurface::IsSharedImage(mDyingBackground)) {
+                gfxSharedImageSurface* s =
+                    static_cast<gfxSharedImageSurface*>(mDyingBackground.get());
+                DeallocShmem(s->GetShmem());
+            }
+            break;
+        default:
+            // We're shutting down or crashed, let automatic cleanup
+            // take care of our shmem, if we have one.
+            break;
+        }
+    }
+
+    nsRefPtr<gfxASurface> mDyingBackground;
+};
+
+/**
+ * This class exists solely to instruct its instance to release its
+ * current background, a new one may be coming.
+ */
+class PluginBackgroundDestroyerChild : public PPluginBackgroundDestroyerChild {
+public:
+    PluginBackgroundDestroyerChild() { }
+    virtual ~PluginBackgroundDestroyerChild() { }
+
+private:
+    // Implementing this for good hygiene.
+    NS_OVERRIDE
+    virtual void ActorDestroy(ActorDestroyReason why)
+    { }
+};
+
+} // namespace plugins
+} // namespace mozilla
+
+#endif  // dom_plugins_PluginBackgroundDestroyer
--- a/dom/plugins/PluginInstanceChild.cpp
+++ b/dom/plugins/PluginInstanceChild.cpp
@@ -32,16 +32,17 @@
  * use your version of this file under the terms of the MPL, indicate your
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
+#include "PluginBackgroundDestroyer.h"
 #include "PluginInstanceChild.h"
 #include "PluginModuleChild.h"
 #include "BrowserStreamChild.h"
 #include "PluginStreamChild.h"
 #include "StreamNotifyChild.h"
 #include "PluginProcessChild.h"
 #include "gfxASurface.h"
 #include "gfxContext.h"
@@ -161,17 +162,16 @@ PluginInstanceChild::PluginInstanceChild
     memset(&mWsInfo, 0, sizeof(mWsInfo));
     mWsInfo.display = DefaultXDisplay();
 #endif // MOZ_X11 && XP_UNIX && !XP_MACOSX
 #if defined(OS_WIN)
     memset(&mAlphaExtract, 0, sizeof(mAlphaExtract));
 #endif // OS_WIN
 #if defined(OS_WIN)
     InitPopupMenuHook();
-    HookSystemParametersInfo();
 #endif // OS_WIN
 #ifdef MOZ_X11
     // Maemo flash can render plugin with any provided rectangle and not require this quirk.
 #ifndef MOZ_PLATFORM_MAEMO
     const char *description = NULL;
     mPluginIface->getvalue(GetNPP(), NPPVpluginDescriptionString,
                            &description);
     if (description) {
@@ -919,16 +919,21 @@ PluginInstanceChild::AnswerNPP_SetWindow
                 // leak and mark as already leaked
                 g_object_set_data(G_OBJECT(gdkcolor),
                                   "moz-have-extra-ref", GUINT_TO_POINTER(1));
             }
         }
     }
 #endif
 
+    PLUGIN_LOG_DEBUG(
+        ("[InstanceChild][%p] Answer_SetWindow w=<x=%d,y=%d, w=%d,h=%d>, clip=<l=%d,t=%d,r=%d,b=%d>",
+         this, mWindow.x, mWindow.y, mWindow.width, mWindow.height,
+         mWindow.clipRect.left, mWindow.clipRect.top, mWindow.clipRect.right, mWindow.clipRect.bottom));
+
     if (mPluginIface->setwindow)
         (void) mPluginIface->setwindow(&mData, &mWindow);
 
 #elif defined(OS_WIN)
     switch (aWindow.type) {
       case NPWindowTypeWindow:
       {
           if (!CreatePluginWindow())
@@ -1223,57 +1228,16 @@ PluginInstanceChild::PluginWindowProc(HW
         self->DestroyPluginWindow();
 
     if (message == WM_NCDESTROY)
         RemoveProp(hWnd, kPluginInstanceChildProperty);
 
     return res;
 }
 
-/* system parameters info hook for flash */
-
-typedef BOOL (WINAPI *User32SystemParametersInfoW)(UINT uiAction,
-                                                   UINT uiParam,
-                                                   PVOID pvParam,
-                                                   UINT fWinIni);
-
-static User32SystemParametersInfoW sUser32SystemParametersInfoWStub = NULL;
-
-static BOOL WINAPI User32SystemParametersInfoHook(UINT uiAction,
-                                                  UINT uiParam,
-                                                  PVOID pvParam,
-                                                  UINT fWinIni)
-{
-  if (!sUser32SystemParametersInfoWStub) {
-      NS_NOTREACHED("sUser32SystemParametersInfoWStub not set??");
-      return FALSE;
-  }
-
-  // Tell them cleartype is disabled, so they don't mess with
-  // the alpha channel in our buffers.
-  if (uiAction == SPI_GETFONTSMOOTHINGTYPE && pvParam) {
-      *((UINT*)(pvParam)) = FE_FONTSMOOTHINGSTANDARD;
-      return TRUE;
-  }
-
-  return sUser32SystemParametersInfoWStub(uiAction, uiParam, pvParam, fWinIni);
-}
-
-void
-PluginInstanceChild::HookSystemParametersInfo()
-{
-    if (!(GetQuirks() & PluginModuleChild::QUIRK_FLASH_MASK_CLEARTYPE_SETTINGS))
-        return;
-    if (sUser32SystemParametersInfoWStub)
-        return;
-    sUser32Intercept.Init("gdi32.dll");
-    sUser32Intercept.AddHook("SystemParametersInfoW", User32SystemParametersInfoHook,
-                             (void**) &sUser32SystemParametersInfoWStub);
-}
-
 /* set window long ptr hook for flash */
 
 /*
  * Flash will reset the subclass of our widget at various times.
  * (Notably when entering and exiting full screen mode.) This
  * occurs independent of the main plugin window event procedure.
  * We trap these subclass calls to prevent our subclass hook from
  * getting dropped.
@@ -2282,29 +2246,37 @@ PluginInstanceChild::RecvAsyncSetWindow(
     return true;
 }
 
 void
 PluginInstanceChild::DoAsyncSetWindow(const gfxSurfaceType& aSurfaceType,
                                       const NPRemoteWindow& aWindow,
                                       bool aIsAsync)
 {
+    PLUGIN_LOG_DEBUG(
+        ("[InstanceChild][%p] AsyncSetWindow to <x=%d,y=%d, w=%d,h=%d>",
+         this, aWindow.x, aWindow.y, aWindow.width, aWindow.height));
+
     AssertPluginThread();
     NS_ASSERTION(!aWindow.window, "Remote window should be null.");
     NS_ASSERTION(!mPendingPluginCall, "Can't do SetWindow during plugin call!");
 
     if (aIsAsync) {
         if (!mCurrentAsyncSetWindowTask) {
             return;
         }
         mCurrentAsyncSetWindowTask = nsnull;
     }
 
     mWindow.window = NULL;
     if (mWindow.width != aWindow.width || mWindow.height != aWindow.height) {
+        // We weakly assume here that the SetWindow arrives before the
+        // next UpdateBackground, for the new window size, if we were
+        // going to get one.
+        mBackground = nsnull;
         ClearCurrentSurface();
         mAccumulatedInvalidRect = nsIntRect(0, 0, aWindow.width, aWindow.height);
     }
     if (mWindow.clipRect.top != aWindow.clipRect.top ||
         mWindow.clipRect.left != aWindow.clipRect.left ||
         mWindow.clipRect.bottom != aWindow.clipRect.bottom ||
         mWindow.clipRect.right != aWindow.clipRect.right)
         mAccumulatedInvalidRect = nsIntRect(0, 0, aWindow.width, aWindow.height);
@@ -2342,19 +2314,21 @@ GfxFromNsRect(const nsIntRect& aRect)
 }
 
 bool
 PluginInstanceChild::CreateOptSurface(void)
 {
     NS_ASSERTION(!mCurrentSurface, "mCurrentSurfaceActor can get out of sync.");
 
     nsRefPtr<gfxASurface> retsurf;
+    // Use an opaque surface unless we're transparent and *don't* have
+    // a background to source from.
     gfxASurface::gfxImageFormat format =
-        mIsTransparent ? gfxASurface::ImageFormatARGB32 :
-                         gfxASurface::ImageFormatRGB24;
+        (mIsTransparent && !mBackground) ? gfxASurface::ImageFormatARGB32 :
+                                           gfxASurface::ImageFormatRGB24;
 
 #if (MOZ_PLATFORM_MAEMO == 5) || (MOZ_PLATFORM_MAEMO == 6)
     // On Maemo 5, we must send the Visibility event to activate the plugin
     if (mMaemoImageRendering) {
         NPEvent pluginEvent;
         XVisibilityEvent& visibilityEvent = pluginEvent.xvisibility;
         visibilityEvent.type = VisibilityNotify;
         visibilityEvent.display = 0;
@@ -2382,20 +2356,22 @@ PluginInstanceChild::CreateOptSurface(vo
                                               mWindow.height));
         return mCurrentSurface != nsnull;
     }
 #endif
 
 #ifdef XP_WIN
     if (mSurfaceType == gfxASurface::SurfaceTypeWin32 ||
         mSurfaceType == gfxASurface::SurfaceTypeD2D) {
+        bool willHaveTransparentPixels = mIsTransparent && !mBackground;
 
         SharedDIBSurface* s = new SharedDIBSurface();
         if (!s->Create(reinterpret_cast<HDC>(mWindow.window),
-                       mWindow.width, mWindow.height, mIsTransparent))
+                       mWindow.width, mWindow.height,
+                       willHaveTransparentPixels))
             return false;
 
         mCurrentSurface = s;
         return true;
     }
 
     NS_RUNTIMEABORT("Shared-memory drawing not expected on Windows.");
 #endif
@@ -2466,16 +2442,18 @@ PluginInstanceChild::MaybeCreatePlatform
         mHelperSurface =
             gfxXlibSurface::Create(screen, visual,
                                    mCurrentSurface->GetSize());
         if (!mHelperSurface) {
             NS_WARNING("Fail to create create helper surface");
             return false;
         }
     }
+#elif defined(XP_WIN)
+    mDoAlphaExtraction = mIsTransparent && !mBackground;
 #endif
 
     return true;
 }
 
 bool
 PluginInstanceChild::EnsureCurrentBuffer(void)
 {
@@ -2524,17 +2502,17 @@ PluginInstanceChild::UpdateWindowAttribu
         // and specify depth of image surface
         gfxImageSurface* img = static_cast<gfxImageSurface*>(curSurface.get());
         if (mWindow.window ||
             mWsInfo.depth != gfxUtils::ImageFormatToDepth(img->Format()) ||
             mWsInfo.colormap) {
             mWindow.window = nsnull;
             mWsInfo.depth = gfxUtils::ImageFormatToDepth(img->Format());
             mWsInfo.colormap = 0;
-            needWindowUpdate = PR_TRUE;
+            needWindowUpdate = true;
         }
     }
 #endif // MAEMO
 #endif // MOZ_X11
 #ifdef XP_WIN
     HDC dc = NULL;
 
     if (curSurface) {
@@ -2590,31 +2568,30 @@ PluginInstanceChild::UpdateWindowAttribu
         NPEvent pluginEvent = {
             WM_WINDOWPOSCHANGED, 0,
             (LPARAM) &winpos
         };
         mPluginIface->event(&mData, &pluginEvent);
     }
 #endif
 
+    PLUGIN_LOG_DEBUG(
+        ("[InstanceChild][%p] UpdateWindow w=<x=%d,y=%d, w=%d,h=%d>, clip=<l=%d,t=%d,r=%d,b=%d>",
+         this, mWindow.x, mWindow.y, mWindow.width, mWindow.height,
+         mWindow.clipRect.left, mWindow.clipRect.top, mWindow.clipRect.right, mWindow.clipRect.bottom));
+
     if (mPluginIface->setwindow) {
         mPluginIface->setwindow(&mData, &mWindow);
     }
 }
 
 void
 PluginInstanceChild::PaintRectToPlatformSurface(const nsIntRect& aRect,
                                                 gfxASurface* aSurface)
 {
-    bool temporarilyMakeVisible = !IsVisible() && !mHasPainted;
-    if (temporarilyMakeVisible) {
-        mWindow.clipRect.right = mWindow.width;
-        mWindow.clipRect.bottom = mWindow.height;
-    }
-
     UpdateWindowAttributes();
 
 #ifdef MOZ_X11
 #if (MOZ_PLATFORM_MAEMO == 5) || (MOZ_PLATFORM_MAEMO == 6)
     // On maemo5 we do support Image rendering NPAPI
     if (mMaemoImageRendering &&
         aSurface->GetType() == gfxASurface::SurfaceTypeImage) {
         aSurface->Flush();
@@ -2693,24 +2670,16 @@ PluginInstanceChild::PaintRectToPlatform
 
     ::SetViewportOrgEx((HDC) mWindow.window, -mWindow.x, -mWindow.y, NULL);
     ::SelectClipRgn((HDC) mWindow.window, NULL);
     ::IntersectClipRect((HDC) mWindow.window, rect.left, rect.top, rect.right, rect.bottom);
     mPluginIface->event(&mData, reinterpret_cast<void*>(&paintEvent));
 #else
     NS_RUNTIMEABORT("Surface type not implemented.");
 #endif
-
-    if (temporarilyMakeVisible) {
-        mWindow.clipRect.right = mWindow.clipRect.bottom = 0;
-
-        if (mPluginIface->setwindow) {
-            mPluginIface->setwindow(&mData, &mWindow);
-        }
-    }
 }
 
 void
 PluginInstanceChild::PaintRectToSurface(const nsIntRect& aRect,
                                         gfxASurface* aSurface,
                                         const gfxRGBA& aColor)
 {
     // Render using temporary X surface, with copy to image surface
@@ -2730,17 +2699,17 @@ PluginInstanceChild::PaintRectToSurface(
         // Don't use mHelperSurface if surface is image and mMaemoImageRendering is TRUE
         if (!mMaemoImageRendering ||
             renderSurface->GetType() != gfxASurface::SurfaceTypeImage)
 #endif
         renderSurface = mHelperSurface;
     }
 #endif
 
-    if (mIsTransparent) {
+    if (aColor.a > 0.0) {
        // Clear surface content for transparent rendering
        nsRefPtr<gfxContext> ctx = new gfxContext(renderSurface);
        ctx->SetColor(aColor);
        ctx->SetOperator(gfxContext::OPERATOR_SOURCE);
        ctx->Rectangle(GfxFromNsRect(plPaintRect));
        ctx->Fill();
     }
 
@@ -2854,52 +2823,106 @@ PluginInstanceChild::PaintRectWithAlphaE
         ctx->SetOperator(gfxContext::OPERATOR_SOURCE);
         ctx->SetSource(blackImage);
         ctx->Rectangle(targetRect);
         ctx->Fill();
     }
 }
 
 bool
+PluginInstanceChild::CanPaintOnBackground()
+{
+    return (mBackground &&
+            mCurrentSurface &&
+            mCurrentSurface->GetSize() == mBackground->GetSize());
+}
+
+bool
 PluginInstanceChild::ShowPluginFrame()
 {
     if (mPendingPluginCall) {
         return false;
     }
 
     AutoRestore<bool> pending(mPendingPluginCall);
     mPendingPluginCall = true;
 
     if (!EnsureCurrentBuffer()) {
         return false;
     }
 
-    // Make expose rect not bigger than clip rect
-    mAccumulatedInvalidRect.IntersectRect(mAccumulatedInvalidRect,
-        nsIntRect(mWindow.clipRect.left, mWindow.clipRect.top,
-                  mWindow.clipRect.right - mWindow.clipRect.left,
-                  mWindow.clipRect.bottom - mWindow.clipRect.top));
+    bool temporarilyMakeVisible = !IsVisible() && !mHasPainted;
+    if (temporarilyMakeVisible) {
+        mWindow.clipRect.right = mWindow.width;
+        mWindow.clipRect.bottom = mWindow.height;
+    } else if (!IsVisible()) {
+        // If we're not visible, don't bother painting a <0,0,0,0>
+        // rect.  If we're eventually made visible, the visibility
+        // change will invalidate our window.
+        return true;
+    }
+
+    NS_ASSERTION(mWindow.width == (mWindow.clipRect.right - mWindow.clipRect.left) &&
+                 mWindow.height == (mWindow.clipRect.bottom - mWindow.clipRect.top),
+                 "Clip rect should be same size as window when using layers");
 
     // Clear accRect here to be able to pass
     // test_invalidate_during_plugin_paint  test
     nsIntRect rect = mAccumulatedInvalidRect;
     mAccumulatedInvalidRect.Empty();
 
     if (!ReadbackDifferenceRect(rect)) {
-        // Just repaint whole plugin, because we cannot read back from Shmem which is owned by another process
+        // We couldn't read back the pixels that differ between the
+        // current surface and last, so we have to invalidate the
+        // entire window.
         rect.SetRect(0, 0, mWindow.width, mWindow.height);
     }
 
-    if (mDoAlphaExtraction) {
+    bool haveTransparentPixels =
+        gfxASurface::CONTENT_COLOR_ALPHA == mCurrentSurface->GetContentType();
+    PLUGIN_LOG_DEBUG(
+        ("[InstanceChild][%p] Painting%s <x=%d,y=%d, w=%d,h=%d>",
+         this, haveTransparentPixels ? " with alpha" : "",
+         rect.x, rect.y, rect.width, rect.height));
+
+    if (CanPaintOnBackground()) {
+        PLUGIN_LOG_DEBUG(("  (on background)"));
+        // Source the background pixels ...
+        {
+            nsRefPtr<gfxContext> ctx = new gfxContext(mCurrentSurface);
+            ctx->SetSource(mBackground);
+            ctx->SetOperator(gfxContext::OPERATOR_SOURCE);
+            ctx->Rectangle(gfxRect(rect.x, rect.y, rect.width, rect.height));
+            ctx->Fill();
+        }
+        // ... and hand off to the plugin
+        // BEWARE: mBackground may die during this call
+        PaintRectToSurface(rect, mCurrentSurface, gfxRGBA(0.0, 0.0, 0.0, 0.0));
+    } else if (mDoAlphaExtraction) {
+        PLUGIN_LOG_DEBUG(("  (with alpha recovery)"));
         PaintRectWithAlphaExtraction(rect, mCurrentSurface);
     } else {
+        PLUGIN_LOG_DEBUG(("  (onto opaque surface)"));
         PaintRectToSurface(rect, mCurrentSurface, gfxRGBA(0.0, 0.0, 0.0, 0.0));
     }
     mHasPainted = true;
 
+    if (temporarilyMakeVisible) {
+        mWindow.clipRect.right = mWindow.clipRect.bottom = 0;
+
+        PLUGIN_LOG_DEBUG(
+            ("[InstanceChild][%p] Undoing temporary clipping w=<x=%d,y=%d, w=%d,h=%d>, clip=<l=%d,t=%d,r=%d,b=%d>",
+             this, mWindow.x, mWindow.y, mWindow.width, mWindow.height,
+             mWindow.clipRect.left, mWindow.clipRect.top, mWindow.clipRect.right, mWindow.clipRect.bottom));
+
+        if (mPluginIface->setwindow) {
+            mPluginIface->setwindow(&mData, &mWindow);
+        }
+    }
+
     NPRect r = { (uint16_t)rect.y, (uint16_t)rect.x,
                  (uint16_t)rect.YMost(), (uint16_t)rect.XMost() };
     SurfaceDescriptor currSurf;
 #ifdef MOZ_X11
     if (mCurrentSurface->GetType() == gfxASurface::SurfaceTypeXlib) {
         gfxXlibSurface *xsurf = static_cast<gfxXlibSurface*>(mCurrentSurface.get());
         currSurf = SurfaceDescriptorX11(xsurf->XDrawable(), xsurf->XRenderFormat()->id,
                                         mCurrentSurface->GetSize());
@@ -2913,17 +2936,17 @@ PluginInstanceChild::ShowPluginFrame()
         SharedDIBSurface* s = static_cast<SharedDIBSurface*>(mCurrentSurface.get());
         if (!mCurrentSurfaceActor) {
             base::SharedMemoryHandle handle = NULL;
             s->ShareToProcess(PluginModuleChild::current()->OtherProcess(), &handle);
 
             mCurrentSurfaceActor =
                 SendPPluginSurfaceConstructor(handle,
                                               mCurrentSurface->GetSize(),
-                                              mIsTransparent);
+                                              haveTransparentPixels);
         }
         currSurf = mCurrentSurfaceActor;
         s->Flush();
     } else
 #endif
     if (gfxSharedImageSurface::IsSharedImage(mCurrentSurface)) {
         currSurf = static_cast<gfxSharedImageSurface*>(mCurrentSurface.get())->GetShmem();
     } else {
@@ -2957,19 +2980,27 @@ PluginInstanceChild::ReadbackDifferenceR
         return false;
 #elif defined(XP_WIN)
     if (!SharedDIBSurface::IsSharedDIBSurface(mBackSurface))
         return false;
 #else
     return false;
 #endif
 
+    if (mCurrentSurface->GetContentType() != mBackSurface->GetContentType())
+        return false;
+
     if (mSurfaceDifferenceRect.IsEmpty())
         return true;
 
+    PLUGIN_LOG_DEBUG(
+        ("[InstanceChild][%p] Reading back part of <x=%d,y=%d, w=%d,h=%d>",
+         this, mSurfaceDifferenceRect.x, mSurfaceDifferenceRect.y,
+         mSurfaceDifferenceRect.width, mSurfaceDifferenceRect.height));
+
     // Read back previous content
     nsRefPtr<gfxContext> ctx = new gfxContext(mCurrentSurface);
     ctx->SetOperator(gfxContext::OPERATOR_SOURCE);
     ctx->SetSource(mBackSurface);
     // Subtract from mSurfaceDifferenceRect area which is overlapping with rect
     nsIntRegion result;
     result.Sub(mSurfaceDifferenceRect, nsIntRegion(rect));
     nsIntRegionRectIterator iter(result);
@@ -3036,16 +3067,120 @@ PluginInstanceChild::InvalidateRect(NPRe
         // If we are able to paint and invalidate sent, then reset
         // accumulated rectangle
         AsyncShowPluginFrame();
         return;
     }
     SendNPN_InvalidateRect(*aInvalidRect);
 }
 
+bool
+PluginInstanceChild::RecvUpdateBackground(const SurfaceDescriptor& aBackground,
+                                          const nsIntRect& aRect)
+{
+    NS_ABORT_IF_FALSE(mIsTransparent, "Only transparent plugins use backgrounds");
+
+    if (SurfaceDescriptor::Tnull_t == aBackground.type()) {
+        // null_t here means "use existing background".  We're
+        // notified when we lose our background through
+        // PPluginBackgroundDestroyer.  We might have already dropped
+        // our background from an ill-timed SetWindow, though.
+        if (!mBackground) {
+            return true;
+        }
+    } else {
+        NS_ABORT_IF_FALSE(!mBackground, "Shouldn't have background here");
+
+        // Now that we have a new background, our choice of surface
+        // format and/or size should have changed.
+        ClearCurrentSurface();
+
+        // XXX refactor me
+        switch (aBackground.type()) {
+#ifdef MOZ_X11
+        case SurfaceDescriptor::TSurfaceDescriptorX11: {
+            SurfaceDescriptorX11 xdesc = aBackground.get_SurfaceDescriptorX11();
+            XRenderPictFormat pf;
+            pf.id = xdesc.xrenderPictID();
+            XRenderPictFormat *incFormat =
+                XRenderFindFormat(DefaultXDisplay(), PictFormatID, &pf, 0);
+            mBackground =
+                new gfxXlibSurface(DefaultScreenOfDisplay(DefaultXDisplay()),
+                                   xdesc.XID(), incFormat, xdesc.size());
+            break;
+        }
+#endif
+        case SurfaceDescriptor::TShmem: {
+            mBackground = gfxSharedImageSurface::Open(aBackground.get_Shmem());
+            break;
+        }
+        default:
+            NS_RUNTIMEABORT("Unexpected background surface descriptor");
+        }
+    }
+
+    if (!mBackground) {
+        return false;
+    }
+
+    // XXX refactor me
+    mAccumulatedInvalidRect.UnionRect(aRect, mAccumulatedInvalidRect);
+
+    // The browser is limping along with a stale copy of our pixels.
+    // Try to repaint ASAP.
+    if (!ShowPluginFrame()) {
+        NS_WARNING("Couldn't immediately repaint plugin instance");
+        AsyncShowPluginFrame();
+    }
+
+    return true;
+}
+
+PPluginBackgroundDestroyerChild*
+PluginInstanceChild::AllocPPluginBackgroundDestroyer()
+{
+    return new PluginBackgroundDestroyerChild();
+}
+
+bool
+PluginInstanceChild::RecvPPluginBackgroundDestroyerConstructor(
+    PPluginBackgroundDestroyerChild* aActor)
+{
+    // Our background changed, so we have to invalidate the area
+    // painted with the old background.  If the background was
+    // destroyed because we have a new background, then we expect to
+    // be notified of that "soon", before processing the asynchronous
+    // invalidation here.  If we're *not* getting a new background,
+    // our current front surface is stale and we want to repaint
+    // "soon" so that we can hand the browser back a surface with
+    // alpha values.  (We should be notified of that invalidation soon
+    // too, but we don't assume that here.)
+    if (mBackground) {
+        gfxIntSize bgsize = mBackground->GetSize();
+        mAccumulatedInvalidRect.UnionRect(
+            nsIntRect(0, 0, bgsize.width, bgsize.height), mAccumulatedInvalidRect);
+        AsyncShowPluginFrame();
+
+        // NB: we don't have to XSync here because only ShowPluginFrame()
+        // uses mBackground, and it always XSyncs after finishing.
+        mBackground = nsnull;
+        ClearCurrentSurface();
+    }
+
+    return PPluginBackgroundDestroyerChild::Send__delete__(aActor);
+}
+
+bool
+PluginInstanceChild::DeallocPPluginBackgroundDestroyer(
+    PPluginBackgroundDestroyerChild* aActor)
+{
+    delete aActor;
+    return true;
+}
+
 uint32_t
 PluginInstanceChild::ScheduleTimer(uint32_t interval, bool repeat,
                                    TimerFunc func)
 {
     ChildTimer* t = new ChildTimer(this, interval, repeat, func);
     if (0 == t->ID()) {
         delete t;
         return 0;
@@ -3125,17 +3260,18 @@ PluginInstanceChild::SwapSurfaces()
     mBackSurface = tmpsurf;
 #ifdef XP_WIN
     mBackSurfaceActor = tmpactor;
 #endif
 
     // Outdated back surface... not usable anymore due to changed plugin size.
     // Dropping obsolete surface
     if (mCurrentSurface && mBackSurface &&
-        mCurrentSurface->GetSize() != mBackSurface->GetSize()) {
+        (mCurrentSurface->GetSize() != mBackSurface->GetSize() ||
+         mCurrentSurface->GetContentType() != mBackSurface->GetContentType())) {
         mCurrentSurface = nsnull;
 #ifdef XP_WIN
         if (mCurrentSurfaceActor) {
             PPluginSurfaceChild::Send__delete__(mCurrentSurfaceActor);
             mCurrentSurfaceActor = NULL;
         }
 #endif
     }
--- a/dom/plugins/PluginInstanceChild.h
+++ b/dom/plugins/PluginInstanceChild.h
@@ -231,16 +231,32 @@ public:
 
 private:
     friend class PluginModuleChild;
 
     NPError
     InternalGetNPObjectForValue(NPNVariable aValue,
                                 NPObject** aObject);
 
+    NS_OVERRIDE
+    virtual bool RecvUpdateBackground(const SurfaceDescriptor& aBackground,
+                                      const nsIntRect& aRect);
+
+    NS_OVERRIDE
+    virtual PPluginBackgroundDestroyerChild*
+    AllocPPluginBackgroundDestroyer();
+
+    NS_OVERRIDE
+    virtual bool
+    RecvPPluginBackgroundDestroyerConstructor(PPluginBackgroundDestroyerChild* aActor);
+
+    NS_OVERRIDE
+    virtual bool
+    DeallocPPluginBackgroundDestroyer(PPluginBackgroundDestroyerChild* aActor);
+
 #if defined(OS_WIN)
     static bool RegisterWindowClass();
     bool CreatePluginWindow();
     void DestroyPluginWindow();
     void ReparentPluginWindow(HWND hWndParent);
     void SizePluginWindow(int width, int height);
     int16_t WinlessHandleEvent(NPEvent& event);
     void CreateWinlessPopupSurrogate();
@@ -285,17 +301,16 @@ private:
 #else
     static LONG WINAPI SetWindowLongAHook(HWND hWnd,
                                           int nIndex,
                                           LONG newLong);
     static LONG WINAPI SetWindowLongWHook(HWND hWnd,
                                           int nIndex,
                                           LONG newLong);
 #endif
-    void HookSystemParametersInfo();
 
     class FlashThrottleAsyncMsg : public ChildAsyncCall
     {
       public:
         FlashThrottleAsyncMsg();
         FlashThrottleAsyncMsg(PluginInstanceChild* aInst, 
                               HWND aWnd, UINT aMsg,
                               WPARAM aWParam, LPARAM aLParam,
@@ -402,16 +417,18 @@ public:
 #if defined(__i386__)
     NPEventModel EventModel() { return mEventModel; }
 #endif
 
 private:
     const NPCocoaEvent   *mCurrentEvent;
 #endif
 
+    bool CanPaintOnBackground();
+
     bool IsVisible() {
         return mWindow.clipRect.top != 0 ||
             mWindow.clipRect.left != 0 ||
             mWindow.clipRect.bottom != 0 ||
             mWindow.clipRect.right != 0;
     }
 
     // ShowPluginFrame - in general does four things:
@@ -483,16 +500,23 @@ private:
 
     // Current surface available for rendering
     nsRefPtr<gfxASurface> mCurrentSurface;
 
     // Back surface, just keeping reference to
     // surface which is on ParentProcess side
     nsRefPtr<gfxASurface> mBackSurface;
 
+    // (Not to be confused with mBackSurface).  This is a recent copy
+    // of the opaque pixels under our object frame, if
+    // |mIsTransparent|.  We ask the plugin render directly onto a
+    // copy of the background pixels if available, and fall back on
+    // alpha recovery otherwise.
+    nsRefPtr<gfxASurface> mBackground;
+
 #ifdef XP_WIN
     // These actors mirror mCurrentSurface/mBackSurface
     PPluginSurfaceChild* mCurrentSurfaceActor;
     PPluginSurfaceChild* mBackSurfaceActor;
 #endif
 
     // Accumulated invalidate rect, while back buffer is not accessible,
     // in plugin coordinates.
--- a/dom/plugins/PluginInstanceParent.cpp
+++ b/dom/plugins/PluginInstanceParent.cpp
@@ -35,16 +35,17 @@
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "PluginInstanceParent.h"
 
 #include "BrowserStreamParent.h"
+#include "PluginBackgroundDestroyer.h"
 #include "PluginModuleParent.h"
 #include "PluginStreamParent.h"
 #include "StreamNotifyParent.h"
 #include "npfunctions.h"
 #include "nsAutoPtr.h"
 #include "mozilla/unused.h"
 #include "gfxASurface.h"
 #include "gfxContext.h"
@@ -103,16 +104,17 @@ PluginInstanceParent::PluginInstancePare
     , mQuirks(0)
 #if defined(XP_MACOSX)
     , mShWidth(0)
     , mShHeight(0)
     , mShColorSpace(nsnull)
     , mDrawingModel(NPDrawingModelCoreGraphics)
     , mIOSurface(nsnull)
 #endif
+    , mNewBackground(false)
 {
     InitQuirksModes(aMimeType);
 }
 
 void
 PluginInstanceParent::InitQuirksModes(const nsCString& aMimeType)
 {
 #ifdef OS_MACOSX
@@ -493,16 +495,22 @@ PluginInstanceParent::RecvNPN_Invalidate
     return true;
 }
 
 bool
 PluginInstanceParent::RecvShow(const NPRect& updatedRect,
                                const SurfaceDescriptor& newSurface,
                                SurfaceDescriptor* prevSurface)
 {
+    PLUGIN_LOG_DEBUG(
+        ("[InstanceParent][%p] RecvShow for <x=%d,y=%d, w=%d,h=%d>",
+         this, updatedRect.left, updatedRect.top,
+         updatedRect.right - updatedRect.left,
+         updatedRect.bottom - updatedRect.top));
+
     nsRefPtr<gfxASurface> surface;
     if (newSurface.type() == SurfaceDescriptor::TShmem) {
         if (!newSurface.get_Shmem().IsReadable()) {
             NS_WARNING("back surface not readable");
             return false;
         }
         surface = gfxSharedImageSurface::Open(newSurface.get_Shmem());
     }
@@ -524,27 +532,35 @@ PluginInstanceParent::RecvShow(const NPR
             static_cast<PluginSurfaceParent*>(newSurface.get_PPluginSurfaceParent());
         surface = s->Surface();
     }
 #endif
 
 #ifdef MOZ_X11
     if (mFrontSurface &&
         mFrontSurface->GetType() == gfxASurface::SurfaceTypeXlib)
+        // This is the "old front buffer" we're about to hand back to
+        // the plugin.  We might still have drawing operations
+        // referencing it, so we XSync here to let them finish before
+        // the plugin starts scribbling on it again, or worse,
+        // destroys it.
         XSync(DefaultXDisplay(), False);
 #endif
 
     if (mFrontSurface && gfxSharedImageSurface::IsSharedImage(mFrontSurface))
         *prevSurface = static_cast<gfxSharedImageSurface*>(mFrontSurface.get())->GetShmem();
     else
         *prevSurface = null_t();
 
     mFrontSurface = surface;
     RecvNPN_InvalidateRect(updatedRect);
 
+    PLUGIN_LOG_DEBUG(("   (RecvShow invalidated for surface %p)",
+                      mFrontSurface.get()));
+
     return true;
 }
 
 nsresult
 PluginInstanceParent::AsyncSetWindow(NPWindow* aWindow)
 {
     NPRemoteWindow window;
     mWindowType = aWindow->type;
@@ -567,16 +583,185 @@ PluginInstanceParent::GetSurface(gfxASur
 {
     if (mFrontSurface) {
       NS_ADDREF(*aSurface = mFrontSurface);
       return NS_OK;
     }
     return NS_ERROR_NOT_AVAILABLE;
 }
 
+nsresult
+PluginInstanceParent::SetBackgroundUnknown()
+{
+    PLUGIN_LOG_DEBUG(("[InstanceParent][%p] SetBackgroundUnknown", this));
+
+    NS_ABORT_IF_FALSE(!mNewBackground, "Forgot EndUpdateBackground?");
+
+    if (mBackground) {
+        DestroyBackground();
+        NS_ABORT_IF_FALSE(!mBackground, "Background not destroyed");
+    }
+
+    return NS_OK;
+}
+
+nsresult
+PluginInstanceParent::BeginUpdateBackground(const nsIntRect& aRect,
+                                            gfxContext** aCtx)
+{
+    PLUGIN_LOG_DEBUG(
+        ("[InstanceParent][%p] BeginUpdateBackground for <x=%d,y=%d, w=%d,h=%d>",
+         this, aRect.x, aRect.y, aRect.width, aRect.height));
+
+    NS_ABORT_IF_FALSE(!mNewBackground, "Forgot EndUpdateBackground?");
+
+    if (!mBackground) {
+        // XXX if we failed to create a background surface on one
+        // update, there's no guarantee that later updates will be for
+        // the entire background area until successful.  We might want
+        // to fix that eventually.
+        NS_ABORT_IF_FALSE(aRect.TopLeft() == nsIntPoint(0, 0),
+                          "Expecting rect for whole frame");
+        if (!CreateBackground(aRect.Size())) {
+            *aCtx = nsnull;
+            return NS_OK;
+        }
+
+        // The new background forwarded over in EndUpdate below.
+        mNewBackground = true;
+    }
+
+#ifdef DEBUG
+    gfxIntSize sz = mBackground->GetSize();
+    NS_ABORT_IF_FALSE(nsIntRect(0, 0, sz.width, sz.height).Contains(aRect),
+                      "Update outside of background area");
+#endif
+
+    nsRefPtr<gfxContext> ctx = new gfxContext(mBackground);
+    *aCtx = ctx.forget().get();
+
+    return NS_OK;
+}
+
+nsresult
+PluginInstanceParent::EndUpdateBackground(gfxContext* aCtx,
+                                          const nsIntRect& aRect)
+{
+    PLUGIN_LOG_DEBUG(
+        ("[InstanceParent][%p] EndUpdateBackground for <x=%d,y=%d, w=%d,h=%d>",
+         this, aRect.x, aRect.y, aRect.width, aRect.height));
+
+#ifdef MOZ_X11
+    // Have to XSync here to avoid the plugin trying to draw with this
+    // surface racing with its creation in the X server.  We also want
+    // to avoid the plugin drawing onto stale pixels, then handing us
+    // back a front surface from those pixels that we might
+    // recomposite for "a while" until the next update.  This XSync
+    // still doesn't guarantee that the plugin draws onto a consistent
+    // view of its background, but it does mean that the plugin is
+    // drawing onto pixels no older than those in the latest
+    // EndUpdateBackground().
+    XSync(DefaultXDisplay(), False);
+#endif
+
+    unused << SendUpdateBackground(
+        mNewBackground ? BackgroundDescriptor() : SurfaceDescriptor(null_t()),
+        aRect);
+    mNewBackground = false;
+
+    return NS_OK;
+}
+
+bool
+PluginInstanceParent::CreateBackground(const nsIntSize& aSize)
+{
+    NS_ABORT_IF_FALSE(!mBackground, "Already have a background");
+
+    // XXX refactor me
+
+#if defined(MOZ_X11)
+    Screen* screen = DefaultScreenOfDisplay(DefaultXDisplay());
+    Visual* visual = DefaultVisualOfScreen(screen);
+    mBackground = gfxXlibSurface::Create(screen, visual,
+                                         gfxIntSize(aSize.width, aSize.height));
+    return !!mBackground;
+
+#elif defined(XP_WIN)
+    // We have chosen to create an unsafe surface in which the plugin
+    // can read from the region while we're writing to it.
+    mBackground =
+        gfxSharedImageSurface::CreateUnsafe(
+            this,
+            gfxIntSize(aSize.width, aSize.height),
+            gfxASurface::ImageFormatRGB24);
+    return !!mBackground;
+#else
+    return nsnull;
+#endif
+}
+
+void
+PluginInstanceParent::DestroyBackground()
+{
+    if (!mBackground) {
+        return;
+    }
+
+    // Relinquish ownership of |mBackground| to its destroyer
+    PPluginBackgroundDestroyerParent* pbd =
+        new PluginBackgroundDestroyerParent(mBackground);
+    mBackground = nsnull;
+
+    // If this fails, there's no problem: |bd| will be destroyed along
+    // with the old background surface.
+    unused << SendPPluginBackgroundDestroyerConstructor(pbd);
+}
+
+SurfaceDescriptor
+PluginInstanceParent::BackgroundDescriptor()
+{
+    NS_ABORT_IF_FALSE(mBackground, "Need a background here");
+
+    // XXX refactor me
+
+#ifdef MOZ_X11
+    gfxXlibSurface* xsurf = static_cast<gfxXlibSurface*>(mBackground.get());
+    return SurfaceDescriptorX11(xsurf->XDrawable(), xsurf->XRenderFormat()->id,
+                                xsurf->GetSize());
+#endif
+
+#ifdef XP_WIN
+    NS_ABORT_IF_FALSE(gfxSharedImageSurface::IsSharedImage(mBackground),
+                      "Expected shared image surface");
+    gfxSharedImageSurface* shmem =
+        static_cast<gfxSharedImageSurface*>(mBackground.get());
+    return shmem->GetShmem();
+#endif
+
+    // If this is ever used, which it shouldn't be, it will trigger a
+    // hard assertion in IPDL-generated code.
+    return SurfaceDescriptor();
+}
+
+PPluginBackgroundDestroyerParent*
+PluginInstanceParent::AllocPPluginBackgroundDestroyer()
+{
+    NS_RUNTIMEABORT("'Power-user' ctor is used exclusively");
+    return nsnull;
+}
+
+bool
+PluginInstanceParent::DeallocPPluginBackgroundDestroyer(
+    PPluginBackgroundDestroyerParent* aActor)
+{
+    delete aActor;
+    return true;
+}
+
+
 NPError
 PluginInstanceParent::NPP_SetWindow(const NPWindow* aWindow)
 {
     PLUGIN_LOG_DEBUG(("%s (aWindow=%p)", FULLFUNCTION, (void*) aWindow));
 
     NS_ENSURE_TRUE(aWindow, NPERR_GENERIC_ERROR);
 
     NPRemoteWindow window;
--- a/dom/plugins/PluginInstanceParent.h
+++ b/dom/plugins/PluginInstanceParent.h
@@ -272,23 +272,37 @@ public:
     AnswerPluginFocusChange(const bool& gotFocus);
 
 #if defined(OS_MACOSX)
     void Invalidate();
 #endif // definied(OS_MACOSX)
 
     nsresult AsyncSetWindow(NPWindow* window);
     nsresult GetSurface(gfxASurface** aSurface);
-    nsresult SetBackgroundUnknown() { return NS_OK; }
+    nsresult SetBackgroundUnknown();
     nsresult BeginUpdateBackground(const nsIntRect& aRect,
-                                   gfxContext** aCtx) { return NS_OK; }
+                                   gfxContext** aCtx);
     nsresult EndUpdateBackground(gfxContext* aCtx,
-                                 const nsIntRect& aRect) { return NS_OK; }
+                                 const nsIntRect& aRect);
 
 private:
+    // Create an appropriate platform surface for a background of size
+    // |aSize|.  Return true if successful.
+    bool CreateBackground(const nsIntSize& aSize);
+    void DestroyBackground();
+    SurfaceDescriptor BackgroundDescriptor() /*const*/;
+
+    NS_OVERRIDE
+    virtual PPluginBackgroundDestroyerParent*
+    AllocPPluginBackgroundDestroyer();
+
+    NS_OVERRIDE
+    virtual bool
+    DeallocPPluginBackgroundDestroyer(PPluginBackgroundDestroyerParent* aActor);
+
     // Quirks mode support for various plugin mime types
     enum PluginQuirks {
         // OSX: Don't use the refresh timer for plug-ins
         // using this quirk. These plug-in most have another
         // way to refresh the window.
         COREANIMATION_REFRESH_TIMER = 1,
     };
 
@@ -335,15 +349,28 @@ private:
     size_t             mShHeight;
     CGColorSpaceRef    mShColorSpace;
     int16_t            mDrawingModel;
     nsIOSurface       *mIOSurface;
 #endif // definied(OS_MACOSX)
 
     // ObjectFrame layer wrapper
     nsRefPtr<gfxASurface>    mFrontSurface;
+    // For windowless+transparent instances, this surface contains a
+    // "pretty recent" copy of the pixels under its <object> frame.
+    // On the plugin side, we use this surface to avoid doing alpha
+    // recovery when possible.  This surface is created and owned by
+    // the browser, but a "read-only" reference is sent to the plugin.
+    //
+    // We have explicitly chosen not to provide any guarantees about
+    // the consistency of the pixels in |mBackground|.  A plugin may
+    // be able to observe partial updates to the background.
+    nsRefPtr<gfxASurface>    mBackground;
+    // True when we just created a background and it needs to be
+    // shared to the plugin subprocess.
+    bool                     mNewBackground;
 };
 
 
 } // namespace plugins
 } // namespace mozilla
 
 #endif // ifndef dom_plugins_PluginInstanceParent_h
--- a/dom/plugins/PluginModuleChild.cpp
+++ b/dom/plugins/PluginModuleChild.cpp
@@ -1871,17 +1871,16 @@ PluginModuleChild::InitQuirksModes(const
 #ifdef OS_WIN
     // application/x-shockwave-flash
     NS_NAMED_LITERAL_CSTRING(flash, "application/x-shockwave-flash");
     if (FindInReadable(flash, aMimeType)) {
         mQuirks |= QUIRK_WINLESS_TRACKPOPUP_HOOK;
         mQuirks |= QUIRK_FLASH_THROTTLE_WMUSER_EVENTS; 
         mQuirks |= QUIRK_FLASH_HOOK_SETLONGPTR;
         mQuirks |= QUIRK_FLASH_HOOK_GETWINDOWINFO;
-        mQuirks |= QUIRK_FLASH_MASK_CLEARTYPE_SETTINGS;
     }
 #endif
 }
 
 bool
 PluginModuleChild::AnswerPPluginInstanceConstructor(PPluginInstanceChild* aActor,
                                                     const nsCString& aMimeType,
                                                     const uint16_t& aMode,
--- a/dom/plugins/PluginModuleChild.h
+++ b/dom/plugins/PluginModuleChild.h
@@ -241,19 +241,16 @@ public:
         // X11: Work around a bug in Flash up to 10.1 d51 at least, where
         // expose event top left coordinates within the plugin-rect and
         // not at the drawable origin are misinterpreted.
         QUIRK_FLASH_EXPOSE_COORD_TRANSLATION            = 1 << 4,
         // Win32: Catch get window info calls on the browser and tweak the
         // results so mouse input works when flash is displaying it's settings
         // window.
         QUIRK_FLASH_HOOK_GETWINDOWINFO                  = 1 << 5,
-        // Win: Flash trashes the alpha channel in our buffers when cleartype
-        // is enabled. Mask this setting so they don't know it's enabled.
-        QUIRK_FLASH_MASK_CLEARTYPE_SETTINGS             = 1 << 6,
     };
 
     int GetQuirks() { return mQuirks; }
     void AddQuirk(PluginQuirks quirk) {
       if (mQuirks == QUIRKS_NOT_INITIALIZED)
         mQuirks = 0;
       mQuirks |= quirk;
     }
--- a/dom/plugins/ipdl.mk
+++ b/dom/plugins/ipdl.mk
@@ -30,16 +30,17 @@
 # decision by deleting the provisions above and replace them with the notice
 # and other provisions required by the GPL or the LGPL. If you do not delete
 # the provisions above, a recipient may use your version of this file under
 # the terms of any one of the MPL, the GPL or the LGPL.
 #
 # ***** END LICENSE BLOCK *****
 
 IPDLSRCS = \
+  PPluginBackgroundDestroyer.ipdl \
   PPluginModule.ipdl \
   PPluginIdentifier.ipdl \
   PPluginInstance.ipdl \
   PPluginScriptableObject.ipdl \
   PBrowserStream.ipdl \
   PPluginStream.ipdl \
   PStreamNotify.ipdl \
   PPluginSurface.ipdl \
--- a/modules/plugin/test/testplugin/nptest_gtk2.cpp
+++ b/modules/plugin/test/testplugin/nptest_gtk2.cpp
@@ -376,23 +376,27 @@ pluginHandleEvent(InstanceData* instance
       gdk_drawable_set_colormap(gdkDrawable, gdkColormap);
       g_object_unref(G_OBJECT(gdkColormap));
     }
 
     const NPRect& clip = window.clipRect;
     if (expose.x < clip.left || expose.y < clip.top ||
         expose.x + expose.width > clip.right ||
         expose.y + expose.height > clip.bottom) {
-      g_warning("expose rectangle not in clip rectangle");
+      g_warning("expose rectangle (x=%d,y=%d,w=%d,h=%d) not in clip rectangle (l=%d,t=%d,r=%d,b=%d)",
+                expose.x, expose.y, expose.width, expose.height,
+                clip.left, clip.top, clip.right, clip.bottom);
       return 0;
     }
     if (expose.x < window.x || expose.y < window.y ||
         expose.x + expose.width > window.x + int32_t(window.width) ||
         expose.y + expose.height > window.y + int32_t(window.height)) {
-      g_warning("expose rectangle not in plugin rectangle");
+      g_warning("expose rectangle (x=%d,y=%d,w=%d,h=%d) not in plugin rectangle (x=%d,y=%d,w=%d,h=%d)",
+                expose.x, expose.y, expose.width, expose.height,
+                window.x, window.y, window.width, window.height);
       return 0;
     }      
 
     GdkRectangle invalidRect =
       { expose.x, expose.y, expose.width, expose.height };
     pluginDrawWindow(instanceData, gdkDrawable, invalidRect);
     g_object_unref(gdkDrawable);
     break;