merge. a=backout
authorJim Mathies <jmathies@mozilla.com>
Tue, 08 Feb 2011 23:06:32 -0600
changeset 62214 c8d984f57dbd00dc62df36cd5d7f1dd554e491b0
parent 62213 8ba0d8f0efb2b6e3a6a6b94c7b3984dcff36ba57 (current diff)
parent 62212 62601280d25dc5c963460b20d45b124ad5bab703 (diff)
child 62215 a74a5384f005c6e95483ea7f0e2ae779d0260564
push id1
push userroot
push dateTue, 10 Dec 2013 15:46:25 +0000
reviewersbackout
milestone2.0b12pre
merge. a=backout
security/manager/boot/public/nsISSLStatusProvider.idl
security/manager/ssl/public/nsISSLStatus.idl
security/manager/ssl/public/nsIX509Cert.idl
--- a/browser/locales/en-US/feedback/main.dtd
+++ b/browser/locales/en-US/feedback/main.dtd
@@ -24,8 +24,10 @@
 <!-- notification -->
 <!ENTITY testpilot.notification.close.tooltip            "Close">
 
 <!-- Firefox 4 beta version UI -->
 <!ENTITY testpilot.enable.label                        "Turn On User Studies">
 <!ENTITY testpilot.feedbackbutton.label                "Feedback">
 <!ENTITY testpilot.happy.label        "Firefox Made Me Happy Because…">
 <!ENTITY testpilot.sad.label          "Firefox Made Me Sad Because…">
+<!ENTITY testpilot.broken.label      "Report this website as broken…">
+<!ENTITY testpilot.idea.label        "Give us a suggestion…">
--- a/dom/base/ConsoleAPI.js
+++ b/dom/base/ConsoleAPI.js
@@ -57,34 +57,56 @@ ConsoleAPI.prototype = {
       id = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
                   .getInterface(Ci.nsIDOMWindowUtils)
                   .outerWindowID;
     } catch (ex) {
       Cu.reportError(ex);
     }
 
     let self = this;
-    return {
+    let chromeObject = {
       // window.console API
       log: function CA_log() {
         self.notifyObservers(id, "log", arguments);
       },
       info: function CA_info() {
         self.notifyObservers(id, "info", arguments);
       },
       warn: function CA_warn() {
         self.notifyObservers(id, "warn", arguments);
       },
       error: function CA_error() {
         self.notifyObservers(id, "error", arguments);
       },
-      // many flavors of console objects exist on the web, so calling
-      // unimplemented methods shouldn't be fatal. See bug 614350
-      __noSuchMethod__: function CA_nsm() {}
+      __exposedProps__: {
+        log: "r",
+        info: "r",
+        warn: "r",
+        error: "r"
+      }
     };
+
+    // We need to return an actual content object here, instead of a wrapped
+    // chrome object. This allows things like console.log.bind() to work.
+    let sandbox = Cu.Sandbox(aWindow);
+    let contentObject = Cu.evalInSandbox(
+        "(function(x) {\
+          var bind = Function.bind;\
+          var obj = {\
+            log: bind.call(x.log, x),\
+            info: bind.call(x.info, x),\
+            warn: bind.call(x.warn, x),\
+            error: bind.call(x.error, x),\
+            __noSuchMethod__: function() {}\
+          };\
+          Object.defineProperty(obj, '__mozillaConsole__', { value: true });\
+          return obj;\
+        })", sandbox)(chromeObject);
+
+      return contentObject;
   },
 
   /**
    * Notify all observers of any console API call
    **/
   notifyObservers: function CA_notifyObservers(aID, aLevel, aArguments) {
     if (!aID)
       return;
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,27 +2502,27 @@ 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) {
-        NS_ASSERTION(SharedDIBSurface::IsSharedDIBSurface(curSurface),
-                     "Expected (SharedDIB) image surface.");
+        if (!SharedDIBSurface::IsSharedDIBSurface(curSurface))
+            NS_RUNTIMEABORT("Expected SharedDIBSurface!");
 
         SharedDIBSurface* dibsurf = static_cast<SharedDIBSurface*>(curSurface.get());
         dc = dibsurf->GetHDC();
     }
     if (mWindow.window != dc) {
         mWindow.window = dc;
         needWindowUpdate = true;
     }
@@ -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();
     }
 
@@ -2755,97 +2724,205 @@ PluginInstanceChild::PaintRectToSurface(
         ctx->Fill();
     }
 }
 
 void
 PluginInstanceChild::PaintRectWithAlphaExtraction(const nsIntRect& aRect,
                                                   gfxASurface* aSurface)
 {
-    // Paint onto black image
-    bool needImageSurface = true;
-    nsRefPtr<gfxImageSurface> blackImage;
-    gfxIntSize clipSize(aRect.width, aRect.height);
-    gfxPoint deviceOffset(-aRect.x, -aRect.y);
-    // Try to re-use existing image surface, and avoid one copy
-    if (aSurface->GetType() == gfxASurface::SurfaceTypeImage) {
-        gfxImageSurface *surface = static_cast<gfxImageSurface*>(aSurface);
-        if (surface->Format() == gfxASurface::ImageFormatARGB32) {
-            needImageSurface = false;
-            blackImage = surface->GetSubimage(GfxFromNsRect(aRect));
+    NS_ABORT_IF_FALSE(aSurface->GetContentType() == gfxASurface::CONTENT_COLOR_ALPHA,
+                      "Refusing to pointlessly recover alpha");
+
+    nsIntRect rect(aRect);
+    // If |aSurface| can be used to paint and can have alpha values
+    // recovered directly to it, do that to save a tmp surface and
+    // copy.
+    bool useSurfaceSubimageForBlack = false;
+    if (gfxASurface::SurfaceTypeImage == aSurface->GetType()) {
+        gfxImageSurface* surfaceAsImage =
+            static_cast<gfxImageSurface*>(aSurface);
+        useSurfaceSubimageForBlack =
+            (surfaceAsImage->Format() == gfxASurface::ImageFormatARGB32);
+        // If we're going to use a subimage, nudge the rect so that we
+        // can use optimal alpha recovery.  If we're not using a
+        // subimage, the temporaries should automatically get
+        // fast-path alpha recovery so we don't need to do anything.
+        if (useSurfaceSubimageForBlack) {
+            rect =
+                gfxAlphaRecovery::AlignRectForSubimageRecovery(aRect,
+                                                               surfaceAsImage);
         }
     }
-    // otherwise create new helper surface
-    if (needImageSurface) {
-        blackImage = new gfxImageSurface(clipSize, gfxASurface::ImageFormatARGB32);
+
+    nsRefPtr<gfxImageSurface> whiteImage;
+    nsRefPtr<gfxImageSurface> blackImage;
+    gfxRect targetRect(rect.x, rect.y, rect.width, rect.height);
+    gfxIntSize targetSize(rect.width, rect.height);
+    gfxPoint deviceOffset = -targetRect.pos;
+
+    // We always use a temporary "white image"
+    whiteImage = new gfxImageSurface(targetSize, gfxASurface::ImageFormatRGB24);
+
+#ifdef XP_WIN
+    // On windows, we need an HDC and so can't paint directly to
+    // vanilla image surfaces.  Bifurcate this painting code so that
+    // we don't accidentally attempt that.
+    if (!SharedDIBSurface::IsSharedDIBSurface(aSurface))
+        NS_RUNTIMEABORT("Expected SharedDIBSurface!");
+
+    // Paint the plugin directly onto the target, with a white
+    // background and copy the result
+    PaintRectToSurface(rect, aSurface, gfxRGBA(1.0, 1.0, 1.0));
+    {
+        gfxRect copyRect(gfxPoint(0, 0), targetRect.size);
+        nsRefPtr<gfxContext> ctx = new gfxContext(whiteImage);
+        ctx->SetOperator(gfxContext::OPERATOR_SOURCE);
+        ctx->SetSource(aSurface, deviceOffset);
+        ctx->Rectangle(copyRect);
+        ctx->Fill();
     }
 
-    // Paint to black image
+    // Paint the plugin directly onto the target, with a black
+    // background
+    PaintRectToSurface(rect, aSurface, gfxRGBA(0.0, 0.0, 0.0));
+
+    // Don't copy the result, just extract a subimage so that we can
+    // recover alpha directly into the target
+    gfxImageSurface *image = static_cast<gfxImageSurface*>(aSurface);
+    blackImage = image->GetSubimage(targetRect);
+
+#else
+    // Paint onto white background
+    whiteImage->SetDeviceOffset(deviceOffset);
+    PaintRectToSurface(rect, whiteImage, gfxRGBA(1.0, 1.0, 1.0));
+
+    if (useSurfaceSubimageForBlack) {
+        gfxImageSurface *surface = static_cast<gfxImageSurface*>(aSurface);
+        blackImage = surface->GetSubimage(targetRect);
+    } else {
+        blackImage = new gfxImageSurface(targetSize,
+                                         gfxASurface::ImageFormatARGB32);
+    }
+
+    // Paint onto black background
     blackImage->SetDeviceOffset(deviceOffset);
-    PaintRectToSurface(aRect, blackImage, gfxRGBA(0.0, 0.0, 0.0));
-
-    // Paint onto white image
-    nsRefPtr<gfxImageSurface> whiteImage =
-        new gfxImageSurface(clipSize, gfxASurface::ImageFormatRGB24);
-
-    whiteImage->SetDeviceOffset(deviceOffset);
-    PaintRectToSurface(aRect, whiteImage, gfxRGBA(1.0, 1.0, 1.0));
-
-    // Extract Alpha from black and white image and store to black Image
-    gfxRect rect(aRect.x, aRect.y, aRect.width, aRect.height);
-    if (!gfxAlphaRecovery::RecoverAlpha(blackImage, whiteImage, nsnull)) {
+    PaintRectToSurface(rect, blackImage, gfxRGBA(0.0, 0.0, 0.0));
+#endif
+
+    NS_ABORT_IF_FALSE(whiteImage && blackImage, "Didn't paint enough!");
+
+    // Extract alpha from black and white image and store to black
+    // image
+    if (!gfxAlphaRecovery::RecoverAlpha(blackImage, whiteImage)) {
         return;
     }
 
-    if (needImageSurface) {
+    // If we had to use a temporary black surface, copy the pixels
+    // with alpha back to the target
+    if (!useSurfaceSubimageForBlack) {
         nsRefPtr<gfxContext> ctx = new gfxContext(aSurface);
         ctx->SetOperator(gfxContext::OPERATOR_SOURCE);
         ctx->SetSource(blackImage);
-        ctx->Rectangle(GfxFromNsRect(aRect));
+        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());
@@ -2859,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 {
@@ -2903,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);
@@ -2982,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;
@@ -3071,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,18 +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();
+    nsresult BeginUpdateBackground(const nsIntRect& aRect,
+                                   gfxContext** aCtx);
+    nsresult EndUpdateBackground(gfxContext* aCtx,
+                                 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,
     };
 
@@ -330,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/PluginLibrary.h
+++ b/dom/plugins/PluginLibrary.h
@@ -41,19 +41,21 @@
 
 #include "prlink.h"
 #include "npapi.h"
 #include "npfunctions.h"
 #include "nscore.h"
 #include "nsTArray.h"
 #include "nsPluginError.h"
 
+class gfxASurface;
+class gfxContext;
+class nsCString;
+struct nsIntRect;
 class nsNPAPIPlugin;
-class gfxASurface;
-class nsCString;
 
 namespace mozilla {
 
 class PluginLibrary
 {
 public:
   virtual ~PluginLibrary() { }
 
@@ -84,14 +86,24 @@ public:
 
   virtual nsresult NPP_ClearSiteData(const char* site, uint64_t flags,
                                      uint64_t maxAge) = 0;
   virtual nsresult NPP_GetSitesWithData(InfallibleTArray<nsCString>& aResult) = 0;
 
   virtual nsresult AsyncSetWindow(NPP instance, NPWindow* window) = 0;
   virtual nsresult GetSurface(NPP instance, gfxASurface** aSurface) = 0;
   virtual bool UseAsyncPainting() = 0;
+  /**
+   * The next three methods are the third leg in the trip to
+   * PluginInstanceParent.  They approximately follow the ReadbackSink
+   * API.
+   */
+  virtual nsresult SetBackgroundUnknown(NPP instance) = 0;
+  virtual nsresult BeginUpdateBackground(NPP instance,
+                                         const nsIntRect&, gfxContext**) = 0;
+  virtual nsresult EndUpdateBackground(NPP instance,
+                                       gfxContext*, const nsIntRect&) = 0;
 };
 
 
 } // namespace mozilla
 
 #endif  // ifndef mozilla_PluginLibrary_h
--- a/dom/plugins/PluginModuleChild.cpp
+++ b/dom/plugins/PluginModuleChild.cpp
@@ -132,24 +132,19 @@ PluginModuleChild::PluginModuleChild() :
 }
 
 PluginModuleChild::~PluginModuleChild()
 {
     NS_ASSERTION(gInstance == this, "Something terribly wrong here!");
     if (mLibrary) {
         PR_UnloadLibrary(mLibrary);
     }
-#ifdef MOZ_WIDGET_QT
-    nsQAppInstance::Release();
-    if (sGtkLib) {
-        PR_UnloadLibrary(sGtkLib);
-        sGtkLib = nsnull;
-        s_gtk_init = nsnull;
-    }
-#endif
+
+    DeinitGraphics();
+
     gInstance = nsnull;
 }
 
 // static
 PluginModuleChild*
 PluginModuleChild::current()
 {
     NS_ASSERTION(gInstance, "Null instance!");
@@ -554,16 +549,36 @@ PluginModuleChild::InitGraphics()
 #ifdef MOZ_X11
     // Do this after initializing GDK, or GDK will install its own handler.
     XRE_InstallX11ErrorHandler();
 #endif
 
     return true;
 }
 
+void
+PluginModuleChild::DeinitGraphics()
+{
+#ifdef MOZ_WIDGET_QT
+    nsQAppInstance::Release();
+    if (sGtkLib) {
+        PR_UnloadLibrary(sGtkLib);
+        sGtkLib = nsnull;
+        s_gtk_init = nsnull;
+    }
+#endif
+
+#if defined(MOZ_X11) && defined(NS_FREE_PERMANENT_DATA)
+    // We free some data off of XDisplay close hooks, ensure they're
+    // run.  Closing the display is pretty scary, so we only do it to
+    // silence leak checkers.
+    XCloseDisplay(DefaultXDisplay());
+#endif
+}
+
 bool
 PluginModuleChild::AnswerNP_Shutdown(NPError *rv)
 {
     AssertPluginThread();
 
     // the PluginModuleParent shuts down this process after this RPC
     // call pops off its stack
 
@@ -1856,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,31 +241,29 @@ 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;
     }
 
 private:
     void InitQuirksModes(const nsCString& aMimeType);
     bool InitGraphics();
+    void DeinitGraphics();
 #if defined(MOZ_WIDGET_GTK2)
     static gboolean DetectNestedEventLoop(gpointer data);
     static gboolean ProcessBrowserEvents(gpointer data);
 
     NS_OVERRIDE
     virtual void EnteredCxxStack();
     NS_OVERRIDE
     virtual void ExitedCxxStack();
--- a/dom/plugins/PluginModuleParent.cpp
+++ b/dom/plugins/PluginModuleParent.cpp
@@ -677,16 +677,50 @@ PluginModuleParent::GetSurface(NPP insta
 {
     PluginInstanceParent* i = InstCast(instance);
     if (!i)
         return NS_ERROR_FAILURE;
 
     return i->GetSurface(aSurface);
 }
 
+nsresult
+PluginModuleParent::SetBackgroundUnknown(NPP instance)
+{
+    PluginInstanceParent* i = InstCast(instance);
+    if (!i)
+        return NS_ERROR_FAILURE;
+
+    return i->SetBackgroundUnknown();
+}
+
+nsresult
+PluginModuleParent::BeginUpdateBackground(NPP instance,
+                                          const nsIntRect& aRect,
+                                          gfxContext** aCtx)
+{
+    PluginInstanceParent* i = InstCast(instance);
+    if (!i)
+        return NS_ERROR_FAILURE;
+
+    return i->BeginUpdateBackground(aRect, aCtx);
+}
+
+nsresult
+PluginModuleParent::EndUpdateBackground(NPP instance,
+                                        gfxContext* aCtx,
+                                        const nsIntRect& aRect)
+{
+    PluginInstanceParent* i = InstCast(instance);
+    if (!i)
+        return NS_ERROR_FAILURE;
+
+    return i->EndUpdateBackground(aCtx, aRect);
+}
+
 #if defined(XP_UNIX) && !defined(XP_MACOSX)
 nsresult
 PluginModuleParent::NP_Initialize(NPNetscapeFuncs* bFuncs, NPPluginFuncs* pFuncs, NPError* error)
 {
     PLUGIN_LOG_DEBUG_METHOD;
 
     mNPNIface = bFuncs;
 
--- a/dom/plugins/PluginModuleParent.h
+++ b/dom/plugins/PluginModuleParent.h
@@ -227,16 +227,26 @@ private:
                                 void *value);
     static void NPP_URLRedirectNotify(NPP instance, const char* url,
                                       int32_t status, void* notifyData);
 
     virtual bool HasRequiredFunctions();
     virtual nsresult AsyncSetWindow(NPP instance, NPWindow* window);
     virtual nsresult GetSurface(NPP instance, gfxASurface** aSurface);
     NS_OVERRIDE virtual bool UseAsyncPainting() { return true; }
+    NS_OVERRIDE
+    virtual nsresult SetBackgroundUnknown(NPP instance);
+    NS_OVERRIDE
+    virtual nsresult BeginUpdateBackground(NPP instance,
+                                           const nsIntRect& aRect,
+                                           gfxContext** aCtx);
+    NS_OVERRIDE
+    virtual nsresult EndUpdateBackground(NPP instance,
+                                         gfxContext* aCtx,
+                                         const nsIntRect& aRect);
 
 #if defined(XP_UNIX) && !defined(XP_MACOSX)
     virtual nsresult NP_Initialize(NPNetscapeFuncs* bFuncs, NPPluginFuncs* pFuncs, NPError* error);
 #else
     virtual nsresult NP_Initialize(NPNetscapeFuncs* bFuncs, NPError* error);
 #endif
     virtual nsresult NP_Shutdown(NPError* error);
     virtual nsresult NP_GetMIMEDescription(const char** mimeDesc);
--- 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/extensions/auth/nsAuthSSPI.cpp
+++ b/extensions/auth/nsAuthSSPI.cpp
@@ -16,16 +16,17 @@
  *
  * The Initial Developer of the Original Code is IBM Corporation.
  * Portions created by the Initial Developer are Copyright (C) 2004
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
  *   Darin Fisher <darin@meer.net>
  *   Jim Mathies <jmathies@mozilla.com>
+ *   Guillermo Robla Vicario <groblavicario@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
@@ -47,16 +48,17 @@
 //
 
 #include "nsAuthSSPI.h"
 #include "nsIServiceManager.h"
 #include "nsIDNSService.h"
 #include "nsIDNSRecord.h"
 #include "nsNetCID.h"
 #include "nsCOMPtr.h"
+#include "nsICryptoHash.h"
 
 #include <windows.h>
 
 #define SEC_SUCCESS(Status) ((Status) >= 0)
 
 #ifndef KERB_WRAP_NO_ENCRYPT
 #define KERB_WRAP_NO_ENCRYPT 0x80000001
 #endif
@@ -185,16 +187,18 @@ MakeSN(const char *principal, nsCString 
 }
 
 //-----------------------------------------------------------------------------
 
 nsAuthSSPI::nsAuthSSPI(pType package)
     : mServiceFlags(REQ_DEFAULT)
     , mMaxTokenLen(0)
     , mPackage(package)
+    , mCertDERData(nsnull)
+    , mCertDERLength(0)
 {
     memset(&mCred, 0, sizeof(mCred));
     memset(&mCtxt, 0, sizeof(mCtxt));
 }
 
 nsAuthSSPI::~nsAuthSSPI()
 {
     Reset();
@@ -207,16 +211,24 @@ nsAuthSSPI::~nsAuthSSPI()
 #endif
         memset(&mCred, 0, sizeof(mCred));
     }
 }
 
 void
 nsAuthSSPI::Reset()
 {
+    mIsFirst = PR_TRUE;
+
+    if (mCertDERData){
+        nsMemory::Free(mCertDERData);
+        mCertDERData = nsnull;
+        mCertDERLength = 0;   
+    }
+
     if (mCtxt.dwLower || mCtxt.dwUpper) {
         (sspi->DeleteSecurityContext)(&mCtxt);
         memset(&mCtxt, 0, sizeof(mCtxt));
     }
 }
 
 NS_IMPL_ISUPPORTS1(nsAuthSSPI, nsIAuthModule)
 
@@ -224,16 +236,20 @@ NS_IMETHODIMP
 nsAuthSSPI::Init(const char *serviceName,
                  PRUint32    serviceFlags,
                  const PRUnichar *domain,
                  const PRUnichar *username,
                  const PRUnichar *password)
 {
     LOG(("  nsAuthSSPI::Init\n"));
 
+    mIsFirst = PR_TRUE;
+    mCertDERLength = 0;
+    mCertDERData = nsnull;
+
     // The caller must supply a service name to be used. (For why we now require
     // a service name for NTLM, see bug 487872.)
     NS_ENSURE_TRUE(serviceName && *serviceName, NS_ERROR_INVALID_ARG);
 
     nsresult rv;
 
     // XXX lazy initialization like this assumes that we are single threaded
     if (!sspi) {
@@ -309,73 +325,186 @@ nsAuthSSPI::Init(const char *serviceName
                                            &mCred,
                                            &useBefore);
     if (rc != SEC_E_OK)
         return NS_ERROR_UNEXPECTED;
     LOG(("AcquireCredentialsHandle() succeeded.\n"));
     return NS_OK;
 }
 
+// The arguments inToken and inTokenLen are used to pass in the server
+// certificate (when available) in the first call of the function. The
+// second time these arguments hold an input token. 
 NS_IMETHODIMP
 nsAuthSSPI::GetNextToken(const void *inToken,
                          PRUint32    inTokenLen,
                          void      **outToken,
                          PRUint32   *outTokenLen)
 {
+    // String for end-point bindings.
+    const char end_point[] = "tls-server-end-point:"; 
+    const int end_point_length = sizeof(end_point) - 1;
+    const int hash_size = 32;  // Size of a SHA256 hash.
+    const int cbt_size = hash_size + end_point_length;
+	
     SECURITY_STATUS rc;
     TimeStamp ignored;
 
     DWORD ctxAttr, ctxReq = 0;
     CtxtHandle *ctxIn;
     SecBufferDesc ibd, obd;
-    SecBuffer ib, ob;
+    // Optional second input buffer for the CBT (Channel Binding Token)
+    SecBuffer ib[2], ob;
+    // Pointer to the block of memory that stores the CBT
+    char* sspi_cbt = nsnull;
+    SEC_CHANNEL_BINDINGS pendpoint_binding;
 
     LOG(("entering nsAuthSSPI::GetNextToken()\n"));
 
     if (!mCred.dwLower && !mCred.dwUpper) {
         LOG(("nsAuthSSPI::GetNextToken(), not initialized. exiting."));
         return NS_ERROR_NOT_INITIALIZED;
     }
 
     if (mServiceFlags & REQ_DELEGATE)
         ctxReq |= ISC_REQ_DELEGATE;
     if (mServiceFlags & REQ_MUTUAL_AUTH)
         ctxReq |= ISC_REQ_MUTUAL_AUTH;
 
     if (inToken) {
-        ib.BufferType = SECBUFFER_TOKEN;
-        ib.cbBuffer = inTokenLen;
-        ib.pvBuffer = (void *) inToken;
-        ibd.ulVersion = SECBUFFER_VERSION;
-        ibd.cBuffers = 1;
-        ibd.pBuffers = &ib;
-        ctxIn = &mCtxt;
-    }
-    else {
-        // If there is no input token, then we are starting a new
-        // authentication sequence.  If we have already initialized our
-        // security context, then we're in trouble because it means that the
-        // first sequence failed.  We need to bail or else we might end up in
-        // an infinite loop.
-        if (mCtxt.dwLower || mCtxt.dwUpper) {
+        if (mIsFirst) {
+            // First time if it comes with a token,
+            // the token represents the server certificate.
+            mIsFirst = PR_FALSE;
+            mCertDERLength = inTokenLen;
+            mCertDERData = nsMemory::Alloc(inTokenLen);
+            if (!mCertDERData)
+                return NS_ERROR_OUT_OF_MEMORY;
+            memcpy(mCertDERData, inToken, inTokenLen);
+
+            // We are starting a new authentication sequence.  
+            // If we have already initialized our
+            // security context, then we're in trouble because it means that the
+            // first sequence failed.  We need to bail or else we might end up in
+            // an infinite loop.
+            if (mCtxt.dwLower || mCtxt.dwUpper) {
+                LOG(("Cannot restart authentication sequence!"));
+                return NS_ERROR_UNEXPECTED;
+            }
+            ctxIn = nsnull;
+            // The certificate needs to be erased before being passed 
+            // to InitializeSecurityContextW().
+            inToken = nsnull;
+            inTokenLen = 0;
+        } else {
+            ibd.ulVersion = SECBUFFER_VERSION;
+            ibd.cBuffers = 0;
+            ibd.pBuffers = ib;
+            
+            // If we have stored a certificate, the Channel Binding Token
+            // needs to be generated and sent in the first input buffer.
+            if (mCertDERLength > 0) {
+                // First we create a proper Endpoint Binding structure. 
+                pendpoint_binding.dwInitiatorAddrType = 0;
+                pendpoint_binding.cbInitiatorLength = 0;
+                pendpoint_binding.dwInitiatorOffset = 0;
+                pendpoint_binding.dwAcceptorAddrType = 0;
+                pendpoint_binding.cbAcceptorLength = 0;
+                pendpoint_binding.dwAcceptorOffset = 0;
+                pendpoint_binding.cbApplicationDataLength = cbt_size;
+                pendpoint_binding.dwApplicationDataOffset = 
+                                            sizeof(SEC_CHANNEL_BINDINGS);
+
+                // Then add it to the array of sec buffers accordingly.
+                ib[ibd.cBuffers].BufferType = SECBUFFER_CHANNEL_BINDINGS;
+                ib[ibd.cBuffers].cbBuffer =
+                        pendpoint_binding.cbApplicationDataLength
+                        + pendpoint_binding.dwApplicationDataOffset;
+          
+                sspi_cbt = (char *) nsMemory::Alloc(ib[ibd.cBuffers].cbBuffer);
+                if (!sspi_cbt){
+                    return NS_ERROR_OUT_OF_MEMORY;
+                }
+
+                // Helper to write in the memory block that stores the CBT
+                char* sspi_cbt_ptr = sspi_cbt;
+          
+                ib[ibd.cBuffers].pvBuffer = sspi_cbt;
+                ibd.cBuffers++;
+
+                memcpy(sspi_cbt_ptr, &pendpoint_binding,
+                       pendpoint_binding.dwApplicationDataOffset);
+                sspi_cbt_ptr += pendpoint_binding.dwApplicationDataOffset;
+
+                memcpy(sspi_cbt_ptr, end_point, end_point_length);
+                sspi_cbt_ptr += end_point_length;
+          
+                // Start hashing. We are always doing SHA256, but depending
+                // on the certificate, a different alogirthm might be needed.
+                nsCAutoString hashString;
+
+                nsresult rv;
+                nsCOMPtr<nsICryptoHash> crypto;
+                crypto = do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv);
+                if (NS_SUCCEEDED(rv))
+                    rv = crypto->Init(nsICryptoHash::SHA256);
+                if (NS_SUCCEEDED(rv))
+                    rv = crypto->Update((unsigned char*)mCertDERData, mCertDERLength);
+                if (NS_SUCCEEDED(rv))
+                    rv = crypto->Finish(PR_FALSE, hashString);
+                if (NS_FAILED(rv)) {
+                    nsMemory::Free(mCertDERData);
+                    mCertDERData = nsnull;
+                    mCertDERLength = 0;
+                    nsMemory::Free(sspi_cbt);
+                    return rv;
+                }
+          
+                // Once the hash has been computed, we store it in memory right
+                // after the Endpoint structure and the "tls-server-end-point:"
+                // char array.
+                memcpy(sspi_cbt_ptr, hashString.get(), hash_size);
+          
+                // Free memory used to store the server certificate
+                nsMemory::Free(mCertDERData);
+                mCertDERData = nsnull;
+                mCertDERLength = 0;
+            } // End of CBT computation.
+
+            // We always need this SECBUFFER.
+            ib[ibd.cBuffers].BufferType = SECBUFFER_TOKEN;
+            ib[ibd.cBuffers].cbBuffer = inTokenLen;
+            ib[ibd.cBuffers].pvBuffer = (void *) inToken;
+            ibd.cBuffers++;
+            ctxIn = &mCtxt;
+        }
+    } else { // First time and without a token (no server certificate)
+        // We are starting a new authentication sequence.  If we have already 
+        // initialized our security context, then we're in trouble because it 
+        // means that the first sequence failed.  We need to bail or else we 
+        // might end up in an infinite loop.
+        if (mCtxt.dwLower || mCtxt.dwUpper || mCertDERData || mCertDERLength) {
             LOG(("Cannot restart authentication sequence!"));
             return NS_ERROR_UNEXPECTED;
         }
-
         ctxIn = NULL;
+        mIsFirst = PR_FALSE;
     }
 
     obd.ulVersion = SECBUFFER_VERSION;
     obd.cBuffers = 1;
     obd.pBuffers = &ob;
     ob.BufferType = SECBUFFER_TOKEN;
     ob.cbBuffer = mMaxTokenLen;
     ob.pvBuffer = nsMemory::Alloc(ob.cbBuffer);
-    if (!ob.pvBuffer)
+    if (!ob.pvBuffer){
+        if (sspi_cbt)
+            nsMemory::Free(sspi_cbt);
         return NS_ERROR_OUT_OF_MEMORY;
+    }
     memset(ob.pvBuffer, 0, ob.cbBuffer);
 
     NS_ConvertUTF8toUTF16 wSN(mServiceName);
     SEC_WCHAR *sn = (SEC_WCHAR *) wSN.get();
 
     rc = (sspi->InitializeSecurityContextW)(&mCred,
                                             ctxIn,
                                             sn,
@@ -391,17 +520,19 @@ nsAuthSSPI::GetNextToken(const void *inT
     if (rc == SEC_I_CONTINUE_NEEDED || rc == SEC_E_OK) {
 
 #ifdef PR_LOGGING
         if (rc == SEC_E_OK)
             LOG(("InitializeSecurityContext: succeeded.\n"));
         else
             LOG(("InitializeSecurityContext: continue.\n"));
 #endif
-
+        if (sspi_cbt)
+            nsMemory::Free(sspi_cbt);
+            
         if (!ob.cbBuffer) {
             nsMemory::Free(ob.pvBuffer);
             ob.pvBuffer = NULL;
         }
         *outToken = ob.pvBuffer;
         *outTokenLen = ob.cbBuffer;
 
         if (rc == SEC_E_OK)
--- a/extensions/auth/nsAuthSSPI.h
+++ b/extensions/auth/nsAuthSSPI.h
@@ -15,16 +15,17 @@
  * The Original Code is the SSPI NegotiateAuth Module.
  *
  * The Initial Developer of the Original Code is IBM Corporation.
  * Portions created by the Initial Developer are Copyright (C) 2004
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
  *   Darin Fisher <darin@meer.net>
+ *   Guillermo Robla Vicario <groblavicario@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
@@ -76,11 +77,14 @@ private:
     CtxtHandle   mCtxt;
     nsCString    mServiceName;
     PRUint32     mServiceFlags;
     PRUint32     mMaxTokenLen;
     pType        mPackage;
     nsString     mDomain;
     nsString     mUsername;
     nsString     mPassword;
+    PRBool       mIsFirst;	
+    void*        mCertDERData; 
+    PRUint32     mCertDERLength;
 };
 
 #endif /* nsAuthSSPI_h__ */
--- a/gfx/ipc/SharedDIBWin.cpp
+++ b/gfx/ipc/SharedDIBWin.cpp
@@ -32,23 +32,27 @@
  * 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 "SharedDIBWin.h"
+#include "gfxAlphaRecovery.h"
 #include "nsMathUtils.h"
 #include "nsDebug.h"
 
 namespace mozilla {
 namespace gfx {
 
 static const PRUint32 kBytesPerPixel = 4;
+static const PRUint32 kByteAlign = 1 << gfxAlphaRecovery::GoodAlignmentLog2();
+static const PRUint32 kHeaderBytes =
+  (sizeof(BITMAPV4HEADER) + kByteAlign - 1) & ~(kByteAlign - 1);
 
 SharedDIBWin::SharedDIBWin() :
     mSharedHdc(nsnull)
   , mSharedBmp(nsnull)
   , mOldObj(nsnull)
 {
 }
 
@@ -135,33 +139,33 @@ SharedDIBWin::SetupBitmapHeader(PRUint32
   aHeader->bV4V4Compression = BI_BITFIELDS;
   aHeader->bV4RedMask       = 0x00FF0000;
   aHeader->bV4GreenMask     = 0x0000FF00;
   aHeader->bV4BlueMask      = 0x000000FF;
 
   if (aTransparent)
     aHeader->bV4AlphaMask     = 0xFF000000;
 
-  return (sizeof(BITMAPV4HEADER) + (-aHeader->bV4Height * aHeader->bV4Width * kBytesPerPixel));
+  return (kHeaderBytes + (-aHeader->bV4Height * aHeader->bV4Width * kBytesPerPixel));
 }
 
 nsresult
 SharedDIBWin::SetupSurface(HDC aHdc, BITMAPV4HEADER *aHdr)
 {
   mSharedHdc = ::CreateCompatibleDC(aHdc);
 
   if (!mSharedHdc)
     return NS_ERROR_FAILURE;
 
   mSharedBmp = ::CreateDIBSection(mSharedHdc,
                                   (BITMAPINFO*)aHdr,
                                   DIB_RGB_COLORS,
                                   &mBitmapBits,
                                   mShMem->handle(),
-                                  (unsigned long)sizeof(BITMAPV4HEADER));
+                                  kHeaderBytes);
   if (!mSharedBmp)
     return NS_ERROR_FAILURE;
 
   mOldObj = SelectObject(mSharedHdc, mSharedBmp);
 
   return NS_OK;
 }
 
--- a/gfx/layers/Layers.cpp
+++ b/gfx/layers/Layers.cpp
@@ -40,16 +40,17 @@
 
 #ifdef MOZ_IPC
 # include "mozilla/layers/ShadowLayers.h"
 #endif  // MOZ_IPC
 
 #include "ImageLayers.h"
 #include "Layers.h"
 #include "gfxPlatform.h"
+#include "ReadbackLayer.h"
 
 using namespace mozilla::layers;
 
 typedef FrameMetrics::ViewID ViewID;
 const ViewID FrameMetrics::NULL_SCROLL_ID = 0;
 const ViewID FrameMetrics::ROOT_SCROLL_ID = 1;
 const ViewID FrameMetrics::START_SCROLL_ID = 2;
 
@@ -61,16 +62,25 @@ FILEOrDefault(FILE* aFile)
 }
 #endif // MOZ_LAYERS_HAVE_LOG
 
 namespace {
 
 // XXX pretty general utilities, could centralize
 
 nsACString&
+AppendToString(nsACString& s, const void* p,
+               const char* pfx="", const char* sfx="")
+{
+  s += pfx;
+  s += nsPrintfCString(64, "%p", p);
+  return s += sfx;
+}
+
+nsACString&
 AppendToString(nsACString& s, const gfxPattern::GraphicsFilter& f,
                const char* pfx="", const char* sfx="")
 {
   s += pfx;
   switch (f) {
   case gfxPattern::FILTER_FAST:      s += "fast"; break;
   case gfxPattern::FILTER_GOOD:      s += "good"; break;
   case gfxPattern::FILTER_BEST:      s += "best"; break;
@@ -386,16 +396,40 @@ ContainerLayer::DefaultComputeEffectiveT
 void
 ContainerLayer::ComputeEffectiveTransformsForChildren(const gfx3DMatrix& aTransformToSurface)
 {
   for (Layer* l = mFirstChild; l; l = l->GetNextSibling()) {
     l->ComputeEffectiveTransforms(aTransformToSurface);
   }
 }
 
+void
+ContainerLayer::DidRemoveChild(Layer* aLayer)
+{
+  ThebesLayer* tl = aLayer->AsThebesLayer();
+  if (tl && tl->UsedForReadback()) {
+    for (Layer* l = mFirstChild; l; l = l->GetNextSibling()) {
+      if (l->GetType() == TYPE_READBACK) {
+        static_cast<ReadbackLayer*>(l)->NotifyThebesLayerRemoved(tl);
+      }
+    }
+  }
+  if (aLayer->GetType() == TYPE_READBACK) {
+    static_cast<ReadbackLayer*>(aLayer)->NotifyRemoved();
+  }
+}
+
+void
+ContainerLayer::DidInsertChild(Layer* aLayer)
+{
+  if (aLayer->GetType() == TYPE_READBACK) {
+    mMayHaveReadbackChild = PR_TRUE;
+  }
+}
+
 #ifdef MOZ_LAYERS_HAVE_LOG
 
 static nsACString& PrintInfo(nsACString& aTo, ShadowLayer* aShadowLayer);
 
 void
 Layer::Dump(FILE* aFile, const char* aPrefix)
 {
   DumpSelf(aFile, aPrefix);
@@ -529,16 +563,32 @@ ImageLayer::PrintInfo(nsACString& aTo, c
 {
   Layer::PrintInfo(aTo, aPrefix);
   if (mFilter != gfxPattern::FILTER_GOOD) {
     AppendToString(aTo, mFilter, " [filter=", "]");
   }
   return aTo;
 }
 
+nsACString&
+ReadbackLayer::PrintInfo(nsACString& aTo, const char* aPrefix)
+{
+  Layer::PrintInfo(aTo, aPrefix);
+  AppendToString(aTo, mSize, " [size=", "]");
+  if (mBackgroundLayer) {
+    AppendToString(aTo, mBackgroundLayer, " [backgroundLayer=", "]");
+    AppendToString(aTo, mBackgroundLayerOffset, " [backgroundOffset=", "]");
+  } else if (mBackgroundColor.a == 1.0) {
+    AppendToString(aTo, mBackgroundColor, " [backgroundColor=", "]");
+  } else {
+    aTo += " [nobackground]";
+  }
+  return aTo;
+}
+
 //--------------------------------------------------
 // LayerManager
 
 void
 LayerManager::Dump(FILE* aFile, const char* aPrefix)
 {
   FILE* file = FILEOrDefault(aFile);
 
@@ -660,16 +710,20 @@ ColorLayer::PrintInfo(nsACString& aTo, c
 nsACString&
 CanvasLayer::PrintInfo(nsACString& aTo, const char* aPrefix)
 { return aTo; }
 
 nsACString&
 ImageLayer::PrintInfo(nsACString& aTo, const char* aPrefix)
 { return aTo; }
 
+nsACString&
+ReadbackLayer::PrintInfo(nsACString& aTo, const char* aPrefix)
+{ return aTo; }
+
 void LayerManager::Dump(FILE* aFile, const char* aPrefix) {}
 void LayerManager::DumpSelf(FILE* aFile, const char* aPrefix) {}
 void LayerManager::Log(const char* aPrefix) {}
 void LayerManager::LogSelf(const char* aPrefix) {}
 
 nsACString&
 LayerManager::PrintInfo(nsACString& aTo, const char* aPrefix)
 { return aTo; }
--- a/gfx/layers/Layers.h
+++ b/gfx/layers/Layers.h
@@ -73,16 +73,18 @@ namespace layers {
 class Layer;
 class ThebesLayer;
 class ContainerLayer;
 class ImageLayer;
 class ColorLayer;
 class ImageContainer;
 class CanvasLayer;
 class ShadowLayer;
+class ReadbackLayer;
+class ReadbackProcessor;
 class SpecificLayerAttributes;
 
 /**
  * The viewport and displayport metrics for the painted frame at the
  * time of a layer-tree transaction.  These metrics are especially
  * useful for shadow layers, because the metrics values are updated
  * atomically with new pixels.
  */
@@ -389,16 +391,21 @@ public:
    * Create a ColorLayer for this manager's layer tree.
    */
   virtual already_AddRefed<ColorLayer> CreateColorLayer() = 0;
   /**
    * CONSTRUCTION PHASE ONLY
    * Create a CanvasLayer for this manager's layer tree.
    */
   virtual already_AddRefed<CanvasLayer> CreateCanvasLayer() = 0;
+  /**
+   * CONSTRUCTION PHASE ONLY
+   * Create a ReadbackLayer for this manager's layer tree.
+   */
+  virtual already_AddRefed<ReadbackLayer> CreateReadbackLayer() { return nsnull; }
 
   /**
    * Can be called anytime
    */
   virtual already_AddRefed<ImageContainer> CreateImageContainer() = 0;
 
   /**
    * Type of layer manager his is. This is to be used sparsely in order to
@@ -498,23 +505,25 @@ class ThebesLayer;
 /**
  * A Layer represents anything that can be rendered onto a destination
  * surface.
  */
 class THEBES_API Layer {
   NS_INLINE_DECL_REFCOUNTING(Layer)  
 
 public:
+  // Keep these in alphabetical order
   enum LayerType {
-    TYPE_THEBES,
+    TYPE_CANVAS,
+    TYPE_COLOR,
     TYPE_CONTAINER,
     TYPE_IMAGE,
-    TYPE_COLOR,
-    TYPE_CANVAS,
-    TYPE_SHADOW
+    TYPE_READBACK,
+    TYPE_SHADOW,
+    TYPE_THEBES
   };
 
   virtual ~Layer() {}
 
   /**
    * Returns the LayerManager this Layer belongs to. Note that the layer
    * manager might be in a destroyed state, at which point it's only
    * valid to set/get user data from it.
@@ -923,22 +932,26 @@ public:
 
   virtual void ComputeEffectiveTransforms(const gfx3DMatrix& aTransformToSurface)
   {
     // The default implementation just snaps 0,0 to pixels.
     gfx3DMatrix idealTransform = GetLocalTransform()*aTransformToSurface;
     mEffectiveTransform = SnapTransform(idealTransform, gfxRect(0, 0, 0, 0), nsnull);
   }
 
+  bool UsedForReadback() { return mUsedForReadback; }
+  void SetUsedForReadback(bool aUsed) { mUsedForReadback = aUsed; }
+
 protected:
   ThebesLayer(LayerManager* aManager, void* aImplData)
     : Layer(aManager, aImplData)
     , mValidRegion()
     , mXResolution(1.0)
     , mYResolution(1.0)
+    , mUsedForReadback(false)
   {
     mContentFlags = 0; // Clear NO_TEXT, NO_TEXT_OVER_TRANSPARENT
   }
 
   virtual nsACString& PrintInfo(nsACString& aTo, const char* aPrefix);
 
   nsIntRegion mValidRegion;
   // Resolution values tell this to paint its content scaled by
@@ -951,16 +964,21 @@ protected:
   // re-painted regions at all.  It only affects how scalable thebes
   // content is rasterized to device pixels.
   //
   // Setting the resolution isn't part of the public ThebesLayer API
   // because it's backend-specific, and it doesn't necessarily make
   // sense for all backends to fully support it.
   float mXResolution;
   float mYResolution;
+  /**
+   * Set when this ThebesLayer is participating in readback, i.e. some
+   * ReadbackLayer (may) be getting its background from this layer.
+   */
+  bool mUsedForReadback;
 };
 
 /**
  * A Layer which other layers render into. It holds references to its
  * children.
  */
 class THEBES_API ContainerLayer : public Layer {
 public:
@@ -1020,22 +1038,28 @@ public:
 
   /**
    * Returns true if this container supports children with component alpha.
    * Should only be called while painting a child of this layer.
    */
   PRBool SupportsComponentAlphaChildren() { return mSupportsComponentAlphaChildren; }
 
 protected:
+  friend class ReadbackProcessor;
+
+  void DidInsertChild(Layer* aLayer);
+  void DidRemoveChild(Layer* aLayer);
+
   ContainerLayer(LayerManager* aManager, void* aImplData)
     : Layer(aManager, aImplData),
       mFirstChild(nsnull),
       mLastChild(nsnull),
       mUseIntermediateSurface(PR_FALSE),
-      mSupportsComponentAlphaChildren(PR_FALSE)
+      mSupportsComponentAlphaChildren(PR_FALSE),
+      mMayHaveReadbackChild(PR_FALSE)
   {
     mContentFlags = 0; // Clear NO_TEXT, NO_TEXT_OVER_TRANSPARENT
   }
 
   /**
    * A default implementation of ComputeEffectiveTransforms for use by OpenGL
    * and D3D.
    */
@@ -1048,16 +1072,17 @@ protected:
 
   virtual nsACString& PrintInfo(nsACString& aTo, const char* aPrefix);
 
   Layer* mFirstChild;
   Layer* mLastChild;
   FrameMetrics mFrameMetrics;
   PRPackedBool mUseIntermediateSurface;
   PRPackedBool mSupportsComponentAlphaChildren;
+  PRPackedBool mMayHaveReadbackChild;
 };
 
 /**
  * A Layer which just renders a solid color in its visible region. It actually
  * can fill any area that contains the visible region, so if you need to
  * restrict the area filled, set a clip region on this layer.
  */
 class THEBES_API ColorLayer : public Layer {
--- a/gfx/layers/Makefile.in
+++ b/gfx/layers/Makefile.in
@@ -63,22 +63,24 @@ DEFINES += -DD3D_DEBUG_INFO
 endif
 
 EXPORTS = \
         BasicLayers.h \
         ImageLayers.h \
         Layers.h \
         LayerManagerOGL.h \
         LayerManagerOGLProgram.h \
+        ReadbackLayer.h \
         $(NULL)
 
 CPPSRCS = \
-	Layers.cpp \
         BasicImages.cpp \
         BasicLayers.cpp \
+        Layers.cpp \
+        ReadbackProcessor.cpp \
         ThebesLayerBuffer.cpp \
         CanvasLayerOGL.cpp \
         ColorLayerOGL.cpp \
         ContainerLayerOGL.cpp \
         ImageLayerOGL.cpp \
         LayerManagerOGL.cpp \
         ThebesLayerOGL.cpp \
         $(NULL)
@@ -99,25 +101,27 @@ CPPSRCS += \
         CanvasLayerD3D9.cpp \
         DeviceManagerD3D9.cpp \
         Nv3DVUtils.cpp \
         $(NULL)
 endif
 ifdef MOZ_ENABLE_D3D10_LAYER
 EXPORTS += \
         LayerManagerD3D10.h \
+        ReadbackManagerD3D10.h \
         $(NULL)
 
 CPPSRCS += \
         LayerManagerD3D10.cpp \
         ThebesLayerD3D10.cpp \
         ContainerLayerD3D10.cpp \
         ImageLayerD3D10.cpp \
         ColorLayerD3D10.cpp \
         CanvasLayerD3D10.cpp \
+        ReadbackManagerD3D10.cpp \
         $(NULL)
 endif
 endif
 
 ifdef MOZ_IPC #{
 EXPORTS_NAMESPACES = IPC mozilla/layers
 EXPORTS_IPC = ShadowLayerUtils.h
 EXPORTS_mozilla/layers =\
new file mode 100644
--- /dev/null
+++ b/gfx/layers/ReadbackLayer.h
@@ -0,0 +1,220 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * ***** 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 Corporation code.
+ *
+ * The Initial Developer of the Original Code is Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Robert O'Callahan <robert@ocallahan.org>
+ *
+ * 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 GFX_READBACKLAYER_H
+#define GFX_READBACKLAYER_H
+
+#include "Layers.h"
+
+namespace mozilla {
+namespace layers {
+
+class ReadbackProcessor;
+
+/**
+ * A ReadbackSink receives a stream of updates to a rectangle of pixels.
+ * These update callbacks are always called on the main thread, either during
+ * EndTransaction or from the event loop.
+ */
+class THEBES_API ReadbackSink {
+public:
+  ReadbackSink() {}
+  virtual ~ReadbackSink() {}
+
+  /**
+   * Sends an update to indicate that the background is currently unknown.
+   */
+  virtual void SetUnknown(PRUint64 aSequenceNumber) = 0;
+  /**
+   * Called by the layer system to indicate that the contents of part of
+   * the readback area are changing.
+   * @param aRect is the rectangle of content that is being updated,
+   * in the coordinate system of the ReadbackLayer.
+   * @param aSequenceNumber updates issued out of order should be ignored.
+   * Only use updates whose sequence counter is greater than all other updates
+   * seen so far. Return null when a non-fresh sequence value is given.
+   * @return a context into which the update should be drawn. This should be
+   * set up to clip to aRect. Zero should never be passed as a sequence number.
+   * If this returns null, EndUpdate should NOT be called. If it returns
+   * non-null, EndUpdate must be called.
+   *
+   * We don't support partially unknown backgrounds. Therefore, the
+   * first BeginUpdate after a SetUnknown will have the complete background.
+   */
+  virtual already_AddRefed<gfxContext>
+      BeginUpdate(const nsIntRect& aRect, PRUint64 aSequenceNumber) = 0;
+  /**
+   * EndUpdate must be called immediately after BeginUpdate, without returning
+   * to the event loop.
+   * @param aContext the context returned by BeginUpdate
+   * Implicitly Restore()s the state of aContext.
+   */
+  virtual void EndUpdate(gfxContext* aContext, const nsIntRect& aRect) = 0;
+};
+
+/**
+ * A ReadbackLayer never renders anything. It enables clients to extract
+ * the rendered contents of the layer tree below the ReadbackLayer.
+ * The rendered contents are delivered asynchronously via calls to a
+ * ReadbackSink object supplied by the client.
+ *
+ * This is a "best effort" API; it is possible for the layer system to tell
+ * the ReadbackSink that the contents of the readback area are unknown.
+ *
+ * This API exists to work around the limitations of transparent windowless
+ * plugin rendering APIs. It should not be used for anything else.
+ */
+class THEBES_API ReadbackLayer : public Layer {
+public:
+  MOZ_LAYER_DECL_NAME("ReadbackLayer", TYPE_READBACK)
+
+  virtual void ComputeEffectiveTransforms(const gfx3DMatrix& aTransformToSurface)
+  {
+    // Snap our local transform first, and snap the inherited transform as well.
+    // This makes our snapping equivalent to what would happen if our content
+    // was drawn into a ThebesLayer (gfxContext would snap using the local
+    // transform, then we'd snap again when compositing the ThebesLayer).
+    mEffectiveTransform =
+        SnapTransform(GetLocalTransform(), gfxRect(0, 0, mSize.width, mSize.height),
+                      nsnull)*
+        SnapTransform(aTransformToSurface, gfxRect(0, 0, 0, 0), nsnull);
+  }
+
+  /**
+   * CONSTRUCTION PHASE ONLY
+   * Set the callback object to which readback updates will be delivered.
+   * This also resets the "needed rectangle" so that on the next layer tree
+   * transaction we will try to deliver the full contents of the readback
+   * area to the sink.
+   * This layer takes ownership of the sink. It will be deleted when the
+   * layer is destroyed or when a new sink is set.
+   * Initially the contents of the readback area are completely unknown.
+   */
+  void SetSink(ReadbackSink* aSink)
+  {
+    SetUnknown();
+    mSink = aSink;
+  }
+  ReadbackSink* GetSink() { return mSink; }
+
+  /**
+   * CONSTRUCTION PHASE ONLY
+   * Set the size of content that should be read back. The readback area
+   * has its top-left at 0,0 and has size aSize.
+   * Can only be called while the sink is null!
+   */
+  void SetSize(const nsIntSize& aSize)
+  {
+    NS_ASSERTION(!mSink, "Should have no sink while changing size!");
+    mSize = aSize;
+  }
+  const nsIntSize& GetSize() { return mSize; }
+  nsIntRect GetRect() { return nsIntRect(nsIntPoint(0, 0), mSize); }
+
+  PRBool IsBackgroundKnown()
+  {
+    return mBackgroundLayer || mBackgroundColor.a == 1.0;
+  }
+
+  void NotifyRemoved() {
+    SetUnknown();
+    mSink = nsnull;
+  }
+
+  void NotifyThebesLayerRemoved(ThebesLayer* aLayer)
+  {
+    if (mBackgroundLayer == aLayer) {
+      mBackgroundLayer = nsnull;
+    }
+  }
+
+  const nsIntPoint& GetBackgroundLayerOffset() { return mBackgroundLayerOffset; }
+
+  PRUint64 AllocateSequenceNumber() { return ++mSequenceCounter; }
+
+  void SetUnknown()
+  {
+    if (IsBackgroundKnown()) {
+      if (mSink) {
+        mSink->SetUnknown(AllocateSequenceNumber());
+      }
+      mBackgroundLayer = nsnull;
+      mBackgroundColor = gfxRGBA(0,0,0,0);
+    }
+  }
+
+protected:
+  friend class ReadbackProcessor;
+
+  ReadbackLayer(LayerManager* aManager, void* aImplData) :
+    Layer(aManager, aImplData),
+    mSequenceCounter(0),
+    mSize(0,0),
+    mBackgroundLayer(nsnull),
+    mBackgroundLayerOffset(0, 0),
+    mBackgroundColor(gfxRGBA(0,0,0,0))
+  {}
+
+  // Print interesting information about this into aTo.  Internally
+  // used to implement Dump*() and Log*().
+  virtual nsACString& PrintInfo(nsACString& aTo, const char* aPrefix);
+
+  PRUint64 mSequenceCounter;
+  nsAutoPtr<ReadbackSink> mSink;
+  nsIntSize mSize;
+
+  // This can refer to any (earlier) sibling ThebesLayer. That ThebesLayer
+  // must have mUsedForReadback set on it. If the ThebesLayer is removed
+  // for the container, this will be set to null by NotifyThebesLayerRemoved.
+  // This ThebesLayer contains the contents which have previously been reported
+  // to mSink. The ThebesLayer had only an integer translation transform,
+  // and it covered the entire readback area. This layer also had only an
+  // integer translation transform.
+  ThebesLayer* mBackgroundLayer;
+  // When mBackgroundLayer is non-null, this is the offset to add to
+  // convert from the coordinates of mBackgroundLayer to the coordinates
+  // of this layer.
+  nsIntPoint   mBackgroundLayerOffset;
+  // When mBackgroundColor is opaque, this is the color of the ColorLayer
+  // that contained the contents we reported to mSink, which covered the
+  // entire readback area.
+  gfxRGBA      mBackgroundColor;
+};
+
+}
+}
+#endif /* GFX_READBACKLAYER_H */
new file mode 100644
--- /dev/null
+++ b/gfx/layers/ReadbackProcessor.cpp
@@ -0,0 +1,198 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * ***** 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 Corporation code.
+ *
+ * The Initial Developer of the Original Code is Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Robert O'Callahan <robert@ocallahan.org>
+ *
+ * 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 "ReadbackProcessor.h"
+
+namespace mozilla {
+namespace layers {
+
+void
+ReadbackProcessor::BuildUpdates(ContainerLayer* aContainer)
+{
+  NS_ASSERTION(mAllUpdates.IsEmpty(), "Some updates not processed?");
+
+  if (!aContainer->mMayHaveReadbackChild)
+    return;
+
+  aContainer->mMayHaveReadbackChild = PR_FALSE;
+  // go backwards so the updates read from earlier layers are later in the
+  // array.
+  for (Layer* l = aContainer->GetLastChild(); l; l = l->GetPrevSibling()) {
+    if (l->GetType() == Layer::TYPE_READBACK) {
+      aContainer->mMayHaveReadbackChild = PR_TRUE;
+      BuildUpdatesForLayer(static_cast<ReadbackLayer*>(l));
+    }
+  }
+}
+
+static Layer*
+FindBackgroundLayer(ReadbackLayer* aLayer, nsIntPoint* aOffset)
+{
+  gfxMatrix transform;
+  if (!aLayer->GetTransform().Is2D(&transform) ||
+      transform.HasNonIntegerTranslation())
+    return nsnull;
+  nsIntPoint transformOffset(PRInt32(transform.x0), PRInt32(transform.y0));
+
+  for (Layer* l = aLayer->GetPrevSibling(); l; l = l->GetPrevSibling()) {
+    gfxMatrix backgroundTransform;
+    if (!l->GetTransform().Is2D(&backgroundTransform) ||
+        backgroundTransform.HasNonIntegerTranslation())
+      return nsnull;
+
+    nsIntPoint backgroundOffset(PRInt32(backgroundTransform.x0), PRInt32(backgroundTransform.y0));
+    nsIntRect rectInBackground(transformOffset - backgroundOffset, aLayer->GetSize());
+    const nsIntRegion& visibleRegion = l->GetEffectiveVisibleRegion();
+    if (!visibleRegion.Intersects(rectInBackground))
+      continue;
+    // Since l is present in the background, from here on we either choose l
+    // or nothing.
+    if (!visibleRegion.Contains(rectInBackground))
+      return nsnull;
+
+    if (l->GetEffectiveOpacity() != 1.0 ||
+        !(l->GetContentFlags() & Layer::CONTENT_OPAQUE))
+      return nsnull;
+
+    // cliprects are post-transform
+    const nsIntRect* clipRect = l->GetEffectiveClipRect();
+    if (clipRect && !clipRect->Contains(nsIntRect(transformOffset, aLayer->GetSize())))
+      return nsnull;
+
+    Layer::LayerType type = l->GetType();
+    if (type != Layer::TYPE_COLOR && type != Layer::TYPE_THEBES)
+      return nsnull;
+
+    *aOffset = backgroundOffset - transformOffset;
+    return l;
+  }
+
+  return nsnull;
+}
+
+void
+ReadbackProcessor::BuildUpdatesForLayer(ReadbackLayer* aLayer)
+{
+  if (!aLayer->mSink)
+    return;
+
+  nsIntPoint offset;
+  Layer* newBackground = FindBackgroundLayer(aLayer, &offset);
+  if (!newBackground) {
+    aLayer->SetUnknown();
+    return;
+  }
+
+  if (newBackground->GetType() == Layer::TYPE_COLOR) {
+    ColorLayer* colorLayer = static_cast<ColorLayer*>(newBackground);
+    if (aLayer->mBackgroundColor != colorLayer->GetColor()) {
+      aLayer->mBackgroundLayer = nsnull;
+      aLayer->mBackgroundColor = colorLayer->GetColor();
+      NS_ASSERTION(aLayer->mBackgroundColor.a == 1.0,
+                   "Color layer said it was opaque!");
+      nsRefPtr<gfxContext> ctx =
+          aLayer->mSink->BeginUpdate(aLayer->GetRect(),
+                                     aLayer->AllocateSequenceNumber());
+      if (ctx) {
+        ctx->SetColor(aLayer->mBackgroundColor);
+        nsIntSize size = aLayer->GetSize();
+        ctx->Rectangle(gfxRect(0, 0, size.width, size.height));
+        ctx->Fill();
+        aLayer->mSink->EndUpdate(ctx, aLayer->GetRect());
+      }
+    }
+  } else {
+    NS_ASSERTION(newBackground->AsThebesLayer(), "Must be ThebesLayer");
+    ThebesLayer* thebesLayer = static_cast<ThebesLayer*>(newBackground);
+    // updateRect is relative to the ThebesLayer
+    nsIntRect updateRect = aLayer->GetRect() - offset;
+    if (thebesLayer != aLayer->mBackgroundLayer ||
+        offset != aLayer->mBackgroundLayerOffset) {
+      aLayer->mBackgroundLayer = thebesLayer;
+      aLayer->mBackgroundLayerOffset = offset;
+      aLayer->mBackgroundColor = gfxRGBA(0,0,0,0);
+      thebesLayer->SetUsedForReadback(true);
+    } else {
+      nsIntRegion invalid;
+      invalid.Sub(updateRect, thebesLayer->GetValidRegion());
+      updateRect = invalid.GetBounds();
+    }
+
+    Update update = { aLayer, updateRect, aLayer->AllocateSequenceNumber() };
+    mAllUpdates.AppendElement(update);
+  }
+}
+
+void
+ReadbackProcessor::GetThebesLayerUpdates(ThebesLayer* aLayer,
+                                         nsTArray<Update>* aUpdates,
+                                         nsIntRegion* aUpdateRegion)
+{
+  // All ThebesLayers used for readback are in mAllUpdates (some possibly
+  // with an empty update rect).
+  aLayer->SetUsedForReadback(false);
+  if (aUpdateRegion) {
+    aUpdateRegion->SetEmpty();
+  }
+  for (PRUint32 i = mAllUpdates.Length(); i > 0; --i) {
+    const Update& update = mAllUpdates[i - 1];
+    if (update.mLayer->mBackgroundLayer == aLayer) {
+      aLayer->SetUsedForReadback(true);
+      // Don't bother asking for updates if we have an empty update rect.
+      if (!update.mUpdateRect.IsEmpty()) {
+        aUpdates->AppendElement(update);
+        if (aUpdateRegion) {
+          aUpdateRegion->Or(*aUpdateRegion, update.mUpdateRect);
+        }
+      }
+      mAllUpdates.RemoveElementAt(i - 1);
+    }
+  }
+}
+
+ReadbackProcessor::~ReadbackProcessor()
+{
+  for (PRUint32 i = mAllUpdates.Length(); i > 0; --i) {
+    const Update& update = mAllUpdates[i - 1];
+    // Unprocessed update. Notify the readback sink that this content is
+    // unknown.
+    update.mLayer->SetUnknown();
+  }
+}
+
+}
+}
new file mode 100644
--- /dev/null
+++ b/gfx/layers/ReadbackProcessor.h
@@ -0,0 +1,105 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * ***** 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 Corporation code.
+ *
+ * The Initial Developer of the Original Code is Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Robert O'Callahan <robert@ocallahan.org>
+ *
+ * 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 GFX_READBACKPROCESSOR_H
+#define GFX_READBACKPROCESSOR_H
+
+#include "ReadbackLayer.h"
+#include "ThebesLayerBuffer.h"
+
+namespace mozilla {
+namespace layers {
+
+class ReadbackProcessor {
+public:
+  /**
+   * Called by the container before processing any child layers. Call this
+   * if any child layer might have changed in any way (other than content-only
+   * changes to layers other than ColorLayers and ThebesLayers).
+   *
+   * This method recomputes the relationship between ReadbackLayers and
+   * sibling layers, and dispatches changes to ReadbackLayers. Except that
+   * if a ThebesLayer needs its contents sent to some ReadbackLayer, we'll
+   * just record that internally and later the ThebesLayer should call
+   * GetThebesLayerUpdates when it paints, to find out which rectangle needs
+   * to be sent, and the ReadbackLayer it needs to be sent to.
+   */
+  void BuildUpdates(ContainerLayer* aContainer);
+
+  struct Update {
+    /**
+     * The layer a ThebesLayer should send its contents to.
+     */
+    ReadbackLayer* mLayer;
+    /**
+     * The rectangle of content that it should send, in the ThebesLayer's
+     * coordinate system. This rectangle is guaranteed to be in the ThebesLayer's
+     * visible region. Translate it to mLayer's coordinate system
+     * by adding mLayer->GetBackgroundLayerOffset().
+     */
+    nsIntRect      mUpdateRect;
+    /**
+     * The sequence counter value to use when calling DoUpdate
+     */
+    PRUint64       mSequenceCounter;
+  };
+  /**
+   * Appends any ReadbackLayers that need to be updated, and the rects that
+   * need to be updated, to aUpdates. Only need to call this for ThebesLayers
+   * that have been marked UsedForReadback().
+   * Each Update's mLayer's mBackgroundLayer will have been set to aLayer.
+   * If a ThebesLayer doesn't call GetThebesLayerUpdates, then all the
+   * ReadbackLayers that needed data from that ThebesLayer will be marked
+   * as having unknown backgrounds.
+   * @param aUpdateRegion if non-null, this region is set to the union
+   * of the mUpdateRects.
+   */
+  void GetThebesLayerUpdates(ThebesLayer* aLayer,
+                             nsTArray<Update>* aUpdates,
+                             nsIntRegion* aUpdateRegion = nsnull);
+
+  ~ReadbackProcessor();
+
+protected:
+  void BuildUpdatesForLayer(ReadbackLayer* aLayer);
+
+  nsTArray<Update> mAllUpdates;
+};
+
+}
+}
+#endif /* GFX_READBACKPROCESSOR_H */
--- a/gfx/layers/basic/BasicLayers.cpp
+++ b/gfx/layers/basic/BasicLayers.cpp
@@ -53,16 +53,17 @@
 #include "nsIRenderingContext.h"
 #include "gfxContext.h"
 #include "gfxImageSurface.h"
 #include "gfxPattern.h"
 #include "gfxPlatform.h"
 #include "gfxUtils.h"
 #include "ThebesLayerBuffer.h"
 #include "nsIWidget.h"
+#include "ReadbackProcessor.h"
 
 #include "GLContext.h"
 
 namespace mozilla {
 namespace layers {
 
 class BasicContainerLayer;
 class ShadowableLayer;
@@ -101,19 +102,26 @@ public:
   }
 
   /**
    * Layers that paint themselves, such as ImageLayers, should paint
    * in response to this method call. aContext will already have been
    * set up to account for all the properties of the layer (transform,
    * opacity, etc).
    */
-  virtual void Paint(gfxContext* aContext,
-                     LayerManager::DrawThebesLayerCallback aCallback,
-                     void* aCallbackData) {}
+  virtual void Paint(gfxContext* aContext) {}
+
+  /**
+   * Like Paint() but called for ThebesLayers with the additional parameters
+   * they need.
+   */
+  virtual void PaintThebes(gfxContext* aContext,
+                           LayerManager::DrawThebesLayerCallback aCallback,
+                           void* aCallbackData,
+                           ReadbackProcessor* aReadback) {}
 
   virtual ShadowableLayer* AsShadowableLayer() { return nsnull; }
 
   /**
    * Implementations return true here if they *must* retain their
    * layer contents.  This is true of shadowable layers with shadows,
    * because there's no target on which to composite directly in the
    * layer-publishing child process.
@@ -231,38 +239,40 @@ ContainerInsertAfter(Layer* aChild, Laye
                "aChild already in the tree");
   NS_ASSERTION(!aChild->GetNextSibling() && !aChild->GetPrevSibling(),
                "aChild already has siblings?");
   NS_ASSERTION(!aAfter ||
                (aAfter->Manager() == aContainer->Manager() &&
                 aAfter->GetParent() == aContainer),
                "aAfter is not our child");
 
-  NS_ADDREF(aChild);
-
   aChild->SetParent(aContainer);
   if (aAfter == aContainer->mLastChild) {
     aContainer->mLastChild = aChild;
   }
   if (!aAfter) {
     aChild->SetNextSibling(aContainer->mFirstChild);
     if (aContainer->mFirstChild) {
       aContainer->mFirstChild->SetPrevSibling(aChild);
     }
     aContainer->mFirstChild = aChild;
+    NS_ADDREF(aChild);
+    aContainer->DidInsertChild(aChild);
     return;
   }
 
   Layer* next = aAfter->GetNextSibling();
   aChild->SetNextSibling(next);
   aChild->SetPrevSibling(aAfter);
   if (next) {
     next->SetPrevSibling(aChild);
   }
   aAfter->SetNextSibling(aChild);
+  NS_ADDREF(aChild);
+  aContainer->DidInsertChild(aChild);
 }
 
 template<class Container>
 static void
 ContainerRemoveChild(Layer* aChild, Container* aContainer)
 {
   NS_ASSERTION(aChild->Manager() == aContainer->Manager(),
                "Child has wrong manager");
@@ -281,16 +291,17 @@ ContainerRemoveChild(Layer* aChild, Cont
   } else {
     aContainer->mLastChild = prev;
   }
 
   aChild->SetNextSibling(nsnull);
   aChild->SetPrevSibling(nsnull);
   aChild->SetParent(nsnull);
 
+  aContainer->DidRemoveChild(aChild);
   NS_RELEASE(aChild);
 }
 
 class BasicThebesLayer;
 class BasicThebesLayerBuffer : public ThebesLayerBuffer {
   typedef ThebesLayerBuffer Base;
 
 public:
@@ -372,19 +383,20 @@ public:
   }
   virtual void InvalidateRegion(const nsIntRegion& aRegion)
   {
     NS_ASSERTION(BasicManager()->InConstruction(),
                  "Can only set properties in construction phase");
     mValidRegion.Sub(mValidRegion, aRegion);
   }
 
-  virtual void Paint(gfxContext* aContext,
-                     LayerManager::DrawThebesLayerCallback aCallback,
-                     void* aCallbackData);
+  virtual void PaintThebes(gfxContext* aContext,
+                           LayerManager::DrawThebesLayerCallback aCallback,
+                           void* aCallbackData,
+                           ReadbackProcessor* aReadback);
 
   virtual void ClearCachedResources() { mBuffer.Clear(); mValidRegion.SetEmpty(); }
   
   virtual already_AddRefed<gfxASurface>
   CreateBuffer(Buffer::ContentType aType, const nsIntSize& aSize)
   {
     nsRefPtr<gfxASurface> referenceSurface = mBuffer.GetBuffer();
     if (!referenceSurface) {
@@ -499,34 +511,42 @@ PushGroupForLayer(gfxContext* aContext, 
     needsClipToVisibleRegion = !didCompleteClip || aRegion.GetNumRects() > 1;
     contentType = gfxASurface::CONTENT_COLOR;
   }
   aContext->PushGroupAndCopyBackground(contentType);
   return needsClipToVisibleRegion;
 }
 
 void
-BasicThebesLayer::Paint(gfxContext* aContext,
-                        LayerManager::DrawThebesLayerCallback aCallback,
-                        void* aCallbackData)
+BasicThebesLayer::PaintThebes(gfxContext* aContext,
+                              LayerManager::DrawThebesLayerCallback aCallback,
+                              void* aCallbackData,
+                              ReadbackProcessor* aReadback)
 {
   NS_ASSERTION(BasicManager()->InDrawing(),
                "Can only draw in drawing phase");
   nsRefPtr<gfxASurface> targetSurface = aContext->CurrentSurface();
 
+  nsTArray<ReadbackProcessor::Update> readbackUpdates;
+  if (aReadback && UsedForReadback()) {
+    aReadback->GetThebesLayerUpdates(this, &readbackUpdates);
+  }
+
   PRBool canUseOpaqueSurface = CanUseOpaqueSurface();
   Buffer::ContentType contentType =
     canUseOpaqueSurface ? gfxASurface::CONTENT_COLOR :
                           gfxASurface::CONTENT_COLOR_ALPHA;
   float opacity = GetEffectiveOpacity();
 
   if (!BasicManager()->IsRetained() ||
       (!canUseOpaqueSurface &&
        (mContentFlags & CONTENT_COMPONENT_ALPHA) &&
        !MustRetainContent())) {
+    NS_ASSERTION(readbackUpdates.IsEmpty(), "Can't do readback for non-retained layer");
+
     mValidRegion.SetEmpty();
     mBuffer.Clear();
 
     nsIntRegion toDraw = IntersectWithClip(mVisibleRegion, aContext);
     if (!toDraw.IsEmpty()) {
       if (!aCallback) {
         BasicManager()->SetTransactionIncomplete();
         return;
@@ -579,16 +599,30 @@ BasicThebesLayer::Paint(gfxContext* aCon
       // It's possible that state.mRegionToInvalidate is nonempty here,
       // if we are shrinking the valid region to nothing.
       NS_ASSERTION(state.mRegionToDraw.IsEmpty(),
                    "If we need to draw, we should have a context");
     }
   }
 
   mBuffer.DrawTo(this, aContext, opacity);
+
+  for (PRUint32 i = 0; i < readbackUpdates.Length(); ++i) {
+    ReadbackProcessor::Update& update = readbackUpdates[i];
+    nsIntPoint offset = update.mLayer->GetBackgroundLayerOffset();
+    nsRefPtr<gfxContext> ctx =
+      update.mLayer->GetSink()->BeginUpdate(update.mUpdateRect + offset,
+                                            update.mSequenceCounter);
+    if (ctx) {
+      NS_ASSERTION(opacity == 1.0, "Should only read back opaque layers");
+      ctx->Translate(gfxPoint(offset.x, offset.y));
+      mBuffer.DrawTo(this, ctx, 1.0);
+      update.mLayer->GetSink()->EndUpdate(ctx, update.mUpdateRect + offset);
+    }
+  }
 }
 
 static PRBool
 IsClippingCheap(gfxContext* aTarget, const nsIntRegion& aRegion)
 {
   // Assume clipping is cheap if the context just has an integer
   // translation, and the visible region is simple.
   return !aTarget->CurrentMatrix().HasNonIntegerTranslation() &&
@@ -659,19 +693,17 @@ public:
 
   virtual void SetVisibleRegion(const nsIntRegion& aRegion)
   {
     NS_ASSERTION(BasicManager()->InConstruction(),
                  "Can only set properties in construction phase");
     ImageLayer::SetVisibleRegion(aRegion);
   }
 
-  virtual void Paint(gfxContext* aContext,
-                     LayerManager::DrawThebesLayerCallback aCallback,
-                     void* aCallbackData);
+  virtual void Paint(gfxContext* aContext);
 
   static void PaintContext(gfxPattern* aPattern,
                            const nsIntRegion& aVisible,
                            const nsIntRect* aTileSourceRect,
                            float aOpacity,
                            gfxContext* aContext);
 
 protected:
@@ -683,19 +715,17 @@ protected:
   already_AddRefed<gfxPattern>
   GetAndPaintCurrentImage(gfxContext* aContext,
                           float aOpacity);
 
   gfxIntSize mSize;
 };
 
 void
-BasicImageLayer::Paint(gfxContext* aContext,
-                       LayerManager::DrawThebesLayerCallback aCallback,
-                       void* aCallbackData)
+BasicImageLayer::Paint(gfxContext* aContext)
 {
   nsRefPtr<gfxPattern> dontcare =
       GetAndPaintCurrentImage(aContext, GetEffectiveOpacity());
 }
 
 already_AddRefed<gfxPattern>
 BasicImageLayer::GetAndPaintCurrentImage(gfxContext* aContext,
                                          float aOpacity)
@@ -791,19 +821,17 @@ public:
 
   virtual void SetVisibleRegion(const nsIntRegion& aRegion)
   {
     NS_ASSERTION(BasicManager()->InConstruction(),
                  "Can only set properties in construction phase");
     ColorLayer::SetVisibleRegion(aRegion);
   }
 
-  virtual void Paint(gfxContext* aContext,
-                     LayerManager::DrawThebesLayerCallback aCallback,
-                     void* aCallbackData)
+  virtual void Paint(gfxContext* aContext)
   {
     PaintColorTo(mColor, GetEffectiveOpacity(), aContext);
   }
 
   static void PaintColorTo(gfxRGBA aColor, float aOpacity,
                            gfxContext* aContext);
 
 protected:
@@ -839,19 +867,17 @@ public:
   {
     NS_ASSERTION(BasicManager()->InConstruction(),
                  "Can only set properties in construction phase");
     CanvasLayer::SetVisibleRegion(aRegion);
   }
 
   virtual void Initialize(const Data& aData);
   virtual void Updated(const nsIntRect& aRect);
-  virtual void Paint(gfxContext* aContext,
-                     LayerManager::DrawThebesLayerCallback aCallback,
-                     void* aCallbackData);
+  virtual void Paint(gfxContext* aContext);
 
   virtual void PaintWithOpacity(gfxContext* aContext,
                                 float aOpacity);
 
 protected:
   BasicLayerManager* BasicManager()
   {
     return static_cast<BasicLayerManager*>(mManager);
@@ -951,19 +977,17 @@ BasicCanvasLayer::Updated(const nsIntRec
   }
 
   // sanity
   NS_ASSERTION(mUpdatedRect.IsEmpty() || mBounds.Contains(mUpdatedRect),
                "CanvasLayer: Updated rect bigger than bounds!");
 }
 
 void
-BasicCanvasLayer::Paint(gfxContext* aContext,
-                        LayerManager::DrawThebesLayerCallback aCallback,
-                        void* aCallbackData)
+BasicCanvasLayer::Paint(gfxContext* aContext)
 {
   PaintWithOpacity(aContext, GetEffectiveOpacity());
 }
 
 void
 BasicCanvasLayer::PaintWithOpacity(gfxContext* aContext,
                                    float aOpacity)
 {
@@ -990,16 +1014,44 @@ BasicCanvasLayer::PaintWithOpacity(gfxCo
 
   if (mNeedsYFlip) {
     aContext->SetMatrix(m);
   }
 
   mUpdatedRect.Empty();
 }
 
+class BasicReadbackLayer : public ReadbackLayer,
+                           BasicImplData
+{
+public:
+  BasicReadbackLayer(BasicLayerManager* aLayerManager) :
+    ReadbackLayer(aLayerManager, static_cast<BasicImplData*>(this))
+  {
+    MOZ_COUNT_CTOR(BasicReadbackLayer);
+  }
+  virtual ~BasicReadbackLayer()
+  {
+    MOZ_COUNT_DTOR(BasicReadbackLayer);
+  }
+
+  virtual void SetVisibleRegion(const nsIntRegion& aRegion)
+  {
+    NS_ASSERTION(BasicManager()->InConstruction(),
+                 "Can only set properties in construction phase");
+    ReadbackLayer::SetVisibleRegion(aRegion);
+  }
+
+protected:
+  BasicLayerManager* BasicManager()
+  {
+    return static_cast<BasicLayerManager*>(mManager);
+  }
+};
+
 static nsIntRect
 ToOutsideIntRect(const gfxRect &aRect)
 {
   gfxRect r = aRect;
   r.RoundOut();
   return nsIntRect(r.pos.x, r.pos.y, r.size.width, r.size.height);
 }
 
@@ -1292,17 +1344,17 @@ BasicLayerManager::EndTransactionInterna
     mSnapEffectiveTransforms =
       !(mTarget->GetFlags() & gfxContext::FLAG_DISABLE_SNAPPING);
     mRoot->ComputeEffectiveTransforms(gfx3DMatrix::From2D(mTarget->CurrentMatrix()));
 
     nsIntRegion region;
     MarkLeafLayersCoveredByOpaque(mRoot,
                                   mRoot->GetEffectiveVisibleRegion().GetBounds(),
                                   region);
-    PaintLayer(mRoot, aCallback, aCallbackData);
+    PaintLayer(mRoot, aCallback, aCallbackData, nsnull);
 
     // If we're doing manual double-buffering, we need to avoid drawing
     // the results of an incomplete transaction to the destination surface.
     // If the transaction is incomplete and we're not double-buffering then
     // either the system is double-buffering our window (in which case the
     // followup EndTransaction will be drawn over the top of our incomplete
     // transaction before the system updates the window), or we have no
     // overlapping or transparent layers in the update region, in which case
@@ -1363,17 +1415,18 @@ BasicLayerManager::SetRoot(Layer* aLayer
   NS_ASSERTION(aLayer->Manager() == this, "Wrong manager");
   NS_ASSERTION(InConstruction(), "Only allowed in construction phase");
   mRoot = aLayer;
 }
 
 void
 BasicLayerManager::PaintLayer(Layer* aLayer,
                               DrawThebesLayerCallback aCallback,
-                              void* aCallbackData)
+                              void* aCallbackData,
+                              ReadbackProcessor* aReadback)
 {
   const nsIntRect* clipRect = aLayer->GetEffectiveClipRect();
   const gfx3DMatrix& effectiveTransform = aLayer->GetEffectiveTransform();
   PRBool needsGroup = aLayer->GetFirstChild() &&
       static_cast<BasicContainerLayer*>(aLayer)->UseIntermediateSurface();
   // If needsSaveRestore is false, we should still save and restore
   // the CTM
   PRBool needsSaveRestore = needsGroup || clipRect;
@@ -1425,21 +1478,31 @@ BasicLayerManager::PaintLayer(Layer* aLa
   Layer* child = aLayer->GetFirstChild();
   if (!child) {
     BasicImplData* data = ToData(aLayer);
 #ifdef MOZ_LAYERS_HAVE_LOG
     MOZ_LAYERS_LOG(("%s (0x%p) is covered: %i\n", __FUNCTION__,
                    (void*)aLayer, data->IsCoveredByOpaque()));
 #endif
     if (!data->IsCoveredByOpaque()) {
-      data->Paint(mTarget, aCallback, aCallbackData);
+      if (aLayer->GetType() == Layer::TYPE_THEBES) {
+        data->PaintThebes(mTarget, aCallback, aCallbackData, aReadback);
+      } else {
+        data->Paint(mTarget);
+      }
     }
   } else {
+    ReadbackProcessor readback;
+    if (IsRetained()) {
+      ContainerLayer* container = static_cast<ContainerLayer*>(aLayer);
+      readback.BuildUpdates(container);
+    }
+
     for (; child; child = child->GetNextSibling()) {
-      PaintLayer(child, aCallback, aCallbackData);
+      PaintLayer(child, aCallback, aCallbackData, &readback);
       if (mTransactionIncomplete)
         break;
     }
   }
 
   if (needsGroup) {
     mTarget->PopGroupToSource();
     if (needsClipToVisibleRegion) {
@@ -1513,16 +1576,23 @@ BasicLayerManager::CreateColorLayer()
 already_AddRefed<CanvasLayer>
 BasicLayerManager::CreateCanvasLayer()
 {
   NS_ASSERTION(InConstruction(), "Only allowed in construction phase");
   nsRefPtr<CanvasLayer> layer = new BasicCanvasLayer(this);
   return layer.forget();
 }
 
+already_AddRefed<ReadbackLayer>
+BasicLayerManager::CreateReadbackLayer()
+{
+  NS_ASSERTION(InConstruction(), "Only allowed in construction phase");
+  nsRefPtr<ReadbackLayer> layer = new BasicReadbackLayer(this);
+  return layer.forget();
+}
 
 #ifdef MOZ_IPC
 
 class BasicShadowableThebesLayer;
 class BasicShadowableLayer : public ShadowableLayer
 {
 public:
   BasicShadowableLayer()
@@ -1569,16 +1639,29 @@ public:
 };
 
 static ShadowableLayer*
 ToShadowable(Layer* aLayer)
 {
   return ToData(aLayer)->AsShadowableLayer();
 }
 
+// Some layers, like ReadbackLayers, can't be shadowed and shadowing
+// them doesn't make sense anyway
+static bool
+ShouldShadow(Layer* aLayer)
+{
+  if (!ToShadowable(aLayer)) {
+    NS_ABORT_IF_FALSE(aLayer->GetType() == Layer::TYPE_READBACK,
+                      "Only expect not to shadow ReadbackLayers");
+    return false;
+  }
+  return true;
+}
+
 template<class OpT>
 static BasicShadowableLayer*
 GetBasicShadowable(const OpT& op)
 {
   return static_cast<BasicShadowableLayer*>(
     static_cast<const ShadowLayerChild*>(op.layerChild())->layer());
 }
 
@@ -1616,28 +1699,31 @@ private:
   {
     return static_cast<BasicShadowLayerManager*>(mManager);
   }
 };
 
 void
 BasicShadowableContainerLayer::InsertAfter(Layer* aChild, Layer* aAfter)
 {
-  if (HasShadow()) {
+  if (HasShadow() && ShouldShadow(aChild)) {
+    while (aAfter && !ShouldShadow(aAfter)) {
+      aAfter = aAfter->GetPrevSibling();
+    }
     ShadowManager()->InsertAfter(ShadowManager()->Hold(this),
                                  ShadowManager()->Hold(aChild),
                                  aAfter ? ShadowManager()->Hold(aAfter) : nsnull);
   }
   BasicContainerLayer::InsertAfter(aChild, aAfter);
 }
 
 void
 BasicShadowableContainerLayer::RemoveChild(Layer* aChild)
 {
-  if (HasShadow()) {
+  if (HasShadow() && ShouldShadow(aChild)) {
     ShadowManager()->RemoveChild(ShadowManager()->Hold(this),
                                  ShadowManager()->Hold(aChild));
   }
   BasicContainerLayer::RemoveChild(aChild);
 }
 
 static PRBool
 IsSurfaceDescriptorValid(const SurfaceDescriptor& aSurface)
@@ -1851,19 +1937,17 @@ public:
   virtual ~BasicShadowableImageLayer()
   {
     if (mBackSurface) {
       BasicManager()->ShadowLayerForwarder::DestroySharedSurface(mBackSurface);
     }
     MOZ_COUNT_DTOR(BasicShadowableImageLayer);
   }
 
-  virtual void Paint(gfxContext* aContext,
-                     LayerManager::DrawThebesLayerCallback aCallback,
-                     void* aCallbackData);
+  virtual void Paint(gfxContext* aContext);
 
   virtual void FillSpecificAttributes(SpecificLayerAttributes& aAttrs)
   {
     aAttrs = ImageLayerAttributes(mFilter);
   }
 
   virtual Layer* AsLayer() { return this; }
   virtual ShadowableLayer* AsShadowableLayer() { return this; }
@@ -1884,19 +1968,17 @@ private:
   {
     return static_cast<BasicShadowLayerManager*>(mManager);
   }
 
   nsRefPtr<gfxSharedImageSurface> mBackSurface;
 };
  
 void
-BasicShadowableImageLayer::Paint(gfxContext* aContext,
-                                 LayerManager::DrawThebesLayerCallback aCallback,
-                                 void* aCallbackData)
+BasicShadowableImageLayer::Paint(gfxContext* aContext)
 {
   gfxIntSize oldSize = mSize;
   nsRefPtr<gfxPattern> pat = GetAndPaintCurrentImage(aContext, GetEffectiveOpacity());
   if (!pat || !HasShadow())
     return;
 
   if (oldSize != mSize) {
     if (mBackSurface) {
@@ -1971,19 +2053,17 @@ public:
   {
     if (mBackBuffer) {
       BasicManager()->ShadowLayerForwarder::DestroySharedSurface(mBackBuffer);
     }
     MOZ_COUNT_DTOR(BasicShadowableCanvasLayer);
   }
 
   virtual void Initialize(const Data& aData);
-  virtual void Paint(gfxContext* aContext,
-                     LayerManager::DrawThebesLayerCallback aCallback,
-                     void* aCallbackData);
+  virtual void Paint(gfxContext* aContext);
 
   virtual void FillSpecificAttributes(SpecificLayerAttributes& aAttrs)
   {
     aAttrs = CanvasLayerAttributes(mFilter);
   }
 
   virtual Layer* AsLayer() { return this; }
   virtual ShadowableLayer* AsShadowableLayer() { return this; }
@@ -2034,21 +2114,19 @@ BasicShadowableCanvasLayer::Initialize(c
     NS_RUNTIMEABORT("creating CanvasLayer back buffer failed!");
 
   BasicManager()->CreatedCanvasBuffer(BasicManager()->Hold(this),
                                       aData.mSize,
                                       tmpFrontBuffer);
 }
 
 void
-BasicShadowableCanvasLayer::Paint(gfxContext* aContext,
-                                  LayerManager::DrawThebesLayerCallback aCallback,
-                                  void* aCallbackData)
+BasicShadowableCanvasLayer::Paint(gfxContext* aContext)
 {
-  BasicCanvasLayer::Paint(aContext, aCallback, aCallbackData);
+  BasicCanvasLayer::Paint(aContext);
   if (!HasShadow())
     return;
 
   // It'd be nice to draw directly into the shmem back buffer.
   // Doing so is complex -- for 2D canvases, we'd need to copy
   // changed areas, much like we do for Thebes layers, as well as
   // do all sorts of magic to swap out the surface underneath the
   // canvas' thebes/cairo context.
@@ -2161,19 +2239,20 @@ public:
     mOldXResolution = 1.0;
     mOldYResolution = 1.0;
 
     if (IsSurfaceDescriptorValid(mFrontBufferDescriptor)) {
       BasicManager()->ShadowLayerManager::DestroySharedSurface(&mFrontBufferDescriptor, mAllocator);
     }
   }
 
-  virtual void Paint(gfxContext* aContext,
-                     LayerManager::DrawThebesLayerCallback aCallback,
-                     void* aCallbackData);
+  virtual void PaintThebes(gfxContext* aContext,
+                           LayerManager::DrawThebesLayerCallback aCallback,
+                           void* aCallbackData,
+                           ReadbackProcessor* aReadback);
 
 private:
   BasicShadowLayerManager* BasicManager()
   {
     return static_cast<BasicShadowLayerManager*>(mManager);
   }
 
   ShadowThebesLayerBuffer mFrontBuffer;
@@ -2255,19 +2334,20 @@ BasicShadowThebesLayer::Swap(const Thebe
 
   mFrontBufferDescriptor = aNewFront.buffer();
 
   *aReadOnlyFront = aNewFront;
   *aFrontUpdatedRegion = aUpdatedRegion;
 }
 
 void
-BasicShadowThebesLayer::Paint(gfxContext* aContext,
-                              LayerManager::DrawThebesLayerCallback aCallback,
-                              void* aCallbackData)
+BasicShadowThebesLayer::PaintThebes(gfxContext* aContext,
+                                    LayerManager::DrawThebesLayerCallback aCallback,
+                                    void* aCallbackData,
+                                    ReadbackProcessor* aReadback)
 {
   NS_ASSERTION(BasicManager()->InDrawing(),
                "Can only draw in drawing phase");
   NS_ASSERTION(BasicManager()->IsRetained(),
                "ShadowThebesLayer makes no sense without retained mode");
 
   if (!mFrontBuffer.GetBuffer()) {
     return;
@@ -2351,19 +2431,17 @@ public:
   virtual void DestroyFrontBuffer()
   {
     if (mFrontSurface) {
       BasicManager()->ShadowLayerManager::DestroySharedSurface(mFrontSurface, mAllocator);
     }
     mFrontSurface = nsnull;
   }
 
-  virtual void Paint(gfxContext* aContext,
-                     LayerManager::DrawThebesLayerCallback aCallback,
-                     void* aCallbackData);
+  virtual void Paint(gfxContext* aContext);
 
 protected:
   BasicShadowLayerManager* BasicManager()
   {
     return static_cast<BasicShadowLayerManager*>(mManager);
   }
 
   // XXX ShmemImage?
@@ -2384,19 +2462,17 @@ already_AddRefed<gfxSharedImageSurface>
 BasicShadowImageLayer::Swap(gfxSharedImageSurface* newFront)
 {
   already_AddRefed<gfxSharedImageSurface> tmp = mFrontSurface.forget();
   mFrontSurface = newFront;
   return tmp;
 }
 
 void
-BasicShadowImageLayer::Paint(gfxContext* aContext,
-                             LayerManager::DrawThebesLayerCallback aCallback,
-                             void* aCallbackData)
+BasicShadowImageLayer::Paint(gfxContext* aContext)
 {
   if (!mFrontSurface) {
     return;
   }
 
   nsRefPtr<gfxPattern> pat = new gfxPattern(mFrontSurface);
   pat->SetFilter(mFilter);
   BasicImageLayer::PaintContext(
@@ -2412,19 +2488,17 @@ public:
   {
     MOZ_COUNT_CTOR(BasicShadowColorLayer);
   }
   virtual ~BasicShadowColorLayer()
   {
     MOZ_COUNT_DTOR(BasicShadowColorLayer);
   }
 
-  virtual void Paint(gfxContext* aContext,
-                     LayerManager::DrawThebesLayerCallback aCallback,
-                     void* aCallbackData)
+  virtual void Paint(gfxContext* aContext)
   {
     BasicColorLayer::PaintColorTo(mColor, GetEffectiveOpacity(), aContext);
   }
 };
 
 class BasicShadowCanvasLayer : public ShadowCanvasLayer,
                                BasicImplData
 {
@@ -2456,19 +2530,17 @@ public:
   virtual void DestroyFrontBuffer()
   {
     if (mFrontSurface) {
       BasicManager()->ShadowLayerManager::DestroySharedSurface(mFrontSurface, mAllocator);
     }
     mFrontSurface = nsnull;
   }
 
-  virtual void Paint(gfxContext* aContext,
-                     LayerManager::DrawThebesLayerCallback aCallback,
-                     void* aCallbackData);
+  virtual void Paint(gfxContext* aContext);
 
 private:
   BasicShadowLayerManager* BasicManager()
   {
     return static_cast<BasicShadowLayerManager*>(mManager);
   }
 
   nsRefPtr<gfxSharedImageSurface> mFrontSurface;
@@ -2490,19 +2562,17 @@ already_AddRefed<gfxSharedImageSurface>
 BasicShadowCanvasLayer::Swap(gfxSharedImageSurface* newFront)
 {
   already_AddRefed<gfxSharedImageSurface> tmp = mFrontSurface.forget();
   mFrontSurface = newFront;
   return tmp;
 }
 
 void
-BasicShadowCanvasLayer::Paint(gfxContext* aContext,
-                              LayerManager::DrawThebesLayerCallback aCallback,
-                              void* aCallbackData)
+BasicShadowCanvasLayer::Paint(gfxContext* aContext)
 {
   NS_ASSERTION(BasicManager()->InDrawing(),
                "Can only draw in drawing phase");
 
   if (!mFrontSurface) {
     return;
   }
 
@@ -2663,17 +2733,17 @@ BasicShadowLayerManager::SetRoot(Layer* 
 }
 
 void
 BasicShadowLayerManager::Mutated(Layer* aLayer)
 {
   BasicLayerManager::Mutated(aLayer);
 
   NS_ASSERTION(InConstruction() || InDrawing(), "wrong phase");
-  if (HasShadowManager()) {
+  if (HasShadowManager() && ShouldShadow(aLayer)) {
     ShadowLayerForwarder::Mutated(Hold(aLayer));
   }
 }
 
 void
 BasicShadowLayerManager::BeginTransactionWithTarget(gfxContext* aTarget)
 {
   NS_ABORT_IF_FALSE(mKeepAlive.IsEmpty(), "uncommitted txn?");
--- a/gfx/layers/basic/BasicLayers.h
+++ b/gfx/layers/basic/BasicLayers.h
@@ -55,16 +55,17 @@ namespace mozilla {
 namespace layers {
 
 class BasicShadowableLayer;
 class ShadowThebesLayer;
 class ShadowContainerLayer;
 class ShadowImageLayer;
 class ShadowCanvasLayer;
 class ShadowColorLayer;
+class ReadbackProcessor;
 
 /**
  * This is a cairo/Thebes-only, main-thread-only implementation of layers.
  * 
  * In each transaction, the client sets up the layer tree and then during
  * the drawing phase, each ThebesLayer is painted directly into the target
  * context (with appropriate clipping and Push/PopGroups performed
  * between layers).
@@ -147,26 +148,27 @@ public:
   virtual void SetRoot(Layer* aLayer);
 
   virtual already_AddRefed<ThebesLayer> CreateThebesLayer();
   virtual already_AddRefed<ContainerLayer> CreateContainerLayer();
   virtual already_AddRefed<ImageLayer> CreateImageLayer();
   virtual already_AddRefed<CanvasLayer> CreateCanvasLayer();
   virtual already_AddRefed<ImageContainer> CreateImageContainer();
   virtual already_AddRefed<ColorLayer> CreateColorLayer();
+  virtual already_AddRefed<ReadbackLayer> CreateReadbackLayer();
   virtual already_AddRefed<ShadowThebesLayer> CreateShadowThebesLayer()
-  { return NULL; }
+  { return nsnull; }
   virtual already_AddRefed<ShadowContainerLayer> CreateShadowContainerLayer()
-  { return NULL; }
+  { return nsnull; }
   virtual already_AddRefed<ShadowImageLayer> CreateShadowImageLayer()
-  { return NULL; }
+  { return nsnull; }
   virtual already_AddRefed<ShadowColorLayer> CreateShadowColorLayer()
-  { return NULL; }
+  { return nsnull; }
   virtual already_AddRefed<ShadowCanvasLayer> CreateShadowCanvasLayer()
-  { return NULL; }
+  { return nsnull; }
 
   virtual LayersBackend GetBackendType() { return LAYERS_BASIC; }
   virtual void GetBackendName(nsAString& name) { name.AssignLiteral("Basic"); }
 
 #ifdef DEBUG
   PRBool InConstruction() { return mPhase == PHASE_CONSTRUCTION; }
   PRBool InDrawing() { return mPhase == PHASE_DRAWING; }
   PRBool InForward() { return mPhase == PHASE_FORWARD; }
@@ -192,17 +194,18 @@ protected:
     PHASE_NONE, PHASE_CONSTRUCTION, PHASE_DRAWING, PHASE_FORWARD
   };
   TransactionPhase mPhase;
 #endif
 
   // Paints aLayer to mTarget.
   void PaintLayer(Layer* aLayer,
                   DrawThebesLayerCallback aCallback,
-                  void* aCallbackData);
+                  void* aCallbackData,
+                  ReadbackProcessor* aReadback);
 
   // Clear the contents of a layer
   void ClearLayer(Layer* aLayer);
 
   already_AddRefed<gfxContext> PushGroupWithCachedSurface(gfxContext *aTarget,
                                                           gfxASurface::gfxContentType aContent,
                                                           gfxPoint *aSavedOffset);
   void PopGroupWithCachedSurface(gfxContext *aTarget,
--- a/gfx/layers/d3d10/CanvasLayerD3D10.h
+++ b/gfx/layers/d3d10/CanvasLayerD3D10.h
@@ -31,18 +31,18 @@
  * 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 GFX_CANVASLAYEROGL_H
-#define GFX_CANVASLAYEROGL_H
+#ifndef GFX_CANVASLAYERD3D10_H
+#define GFX_CANVASLAYERD3D10_H
 
 #include "LayerManagerD3D10.h"
 #include "GLContext.h"
 #include "gfxASurface.h"
 
 namespace mozilla {
 namespace layers {
 
--- a/gfx/layers/d3d10/ContainerLayerD3D10.cpp
+++ b/gfx/layers/d3d10/ContainerLayerD3D10.cpp
@@ -35,16 +35,19 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "ContainerLayerD3D10.h"
 #include "nsAlgorithm.h"
 #include "gfxUtils.h"
 #include "nsRect.h"
 
+#include "ThebesLayerD3D10.h"
+#include "ReadbackProcessor.h"
+
 namespace mozilla {
 namespace layers {
 
 ContainerLayerD3D10::ContainerLayerD3D10(LayerManagerD3D10 *aManager)
   : ContainerLayer(aManager, NULL)
   , LayerD3D10(aManager)
 {
   mImplData = static_cast<LayerD3D10*>(this);
@@ -67,31 +70,33 @@ ContainerLayerD3D10::InsertAfter(Layer* 
     aChild->SetNextSibling(oldFirstChild);
     aChild->SetPrevSibling(nsnull);
     if (oldFirstChild) {
       oldFirstChild->SetPrevSibling(aChild);
     } else {
       mLastChild = aChild;
     }
     NS_ADDREF(aChild);
+    DidInsertChild(aChild);
     return;
   }
   for (Layer *child = GetFirstChild();
        child; child = child->GetNextSibling()) {
     if (aAfter == child) {
       Layer *oldNextSibling = child->GetNextSibling();
       child->SetNextSibling(aChild);
       aChild->SetNextSibling(oldNextSibling);
       if (oldNextSibling) {
         oldNextSibling->SetPrevSibling(aChild);
       } else {
         mLastChild = aChild;
       }
       aChild->SetPrevSibling(child);
       NS_ADDREF(aChild);
+      DidInsertChild(aChild);
       return;
     }
   }
   NS_WARNING("Failed to find aAfter layer!");
 }
 
 void
 ContainerLayerD3D10::RemoveChild(Layer *aChild)
@@ -101,16 +106,17 @@ ContainerLayerD3D10::RemoveChild(Layer *
     if (mFirstChild) {
       mFirstChild->SetPrevSibling(nsnull);
     } else {
       mLastChild = nsnull;
     }
     aChild->SetNextSibling(nsnull);
     aChild->SetPrevSibling(nsnull);
     aChild->SetParent(nsnull);
+    DidRemoveChild(aChild);
     NS_RELEASE(aChild);
     return;
   }
   Layer *lastChild = nsnull;
   for (Layer *child = GetFirstChild(); child;
        child = child->GetNextSibling()) {
     if (child == aChild) {
       // We're sure this is not our first child. So lastChild != NULL.
@@ -118,16 +124,17 @@ ContainerLayerD3D10::RemoveChild(Layer *
       if (child->GetNextSibling()) {
         child->GetNextSibling()->SetPrevSibling(lastChild);
       } else {
         mLastChild = lastChild;
       }
       child->SetNextSibling(nsnull);
       child->SetPrevSibling(nsnull);
       child->SetParent(nsnull);
+      DidRemoveChild(aChild);
       NS_RELEASE(aChild);
       return;
     }
     lastChild = child;
   }
 }
 
 Layer*
@@ -393,17 +400,24 @@ ContainerLayerD3D10::Validate()
         mSupportsComponentAlphaChildren = PR_TRUE;
       }
     }
   } else {
     mSupportsComponentAlphaChildren = (GetContentFlags() & CONTENT_OPAQUE) ||
         (mParent && mParent->SupportsComponentAlphaChildren());
   }
 
+  ReadbackProcessor readback;
+  readback.BuildUpdates(this);
+
   Layer *layer = GetFirstChild();
   while (layer) {
-    static_cast<LayerD3D10*>(layer->ImplData())->Validate();
+    if (layer->GetType() == TYPE_THEBES) {
+      static_cast<ThebesLayerD3D10*>(layer)->Validate(&readback);
+    } else {
+      static_cast<LayerD3D10*>(layer->ImplData())->Validate();
+    }
     layer = layer->GetNextSibling();
   }
 }
 
 } /* layers */
 } /* mozilla */
--- a/gfx/layers/d3d10/ImageLayerD3D10.cpp
+++ b/gfx/layers/d3d10/ImageLayerD3D10.cpp
@@ -85,18 +85,21 @@ SurfaceToTexture(ID3D10Device *aDevice,
   desc.Usage = D3D10_USAGE_IMMUTABLE;
   
   data.pSysMem = imageSurface->Data();
   data.SysMemPitch = imageSurface->Stride();
 
   nsRefPtr<ID3D10Texture2D> texture;
   HRESULT hr = aDevice->CreateTexture2D(&desc, &data, getter_AddRefs(texture));
 
-  LayerManagerD3D10::ReportFailure(NS_LITERAL_CSTRING("Failed to create texture for image surface"),
-                                   hr);
+  if (FAILED(hr)) {
+    LayerManagerD3D10::ReportFailure(NS_LITERAL_CSTRING("Failed to create texture for image surface"),
+                                     hr);
+  }
+
   return texture.forget();
 }
 
 ImageContainerD3D10::ImageContainerD3D10(ID3D10Device1 *aDevice)
   : ImageContainer(nsnull)
   , mDevice(aDevice)
   , mActiveImageLock("mozilla.layers.ImageContainerD3D10.mActiveImageLock")
 {
--- a/gfx/layers/d3d10/LayerManagerD3D10.cpp
+++ b/gfx/layers/d3d10/LayerManagerD3D10.cpp
@@ -34,23 +34,25 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "LayerManagerD3D10.h"
 #include "LayerManagerD3D10Effect.h"
 #include "gfxWindowsPlatform.h"
 #include "gfxD2DSurface.h"
+#include "gfxFailure.h"
 #include "cairo-win32.h"
 #include "dxgi.h"
 
 #include "ContainerLayerD3D10.h"
 #include "ThebesLayerD3D10.h"
 #include "ColorLayerD3D10.h"
 #include "CanvasLayerD3D10.h"
+#include "ReadbackLayerD3D10.h"
 #include "ImageLayerD3D10.h"
 
 #include "../d3d9/Nv3DVUtils.h"
 
 namespace mozilla {
 namespace layers {
 
 typedef HRESULT (WINAPI*D3D10CreateEffectFromMemoryFunc)(
@@ -62,35 +64,60 @@ typedef HRESULT (WINAPI*D3D10CreateEffec
     ID3D10Effect **ppEffect
 );
 
 struct Vertex
 {
     float position[2];
 };
 
-// {17F88CCB-1F49-4c08-8002-ADA7BD44856D}
-static const GUID sEffect = 
-{ 0x17f88ccb, 0x1f49, 0x4c08, { 0x80, 0x2, 0xad, 0xa7, 0xbd, 0x44, 0x85, 0x6d } };
-// {19599D91-912C-4C2F-A8C5-299DE85FBD34}
-static const GUID sInputLayout = 
-{ 0x19599d91, 0x912c, 0x4c2f, { 0xa8, 0xc5, 0x29, 0x9d, 0xe8, 0x5f, 0xbd, 0x34 } };
-// {293157D2-09C7-4680-AE27-C28E370E418B}
-static const GUID sVertexBuffer = 
-{ 0x293157d2, 0x9c7, 0x4680, { 0xae, 0x27, 0xc2, 0x8e, 0x37, 0xe, 0x41, 0x8b } };
+// {592BF306-0EED-4F76-9D03-A0846450F472}
+static const GUID sDeviceAttachments = 
+{ 0x592bf306, 0xeed, 0x4f76, { 0x9d, 0x3, 0xa0, 0x84, 0x64, 0x50, 0xf4, 0x72 } };
+// {716AEDB1-C9C3-4B4D-8332-6F65D44AF6A8}
+static const GUID sLayerManagerCount = 
+{ 0x716aedb1, 0xc9c3, 0x4b4d, { 0x83, 0x32, 0x6f, 0x65, 0xd4, 0x4a, 0xf6, 0xa8 } };
 
 cairo_user_data_key_t gKeyD3D10Texture;
 
 LayerManagerD3D10::LayerManagerD3D10(nsIWidget *aWidget)
   : mWidget(aWidget)
 {
 }
 
+struct DeviceAttachments
+{
+  nsRefPtr<ID3D10Effect> mEffect;
+  nsRefPtr<ID3D10InputLayout> mInputLayout;
+  nsRefPtr<ID3D10Buffer> mVertexBuffer;
+  nsRefPtr<ReadbackManagerD3D10> mReadbackManager;
+};
+
 LayerManagerD3D10::~LayerManagerD3D10()
 {
+  if (mDevice) {
+    int referenceCount = 0;
+    UINT size = sizeof(referenceCount);
+    HRESULT hr = mDevice->GetPrivateData(sLayerManagerCount, &size, &referenceCount);
+    NS_ASSERTION(SUCCEEDED(hr), "Reference count not found on device.");
+    referenceCount--;
+    mDevice->SetPrivateData(sLayerManagerCount, sizeof(referenceCount), &referenceCount);
+
+    if (!referenceCount) {
+      DeviceAttachments *attachments;
+      size = sizeof(attachments);
+      mDevice->GetPrivateData(sDeviceAttachments, &size, &attachments);
+      // No LayerManagers left for this device. Clear out interfaces stored which
+      // hold a reference to the device.
+      mDevice->SetPrivateData(sDeviceAttachments, 0, NULL);
+
+      delete attachments;
+    }
+  }
+
   Destroy();
 }
 
 static bool
 IsOptimus()
 {
   return GetModuleHandleA("nvumdshim.dll");
 }
@@ -124,18 +151,29 @@ LayerManagerD3D10::Initialize()
   if (mNv3DVUtils) {
     IUnknown* devUnknown = NULL;
     if (mDevice) {
       mDevice->QueryInterface(IID_IUnknown, (void **)&devUnknown);
     }
     mNv3DVUtils->SetDeviceInfo(devUnknown);
   }
 
-  UINT size = sizeof(ID3D10Effect*);
-  if (FAILED(mDevice->GetPrivateData(sEffect, &size, mEffect.StartAssignment()))) {
+  int referenceCount = 0;
+  UINT size = sizeof(referenceCount);
+  // If this isn't there yet it'll fail, count will remain 0, which is correct.
+  mDevice->GetPrivateData(sLayerManagerCount, &size, &referenceCount);
+  referenceCount++;
+  mDevice->SetPrivateData(sLayerManagerCount, sizeof(referenceCount), &referenceCount);
+
+  DeviceAttachments *attachments;
+  size = sizeof(DeviceAttachments*);
+  if (FAILED(mDevice->GetPrivateData(sDeviceAttachments, &size, &attachments))) {
+    attachments = new DeviceAttachments;
+    mDevice->SetPrivateData(sDeviceAttachments, sizeof(attachments), &attachments);
+
     D3D10CreateEffectFromMemoryFunc createEffect = (D3D10CreateEffectFromMemoryFunc)
 	GetProcAddress(LoadLibraryA("d3d10_1.dll"), "D3D10CreateEffectFromMemory");
 
     if (!createEffect) {
       return false;
     }
 
     hr = createEffect((void*)g_main,
@@ -144,21 +182,18 @@ LayerManagerD3D10::Initialize()
                       mDevice,
                       NULL,
                       getter_AddRefs(mEffect));
     
     if (FAILED(hr)) {
       return false;
     }
 
-    mDevice->SetPrivateDataInterface(sEffect, mEffect);
-  }
-
-  size = sizeof(ID3D10InputLayout*);
-  if (FAILED(mDevice->GetPrivateData(sInputLayout, &size, mInputLayout.StartAssignment()))) {
+    attachments->mEffect = mEffect;
+  
     D3D10_INPUT_ELEMENT_DESC layout[] =
     {
       { "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D10_INPUT_PER_VERTEX_DATA, 0 },
     };
     D3D10_PASS_DESC passDesc;
     mEffect->GetTechniqueByName("RenderRGBLayerPremul")->GetPassByIndex(0)->
       GetDesc(&passDesc);
 
@@ -167,33 +202,34 @@ LayerManagerD3D10::Initialize()
                                     passDesc.pIAInputSignature,
                                     passDesc.IAInputSignatureSize,
                                     getter_AddRefs(mInputLayout));
     
     if (FAILED(hr)) {
       return false;
     }
 
-    mDevice->SetPrivateDataInterface(sInputLayout, mInputLayout);
-  }
-
-  size = sizeof(ID3D10Buffer*);
-  if (FAILED(mDevice->GetPrivateData(sVertexBuffer, &size, mVertexBuffer.StartAssignment()))) {
+    attachments->mInputLayout = mInputLayout;
+  
     Vertex vertices[] = { {0.0, 0.0}, {1.0, 0.0}, {0.0, 1.0}, {1.0, 1.0} };
     CD3D10_BUFFER_DESC bufferDesc(sizeof(vertices), D3D10_BIND_VERTEX_BUFFER);
     D3D10_SUBRESOURCE_DATA data;
     data.pSysMem = (void*)vertices;
 
     hr = mDevice->CreateBuffer(&bufferDesc, &data, getter_AddRefs(mVertexBuffer));
 
     if (FAILED(hr)) {
       return false;
     }
 
-    mDevice->SetPrivateDataInterface(sVertexBuffer, mVertexBuffer);
+    attachments->mVertexBuffer = mVertexBuffer;
+  } else {
+    mEffect = attachments->mEffect;
+    mVertexBuffer = attachments->mVertexBuffer;
+    mInputLayout = attachments->mInputLayout;
   }
 
   nsRefPtr<IDXGIDevice> dxgiDevice;
   nsRefPtr<IDXGIAdapter> dxgiAdapter;
   nsRefPtr<IDXGIFactory> dxgiFactory;
 
   mDevice->QueryInterface(dxgiDevice.StartAssignment());
   dxgiDevice->GetAdapter(getter_AddRefs(dxgiAdapter));
@@ -328,16 +364,23 @@ LayerManagerD3D10::CreateColorLayer()
 
 already_AddRefed<CanvasLayer>
 LayerManagerD3D10::CreateCanvasLayer()
 {
   nsRefPtr<CanvasLayer> layer = new CanvasLayerD3D10(this);
   return layer.forget();
 }
 
+already_AddRefed<ReadbackLayer>
+LayerManagerD3D10::CreateReadbackLayer()
+{
+  nsRefPtr<ReadbackLayer> layer = new ReadbackLayerD3D10(this);
+  return layer.forget();
+}
+
 already_AddRefed<ImageContainer>
 LayerManagerD3D10::CreateImageContainer()
 {
   nsRefPtr<ImageContainer> layer = new ImageContainerD3D10(mDevice);
   return layer.forget();
 }
 
 static void ReleaseTexture(void *texture)
@@ -377,16 +420,23 @@ LayerManagerD3D10::CreateOptimalSurface(
 
   surface->SetData(&gKeyD3D10Texture,
                    texture.forget().get(),
                    ReleaseTexture);
 
   return surface.forget();
 }
 
+ReadbackManagerD3D10*
+LayerManagerD3D10::readbackManager()
+{
+  EnsureReadbackManager();
+  return mReadbackManager;
+}
+
 void
 LayerManagerD3D10::SetViewport(const nsIntSize &aViewport)
 {
   mViewport = aViewport;
 
   D3D10_VIEWPORT viewport;
   viewport.MaxDepth = 1.0f;
   viewport.MinDepth = 0;
@@ -491,16 +541,42 @@ LayerManagerD3D10::VerifyBufferSize()
     mSwapChain->ResizeBuffers(1, rect.width, rect.height,
                               DXGI_FORMAT_B8G8R8A8_UNORM,
                               DXGI_SWAP_CHAIN_FLAG_GDI_COMPATIBLE);
   }
 
 }
 
 void
+LayerManagerD3D10::EnsureReadbackManager()
+{
+  if (mReadbackManager) {
+    return;
+  }
+
+  DeviceAttachments *attachments;
+  UINT size = sizeof(DeviceAttachments*);
+  if (FAILED(mDevice->GetPrivateData(sDeviceAttachments, &size, &attachments))) {
+    // Strange! This shouldn't happen ... return a readback manager for this
+    // layer manager only.
+    mReadbackManager = new ReadbackManagerD3D10();
+    gfx::LogFailure(NS_LITERAL_CSTRING("Couldn't get device attachments for device."));
+    return;
+  }
+
+  if (attachments->mReadbackManager) {
+    mReadbackManager = attachments->mReadbackManager;
+    return;
+  }
+
+  mReadbackManager = new ReadbackManagerD3D10();
+  attachments->mReadbackManager = mReadbackManager;
+}
+
+void
 LayerManagerD3D10::Render()
 {
   if (mRoot) {
     static_cast<LayerD3D10*>(mRoot->ImplData())->Validate();
   }
 
   SetupPipeline();
 
--- a/gfx/layers/d3d10/LayerManagerD3D10.h
+++ b/gfx/layers/d3d10/LayerManagerD3D10.h
@@ -41,16 +41,18 @@
 #include "Layers.h"
 
 #include <windows.h>
 #include <d3d10_1.h>
 
 #include "gfxContext.h"
 #include "nsIWidget.h"
 
+#include "ReadbackManagerD3D10.h"
+
 namespace mozilla {
 namespace layers {
 
 class Nv3DVUtils;
 
 /**
  * This structure is used to pass rectangles to our shader constant. We can use
  * this for passing rectangular areas to SetVertexShaderConstant. In the format
@@ -118,16 +120,18 @@ public:
   virtual already_AddRefed<ContainerLayer> CreateContainerLayer();
 
   virtual already_AddRefed<ImageLayer> CreateImageLayer();
 
   virtual already_AddRefed<ColorLayer> CreateColorLayer();
 
   virtual already_AddRefed<CanvasLayer> CreateCanvasLayer();
 
+  virtual already_AddRefed<ReadbackLayer> CreateReadbackLayer();
+
   virtual already_AddRefed<ImageContainer> CreateImageContainer();
 
   virtual already_AddRefed<gfxASurface>
     CreateOptimalSurface(const gfxIntSize &aSize,
                          gfxASurface::gfxImageFormat imageFormat);
 
   virtual LayersBackend GetBackendType() { return LAYERS_D3D10; }
   virtual void GetBackendName(nsAString& name) { name.AssignLiteral("Direct3D 10"); }
@@ -137,38 +141,42 @@ public:
 #endif // MOZ_LAYERS_HAVE_LOG
 
   // Public helpers
 
   ID3D10Device1 *device() const { return mDevice; }
 
   ID3D10Effect *effect() const { return mEffect; }
 
+  ReadbackManagerD3D10 *readbackManager();
+
   void SetViewport(const nsIntSize &aViewport);
   const nsIntSize &GetViewport() { return mViewport; }
 
   /**
    * Return pointer to the Nv3DVUtils instance
    */
   Nv3DVUtils *GetNv3DVUtils()  { return mNv3DVUtils; }
 
   static void LayerManagerD3D10::ReportFailure(const nsACString &aMsg, HRESULT aCode);
 
 private:
   void SetupPipeline();
   void UpdateRenderTarget();
   void VerifyBufferSize();
+  void EnsureReadbackManager();
 
   void Render();
 
   nsRefPtr<ID3D10Device1> mDevice;
 
   nsRefPtr<ID3D10Effect> mEffect;
   nsRefPtr<ID3D10InputLayout> mInputLayout;
   nsRefPtr<ID3D10Buffer> mVertexBuffer;
+  nsRefPtr<ReadbackManagerD3D10> mReadbackManager;
 
   nsRefPtr<ID3D10RenderTargetView> mRTView;
 
   nsRefPtr<IDXGISwapChain> mSwapChain;
 
   nsIWidget *mWidget;
 
   CallbackInfo mCurrentCallbackInfo;
new file mode 100644
--- /dev/null
+++ b/gfx/layers/d3d10/ReadbackLayerD3D10.h
@@ -0,0 +1,65 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * ***** 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 Corporation code.
+ *
+ * The Initial Developer of the Original Code is Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Bas Schouten <bschouten@mozilla.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 GFX_READBACKLAYERD3D10_H
+#define GFX_READBACKLAYERD3D10_H
+
+#include "LayerManagerD3D10.h"
+#include "ReadbackLayer.h"
+
+namespace mozilla {
+namespace layers {
+
+class THEBES_API ReadbackLayerD3D10 :
+  public ReadbackLayer,
+  public LayerD3D10
+{
+public:
+    ReadbackLayerD3D10(LayerManagerD3D10 *aManager)
+    : ReadbackLayer(aManager, NULL),
+      LayerD3D10(aManager)
+  {
+      mImplData = static_cast<LayerD3D10*>(this);
+  }
+
+  virtual Layer* GetLayer() { return this; }
+  virtual void RenderLayer() {}
+};
+
+} /* layers */
+} /* mozilla */
+#endif /* GFX_READBACKLAYERD3D10_H */
new file mode 100644
--- /dev/null
+++ b/gfx/layers/d3d10/ReadbackManagerD3D10.cpp
@@ -0,0 +1,250 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * ***** 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 Corporation code.
+ *
+ * The Initial Developer of the Original Code is Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Bas Schouten <bschouten@mozilla.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 "ReadbackManagerD3D10.h"
+#include "ReadbackProcessor.h"
+
+#include "nsIThread.h"
+#include "nsThreadUtils.h"
+#include "gfxImageSurface.h"
+
+namespace mozilla {
+namespace layers {
+
+// Structure that contains the information required to execute a readback task,
+// the only member accessed off the main thread here is mReadbackTexture. Since
+// mLayer may be released only on the main thread this object should always be
+// destroyed on the main thread!
+struct ReadbackTask {
+  // The texture that we copied the contents of the thebeslayer to.
+  nsRefPtr<ID3D10Texture2D> mReadbackTexture;
+  // This exists purely to keep the ReadbackLayer alive for the lifetime of
+  // mUpdate. Note that this addref and release should occur -solely- on the
+  // main thread.
+  nsRefPtr<ReadbackLayer> mLayer;
+  ReadbackProcessor::Update mUpdate;
+  // The origin in ThebesLayer coordinates of mReadbackTexture.
+  gfxPoint mOrigin;
+  // mLayer->GetBackgroundOffset() when the task is created.  We have
+  // to save this in the ReadbackTask because it might change before
+  // the update is delivered to the readback sink.
+  nsIntPoint mBackgroundOffset;
+};
+
+// This class is created and dispatched from the Readback thread but it must be
+// destroyed by the main thread.
+class ReadbackResultWriter : public nsIRunnable
+{
+  NS_DECL_ISUPPORTS
+public:
+  ReadbackResultWriter(ReadbackTask *aTask) : mTask(aTask) {}
+
+  NS_IMETHODIMP Run()
+  {
+    ReadbackProcessor::Update *update = &mTask->mUpdate;
+
+    if (!update->mLayer->GetSink()) {
+      // This can happen when a plugin is destroyed.
+      return NS_OK;
+    }
+
+    nsIntPoint offset = mTask->mBackgroundOffset;
+
+    D3D10_TEXTURE2D_DESC desc;
+    mTask->mReadbackTexture->GetDesc(&desc);
+
+    D3D10_MAPPED_TEXTURE2D mappedTex;
+    // We know this map will immediately succeed, as we've already mapped this
+    // copied data on our task thread.
+    HRESULT hr = mTask->mReadbackTexture->Map(0, D3D10_MAP_READ, 0, &mappedTex);
+
+    if (FAILED(hr)) {
+      // If this fails we're never going to get our ThebesLayer content.
+      update->mLayer->GetSink()->SetUnknown(update->mSequenceCounter);
+      return NS_OK;
+    }
+
+    nsRefPtr<gfxImageSurface> sourceSurface =
+      new gfxImageSurface((unsigned char*)mappedTex.pData,
+                          gfxIntSize(desc.Width, desc.Height),
+                          mappedTex.RowPitch,
+                          gfxASurface::ImageFormatRGB24);
+
+    nsRefPtr<gfxContext> ctx =
+      update->mLayer->GetSink()->BeginUpdate(update->mUpdateRect + offset,
+                                             update->mSequenceCounter);
+
+    if (ctx) {
+      ctx->Translate(gfxPoint(offset.x, offset.y));
+      ctx->SetSource(sourceSurface, gfxPoint(mTask->mOrigin.x,
+                                             mTask->mOrigin.y));
+      ctx->Paint();
+
+      update->mLayer->GetSink()->EndUpdate(ctx, update->mUpdateRect + offset);
+    }
+
+    mTask->mReadbackTexture->Unmap(0);
+
+    return NS_OK;
+  }
+
+private:
+  nsAutoPtr<ReadbackTask> mTask;
+};
+
+NS_IMPL_THREADSAFE_ISUPPORTS1(ReadbackResultWriter, nsIRunnable)
+
+DWORD WINAPI StartTaskThread(void *aManager)
+{
+  static_cast<ReadbackManagerD3D10*>(aManager)->ProcessTasks();
+
+  return 0;
+}
+
+ReadbackManagerD3D10::ReadbackManagerD3D10()
+  : mRefCnt(0)
+{
+  ::InitializeCriticalSection(&mTaskMutex);
+  mShutdownEvent = ::CreateEventA(NULL, FALSE, FALSE, "ReadbackShutdownEvent");
+  mTaskSemaphore = ::CreateSemaphoreA(NULL, 0, 1000000, "ReadbackTaskSemaphore");
+  mTaskThread = ::CreateThread(NULL, 0, StartTaskThread, this, 0, 0);
+}
+
+ReadbackManagerD3D10::~ReadbackManagerD3D10()
+{
+  ::SetEvent(mShutdownEvent);
+
+  // This shouldn't take longer than 5 seconds, if it does we're going to choose
+  // to leak the thread and its synchronisation in favor of crashing or freezing
+  DWORD result = ::WaitForSingleObject(mTaskThread, 5000);
+  if (result != WAIT_TIMEOUT) {
+    ::DeleteCriticalSection(&mTaskMutex);
+    ::CloseHandle(mShutdownEvent);
+    ::CloseHandle(mTaskSemaphore);
+    ::CloseHandle(mTaskThread);
+  } else {
+    NS_WARNING("ReadbackManager: Task thread did not shutdown in 5 seconds. Leaking.");
+  }
+}
+
+void
+ReadbackManagerD3D10::PostTask(ID3D10Texture2D *aTexture, void *aUpdate, const gfxPoint &aOrigin)
+{
+  ReadbackTask *task = new ReadbackTask;
+  task->mReadbackTexture = aTexture;
+  task->mUpdate = *static_cast<ReadbackProcessor::Update*>(aUpdate);
+  task->mOrigin = aOrigin;
+  task->mLayer = task->mUpdate.mLayer;
+  task->mBackgroundOffset = task->mLayer->GetBackgroundLayerOffset();
+
+  ::EnterCriticalSection(&mTaskMutex);
+  mPendingReadbackTasks.AppendElement(task);
+  ::LeaveCriticalSection(&mTaskMutex);
+
+  ::ReleaseSemaphore(mTaskSemaphore, 1, NULL);
+}
+
+HRESULT
+ReadbackManagerD3D10::QueryInterface(REFIID riid, void **ppvObject)
+{
+  if (!ppvObject) {
+    return E_POINTER;
+  }
+
+  if (riid == IID_IUnknown) {
+    *ppvObject = this;
+  } else {
+    return E_NOINTERFACE;
+  }
+
+  return S_OK;
+}
+
+ULONG
+ReadbackManagerD3D10::AddRef()
+{
+  NS_ASSERTION(NS_IsMainThread(),
+    "ReadbackManagerD3D10 should only be refcounted on main thread.");
+  return ++mRefCnt;
+}
+
+ULONG
+ReadbackManagerD3D10::Release()
+{
+  NS_ASSERTION(NS_IsMainThread(),
+    "ReadbackManagerD3D10 should only be refcounted on main thread.");
+  ULONG newRefCnt = --mRefCnt;
+  if (!newRefCnt) {
+    mRefCnt++;
+    delete this;
+  }
+  return newRefCnt;
+}
+
+void
+ReadbackManagerD3D10::ProcessTasks()
+{
+  HANDLE handles[] = { mTaskSemaphore, mShutdownEvent };
+  
+  while (true) {
+    DWORD result = ::WaitForMultipleObjects(2, handles, FALSE, INFINITE);
+    if (result != WAIT_OBJECT_0) {
+      return;
+    }
+
+    ::EnterCriticalSection(&mTaskMutex);
+    ReadbackTask *nextReadbackTask = mPendingReadbackTasks[0].forget();
+    mPendingReadbackTasks.RemoveElementAt(0);
+    ::LeaveCriticalSection(&mTaskMutex);
+
+    // We want to block here until the texture contents are available, the
+    // easiest thing is to simply map and unmap.
+    D3D10_MAPPED_TEXTURE2D mappedTex;
+    nextReadbackTask->mReadbackTexture->Map(0, D3D10_MAP_READ, 0, &mappedTex);
+    nextReadbackTask->mReadbackTexture->Unmap(0);
+
+    // We can only send the update to the sink on the main thread, so post an
+    // event there to do so. Ownership of the task is passed from
+    // mPendingReadbackTasks to ReadbackResultWriter here.
+    nsCOMPtr<nsIThread> thread = do_GetMainThread();
+    thread->Dispatch(new ReadbackResultWriter(nextReadbackTask),
+                     nsIEventTarget::DISPATCH_NORMAL);
+  }
+}
+
+}
+}
new file mode 100644
--- /dev/null
+++ b/gfx/layers/d3d10/ReadbackManagerD3D10.h
@@ -0,0 +1,107 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * ***** 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 Corporation code.
+ *
+ * The Initial Developer of the Original Code is Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Bas Schouten <bschouten@mozilla.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 GFX_READBACKMANAGERD3D10_H
+#define GFX_READBACKMANAGERD3D10_H
+
+#include <windows.h>
+#include <d3d10_1.h>
+
+#include "nsTArray.h"
+#include "nsAutoPtr.h"
+#include "gfxPoint.h"
+
+namespace mozilla {
+namespace layers {
+
+DWORD WINAPI StartTaskThread(void *aManager);
+
+struct ReadbackTask;
+
+class ReadbackManagerD3D10 : public IUnknown
+{
+public:
+  ReadbackManagerD3D10();
+  ~ReadbackManagerD3D10();
+
+  /**
+   * Tell the readback manager to post a readback task.
+   *
+   * @param aTexture D3D10_USAGE_STAGING texture that will contain the data that
+   *                 was readback.
+   * @param aUpdate  ReadbackProcessor::Update object. This is a void pointer
+   *                 since we cannot forward declare a nested class, and do not
+   *                 export ReadbackProcessor.h
+   * @param aOrigin  Origin of the aTexture surface in the ThebesLayer
+   *                 coordinate system.
+   */
+  void PostTask(ID3D10Texture2D *aTexture, void *aUpdate, const gfxPoint &aOrigin);
+
+  virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid,
+                                                   void **ppvObject);
+  virtual ULONG STDMETHODCALLTYPE AddRef(void);
+  virtual ULONG STDMETHODCALLTYPE Release(void);
+
+private:
+  friend DWORD WINAPI StartTaskThread(void *aManager);
+
+  void ProcessTasks();
+
+  // The invariant maintained by |mTaskSemaphore| is that the readback thread
+  // will awaken from WaitForMultipleObjects() at least once per readback 
+  // task enqueued by the main thread.  Since the readback thread processes
+  // exactly one task per wakeup (with one exception), no tasks are lost.  The
+  // exception is when the readback thread is shut down, which orphans the
+  // remaining tasks, on purpose.
+  HANDLE mTaskSemaphore;
+  // Event signaled when the task thread should shutdown
+  HANDLE mShutdownEvent;
+  // Handle to the task thread
+  HANDLE mTaskThread;
+
+  // FiFo list of readback tasks that are to be executed. Access is synchronized
+  // by mTaskMutex.
+  CRITICAL_SECTION mTaskMutex;
+  nsTArray<nsAutoPtr<ReadbackTask>> mPendingReadbackTasks;
+
+  ULONG mRefCnt;
+};
+
+}
+}
+
+#endif /* GFX_READBACKMANAGERD3D10_H */
\ No newline at end of file
--- a/gfx/layers/d3d10/ThebesLayerD3D10.cpp
+++ b/gfx/layers/d3d10/ThebesLayerD3D10.cpp
@@ -40,16 +40,18 @@
 
 #include "gfxWindowsPlatform.h"
 #ifdef CAIRO_HAS_D2D_SURFACE
 #include "gfxD2DSurface.h"
 #endif
 
 #include "gfxTeeSurface.h"
 #include "gfxUtils.h"
+#include "ReadbackLayer.h"
+#include "ReadbackProcessor.h"
 
 namespace mozilla {
 namespace layers {
 
 ThebesLayerD3D10::ThebesLayerD3D10(LayerManagerD3D10 *aManager)
   : ThebesLayer(aManager, NULL)
   , LayerD3D10(aManager)
 {
@@ -163,17 +165,17 @@ ThebesLayerD3D10::RenderLayer()
   }
 
   // Set back to default.
   effect()->GetVariableByName("vTextureCoords")->AsVector()->
     SetFloatVector(ShaderConstantRectD3D10(0, 0, 1.0f, 1.0f));
 }
 
 void
-ThebesLayerD3D10::Validate()
+ThebesLayerD3D10::Validate(ReadbackProcessor *aReadback)
 {
   if (mVisibleRegion.IsEmpty()) {
     return;
   }
 
   SurfaceMode mode = GetSurfaceMode();
   if (mode == SURFACE_COMPONENT_ALPHA &&
       (!mParent || !mParent->SupportsComponentAlphaChildren())) {
@@ -186,16 +188,22 @@ ThebesLayerD3D10::Validate()
   GetDesiredResolutions(xres, yres);
 
   // If our resolution changed, we need new sized textures, delete the old ones.
   if (ResolutionChanged(xres, yres)) {
       mTexture = nsnull;
       mTextureOnWhite = nsnull;
   }
 
+  nsTArray<ReadbackProcessor::Update> readbackUpdates;
+  nsIntRegion readbackRegion;
+  if (aReadback && UsedForReadback()) {
+    aReadback->GetThebesLayerUpdates(this, &readbackUpdates, &readbackRegion);
+  }
+
   nsIntRect visibleRect = mVisibleRegion.GetBounds();
 
   if (mTexture) {
     if (!mTextureRegion.IsEqual(mVisibleRegion)) {
       nsRefPtr<ID3D10Texture2D> oldTexture = mTexture;
       mTexture = nsnull;
       nsRefPtr<ID3D10Texture2D> oldTextureOnWhite = mTextureOnWhite;
       mTextureOnWhite = nsnull;
@@ -259,16 +267,33 @@ ThebesLayerD3D10::Validate()
      * This is an issue for opaque surfaces, which otherwise won't get their
      * background painted.
      */
     nsIntRegion region;
     region.Sub(mVisibleRegion, mValidRegion);
 
     DrawRegion(region, mode);
 
+    if (readbackUpdates.Length() > 0) {
+      CD3D10_TEXTURE2D_DESC desc(DXGI_FORMAT_B8G8R8A8_UNORM,
+                                 visibleRect.width, visibleRect.height,
+                                 1, 1, 0, D3D10_USAGE_STAGING,
+                                 D3D10_CPU_ACCESS_READ);
+
+      nsRefPtr<ID3D10Texture2D> readbackTexture;
+      device()->CreateTexture2D(&desc, NULL, getter_AddRefs(readbackTexture));
+      device()->CopyResource(readbackTexture, mTexture);
+
+      for (int i = 0; i < readbackUpdates.Length(); i++) {
+        mD3DManager->readbackManager()->PostTask(readbackTexture,
+                                                 &readbackUpdates[i],
+                                                 gfxPoint(visibleRect.x, visibleRect.y));
+      }
+    }
+
     mValidRegion = mVisibleRegion;
   }
 }
 
 void
 ThebesLayerD3D10::LayerManagerDestroyed()
 {
   mD3DManager = nsnull;
--- a/gfx/layers/d3d10/ThebesLayerD3D10.h
+++ b/gfx/layers/d3d10/ThebesLayerD3D10.h
@@ -46,23 +46,25 @@ namespace layers {
 
 class ThebesLayerD3D10 : public ThebesLayer,
                          public LayerD3D10
 {
 public:
   ThebesLayerD3D10(LayerManagerD3D10 *aManager);
   virtual ~ThebesLayerD3D10();
 
+  void Validate(ReadbackProcessor *aReadback);
+
   /* ThebesLayer implementation */
   void InvalidateRegion(const nsIntRegion& aRegion);
 
   /* LayerD3D10 implementation */
   virtual Layer* GetLayer();
   virtual void RenderLayer();
-  virtual void Validate();
+  virtual void Validate() { Validate(nsnull); }
   virtual void LayerManagerDestroyed();
 
 private:
   /* Texture with our surface data */
   nsRefPtr<ID3D10Texture2D> mTexture;
 
   /* Shader resource view for our texture */
   nsRefPtr<ID3D10ShaderResourceView> mSRView;
--- a/gfx/layers/d3d9/CanvasLayerD3D9.h
+++ b/gfx/layers/d3d9/CanvasLayerD3D9.h
@@ -31,18 +31,18 @@
  * 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 GFX_CANVASLAYEROGL_H
-#define GFX_CANVASLAYEROGL_H
+#ifndef GFX_CANVASLAYERD3D9_H
+#define GFX_CANVASLAYERD3D9_H
 
 #include "LayerManagerD3D9.h"
 #include "GLContext.h"
 #include "gfxASurface.h"
 
 namespace mozilla {
 namespace layers {
 
--- a/gfx/layers/d3d9/ContainerLayerD3D9.cpp
+++ b/gfx/layers/d3d9/ContainerLayerD3D9.cpp
@@ -33,16 +33,18 @@
  * 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 "ContainerLayerD3D9.h"
 #include "gfxUtils.h"
 #include "nsRect.h"
+#include "ThebesLayerD3D9.h"
+#include "ReadbackProcessor.h"
 
 namespace mozilla {
 namespace layers {
 
 ContainerLayerD3D9::ContainerLayerD3D9(LayerManagerD3D9 *aManager)
   : ContainerLayer(aManager, NULL)
   , LayerD3D9(aManager)
 {
@@ -66,31 +68,33 @@ ContainerLayerD3D9::InsertAfter(Layer* a
     aChild->SetNextSibling(oldFirstChild);
     aChild->SetPrevSibling(nsnull);
     if (oldFirstChild) {
       oldFirstChild->SetPrevSibling(aChild);
     } else {
       mLastChild = aChild;
     }
     NS_ADDREF(aChild);
+    DidInsertChild(aChild);
     return;
   }
   for (Layer *child = GetFirstChild();
        child; child = child->GetNextSibling()) {
     if (aAfter == child) {
       Layer *oldNextSibling = child->GetNextSibling();
       child->SetNextSibling(aChild);
       aChild->SetNextSibling(oldNextSibling);
       if (oldNextSibling) {
         oldNextSibling->SetPrevSibling(aChild);
       } else {
         mLastChild = aChild;
       }
       aChild->SetPrevSibling(child);
       NS_ADDREF(aChild);
+      DidInsertChild(aChild);
       return;
     }
   }
   NS_WARNING("Failed to find aAfter layer!");
 }
 
 void
 ContainerLayerD3D9::RemoveChild(Layer *aChild)
@@ -100,16 +104,17 @@ ContainerLayerD3D9::RemoveChild(Layer *a
     if (mFirstChild) {
       mFirstChild->SetPrevSibling(nsnull);
     } else {
       mLastChild = nsnull;
     }
     aChild->SetNextSibling(nsnull);
     aChild->SetPrevSibling(nsnull);
     aChild->SetParent(nsnull);
+    DidRemoveChild(aChild);
     NS_RELEASE(aChild);
     return;
   }
   Layer *lastChild = nsnull;
   for (Layer *child = GetFirstChild(); child;
        child = child->GetNextSibling()) {
     if (child == aChild) {
       // We're sure this is not our first child. So lastChild != NULL.
@@ -117,16 +122,17 @@ ContainerLayerD3D9::RemoveChild(Layer *a
       if (child->GetNextSibling()) {
         child->GetNextSibling()->SetPrevSibling(lastChild);
       } else {
         mLastChild = lastChild;
       }
       child->SetNextSibling(nsnull);
       child->SetPrevSibling(nsnull);
       child->SetParent(nsnull);
+      DidRemoveChild(aChild);
       NS_RELEASE(aChild);
       return;
     }
     lastChild = child;
   }
 }
 
 Layer*
@@ -170,16 +176,19 @@ ContainerLayerD3D9::RenderLayer()
   nsRefPtr<IDirect3DTexture9> renderTexture;
   float previousRenderTargetOffset[4];
   RECT containerClipRect;
   float renderTargetOffset[] = { 0, 0, 0, 0 };
   float oldViewMatrix[4][4];
 
   device()->GetScissorRect(&containerClipRect);
 
+  ReadbackProcessor readback;
+  readback.BuildUpdates(this);
+
   nsIntRect visibleRect = mVisibleRegion.GetBounds();
   PRBool useIntermediate = UseIntermediateSurface();
 
   mSupportsComponentAlphaChildren = PR_FALSE;
   gfxMatrix contTransform;
   if (useIntermediate) {
     device()->GetRenderTarget(0, getter_AddRefs(previousRenderTarget));
     device()->CreateTexture(visibleRect.width, visibleRect.height, 1,
@@ -310,17 +319,21 @@ ContainerLayerD3D9::RenderLayer()
         r.top = NS_MAX<LONG>(0, r.top);
       }
       r.bottom = NS_MIN<LONG>(r.bottom, desc.Height);
       r.right = NS_MIN<LONG>(r.right, desc.Width);
 
       device()->SetScissorRect(&r);
     }
 
-    layerToRender->RenderLayer();
+    if (layerToRender->GetLayer()->GetType() == TYPE_THEBES) {
+      static_cast<ThebesLayerD3D9*>(layerToRender)->RenderThebesLayer(&readback);
+    } else {
+      layerToRender->RenderLayer();
+    }
 
     if (clipRect && !useIntermediate) {
       // In this situation we've set a new scissor rect and we will continue
       // to render directly to our container. We need to restore its scissor.
       // Not setting this when useIntermediate is true is an optimization since
       // we'll get a new one set anyway.
       device()->SetScissorRect(&containerClipRect);
     }
--- a/gfx/layers/d3d9/LayerManagerD3D9.cpp
+++ b/gfx/layers/d3d9/LayerManagerD3D9.cpp
@@ -37,16 +37,17 @@
 
 #include "LayerManagerD3D9.h"
 
 #include "ThebesLayerD3D9.h"
 #include "ContainerLayerD3D9.h"
 #include "ImageLayerD3D9.h"
 #include "ColorLayerD3D9.h"
 #include "CanvasLayerD3D9.h"
+#include "ReadbackLayerD3D9.h"
 #include "gfxWindowsPlatform.h"
 #include "nsIGfxInfo.h"
 #include "nsServiceManagerUtils.h"
 #include "nsIPrefService.h"
 #include "nsIPrefBranch2.h"
 
 namespace mozilla {
 namespace layers {
@@ -223,16 +224,23 @@ LayerManagerD3D9::CreateColorLayer()
 
 already_AddRefed<CanvasLayer>
 LayerManagerD3D9::CreateCanvasLayer()
 {
   nsRefPtr<CanvasLayer> layer = new CanvasLayerD3D9(this);
   return layer.forget();
 }
 
+already_AddRefed<ReadbackLayer>
+LayerManagerD3D9::CreateReadbackLayer()
+{
+  nsRefPtr<ReadbackLayer> layer = new ReadbackLayerD3D9(this);
+  return layer.forget();
+}
+
 already_AddRefed<ImageContainer>
 LayerManagerD3D9::CreateImageContainer()
 {
   nsRefPtr<ImageContainer> container = new ImageContainerD3D9(device());
   return container.forget();
 }
 
 void ReleaseTexture(void *texture)
--- a/gfx/layers/d3d9/LayerManagerD3D9.h
+++ b/gfx/layers/d3d9/LayerManagerD3D9.h
@@ -144,16 +144,18 @@ public:
   virtual already_AddRefed<ContainerLayer> CreateContainerLayer();
 
   virtual already_AddRefed<ImageLayer> CreateImageLayer();
 
   virtual already_AddRefed<ColorLayer> CreateColorLayer();
 
   virtual already_AddRefed<CanvasLayer> CreateCanvasLayer();
 
+  virtual already_AddRefed<ReadbackLayer> CreateReadbackLayer();
+
   virtual already_AddRefed<ImageContainer> CreateImageContainer();
 
   virtual LayersBackend GetBackendType() { return LAYERS_D3D9; }
   virtual void GetBackendName(nsAString& name) { name.AssignLiteral("Direct3D 9"); }
   bool DeviceWasRemoved() { return deviceManager()->DeviceWasRemoved(); }
 
   /*
    * Helper methods.
new file mode 100644
--- /dev/null
+++ b/gfx/layers/d3d9/ReadbackLayerD3D9.h
@@ -0,0 +1,65 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * ***** 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 Corporation code.
+ *
+ * The Initial Developer of the Original Code is Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Robert O'Callahan <rocallahan@mozilla.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 GFX_READBACKLAYERD3D9_H
+#define GFX_READBACKLAYERD3D9_H
+
+#include "LayerManagerD3D9.h"
+#include "ReadbackLayer.h"
+
+namespace mozilla {
+namespace layers {
+
+class THEBES_API ReadbackLayerD3D9 :
+  public ReadbackLayer,
+  public LayerD3D9
+{
+public:
+    ReadbackLayerD3D9(LayerManagerD3D9 *aManager)
+    : ReadbackLayer(aManager, NULL),
+      LayerD3D9(aManager)
+  {
+      mImplData = static_cast<LayerD3D9*>(this);
+  }
+
+  virtual Layer* GetLayer() { return this; }
+  virtual void RenderLayer() {}
+};
+
+} /* layers */
+} /* mozilla */
+#endif /* GFX_READBACKLAYERD3D9_H */
--- a/gfx/layers/d3d9/ThebesLayerD3D9.cpp
+++ b/gfx/layers/d3d9/ThebesLayerD3D9.cpp
@@ -36,16 +36,17 @@
  * ***** END LICENSE BLOCK ***** */
 
 #include "ThebesLayerD3D9.h"
 #include "gfxPlatform.h"
 
 #include "gfxWindowsPlatform.h"
 #include "gfxTeeSurface.h"
 #include "gfxUtils.h"
+#include "ReadbackProcessor.h"
 
 namespace mozilla {
 namespace layers {
 
 ThebesLayerD3D9::ThebesLayerD3D9(LayerManagerD3D9 *aManager)
   : ThebesLayer(aManager, NULL)
   , LayerD3D9(aManager)
 {
@@ -196,17 +197,17 @@ ThebesLayerD3D9::RenderVisibleRegion()
         (float)iterRect->width / (float)mTextureRect.width,
         (float)iterRect->height / (float)mTextureRect.height), 1);
 
     device()->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2);
   }
 }
 
 void
-ThebesLayerD3D9::RenderLayer()
+ThebesLayerD3D9::RenderThebesLayer(ReadbackProcessor* aReadback)
 {
   if (mVisibleRegion.IsEmpty()) {
     return;
   }
 
   SurfaceMode mode = GetSurfaceMode();
   if (mode == SURFACE_COMPONENT_ALPHA &&
       (!mParent || !mParent->SupportsComponentAlphaChildren())) {
@@ -214,32 +215,40 @@ ThebesLayerD3D9::RenderLayer()
   }
   VerifyContentType(mode);
   UpdateTextures(mode);
   if (!HaveTextures(mode)) {
     NS_WARNING("Texture creation failed");
     return;
   }
 
-  if (!mValidRegion.IsEqual(mVisibleRegion)) {
+  nsTArray<ReadbackProcessor::Update> readbackUpdates;
+  nsIntRegion readbackRegion;
+  if (aReadback && UsedForReadback()) {
+    aReadback->GetThebesLayerUpdates(this, &readbackUpdates, &readbackRegion);
+  }
+
+  // Because updates to D3D9 ThebesLayers are rendered with the CPU, we don't
+  // have to do readback from D3D9 surfaces. Instead we make sure that any area
+  // needed for readback is included in the drawRegion we ask layout to render.
+  // Then the readback areas we need can be copied out of the temporary
+  // destinationSurface in DrawRegion.
+  nsIntRegion drawRegion;
+  drawRegion.Sub(mVisibleRegion, mValidRegion);
+  drawRegion.Or(drawRegion, readbackRegion);
+  // NS_ASSERTION(mVisibleRegion.Contains(region), "Bad readback region!");
+
+  if (!drawRegion.IsEmpty()) {
     LayerManagerD3D9::CallbackInfo cbInfo = mD3DManager->GetCallbackInfo();
     if (!cbInfo.Callback) {
       NS_ERROR("D3D9 should never need to update ThebesLayers in an empty transaction");
       return;
     }
 
-    /* We use the bounds of the visible region because we draw the bounds of
-     * this region when we draw this entire texture. We have to make sure that
-     * the areas that aren't filled with content get their background drawn.
-     * This is an issue for opaque surfaces, which otherwise won't get their
-     * background painted.
-     */
-    nsIntRegion region;
-    region.Sub(mVisibleRegion, mValidRegion);
-    DrawRegion(region, mode);
+    DrawRegion(drawRegion, mode, readbackUpdates);
 
     mValidRegion = mVisibleRegion;
   }
 
   SetShaderTransformAndOpacity();
 
   if (mode == SURFACE_COMPONENT_ALPHA) {
     mD3DManager->SetShaderMode(DeviceManagerD3D9::COMPONENTLAYERPASS1);
@@ -402,17 +411,18 @@ FillSurface(gfxASurface* aSurface, const
   ctx->Scale(aXRes, aYRes);
   ctx->Translate(-gfxPoint(aOffset.x, aOffset.y));
   gfxUtils::ClipToRegion(ctx, aRegion);
   ctx->SetColor(aColor);
   ctx->Paint();
 }
 
 void
-ThebesLayerD3D9::DrawRegion(nsIntRegion &aRegion, SurfaceMode aMode)
+ThebesLayerD3D9::DrawRegion(nsIntRegion &aRegion, SurfaceMode aMode,
+                            const nsTArray<ReadbackProcessor::Update>& aReadbackUpdates)
 {
   HRESULT hr;
   nsIntRect visibleRect = mVisibleRegion.GetBounds();
   float xres, yres;
   GetDesiredResolutions(xres, yres);
 
   nsRefPtr<gfxASurface> destinationSurface;
   nsIntRect bounds = aRegion.GetBounds();
@@ -475,16 +485,32 @@ ThebesLayerD3D9::DrawRegion(nsIntRegion 
   nsRefPtr<gfxContext> context = new gfxContext(destinationSurface);
   // Draw content scaled at our current resolution.
   context->Scale(xres, yres);
   context->Translate(gfxPoint(-bounds.x, -bounds.y));
   aRegion.ExtendForScaling(xres, yres);
   LayerManagerD3D9::CallbackInfo cbInfo = mD3DManager->GetCallbackInfo();
   cbInfo.Callback(this, context, aRegion, nsIntRegion(), cbInfo.CallbackData);
 
+  for (PRUint32 i = 0; i < aReadbackUpdates.Length(); ++i) {
+    NS_ASSERTION(aMode == SURFACE_OPAQUE,
+                 "Transparent surfaces should not be used for readback");
+    const ReadbackProcessor::Update& update = aReadbackUpdates[i];
+    nsIntPoint offset = update.mLayer->GetBackgroundLayerOffset();
+    nsRefPtr<gfxContext> ctx =
+        update.mLayer->GetSink()->BeginUpdate(update.mUpdateRect + offset,
+                                              update.mSequenceCounter);
+    if (ctx) {
+      ctx->Translate(gfxPoint(offset.x, offset.y));
+      ctx->SetSource(destinationSurface, gfxPoint(bounds.x, bounds.y));
+      ctx->Paint();
+      update.mLayer->GetSink()->EndUpdate(ctx, update.mUpdateRect + offset);
+    }
+  }
+
   nsAutoTArray<IDirect3DTexture9*,2> srcTextures;
   nsAutoTArray<IDirect3DTexture9*,2> destTextures;
   switch (aMode)
   {
     case SURFACE_OPAQUE:
       opaqueRenderer.End();
       srcTextures.AppendElement(opaqueRenderer.GetTexture());
       destTextures.AppendElement(mTexture);
--- a/gfx/layers/d3d9/ThebesLayerD3D9.h
+++ b/gfx/layers/d3d9/ThebesLayerD3D9.h
@@ -36,37 +36,42 @@
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef GFX_THEBESLAYERD3D9_H
 #define GFX_THEBESLAYERD3D9_H
 
 #include "Layers.h"
 #include "LayerManagerD3D9.h"
 #include "gfxImageSurface.h"
+#include "ReadbackProcessor.h"
 
 namespace mozilla {
 namespace layers {
 
+class ReadbackProcessor;
+
 class ThebesLayerD3D9 : public ThebesLayer,
                         public LayerD3D9
 {
 public:
   ThebesLayerD3D9(LayerManagerD3D9 *aManager);
   virtual ~ThebesLayerD3D9();
 
   /* ThebesLayer implementation */
   void InvalidateRegion(const nsIntRegion& aRegion);
 
   /* LayerD3D9 implementation */
   Layer* GetLayer();
   virtual PRBool IsEmpty();
-  virtual void RenderLayer();
+  virtual void RenderLayer() { RenderThebesLayer(nsnull); }
   virtual void CleanResources();
   virtual void LayerManagerDestroyed();
 
+  void RenderThebesLayer(ReadbackProcessor* aReadback);
+
 private:
   /*
    * D3D9 texture
    */
   nsRefPtr<IDirect3DTexture9> mTexture;
   /*
    * D3D9 texture for render-on-white when doing component alpha
    */
@@ -91,17 +96,18 @@ private:
   void UpdateTextures(SurfaceMode aMode);
 
   /* Render the rectangles of mVisibleRegion with D3D9 using the currently
    * bound textures, target, shaders, etc.
    */
   void RenderVisibleRegion();
 
   /* Have a region of our layer drawn */
-  void DrawRegion(nsIntRegion &aRegion, SurfaceMode aMode);
+  void DrawRegion(nsIntRegion &aRegion, SurfaceMode aMode,
+                  const nsTArray<ReadbackProcessor::Update>& aReadbackUpdates);
 
   /* Create a new texture */
   void CreateNewTextures(const gfxIntSize &aSize, SurfaceMode aMode);
 
   void CopyRegion(IDirect3DTexture9* aSrc, const nsIntPoint &aSrcOffset,
                   IDirect3DTexture9* aDest, const nsIntPoint &aDestOffset,
                   const nsIntRegion &aCopyRegion, nsIntRegion* aValidRegion,
                   float aXRes, float aYRes);
--- a/gfx/layers/opengl/ContainerLayerOGL.cpp
+++ b/gfx/layers/opengl/ContainerLayerOGL.cpp
@@ -52,31 +52,33 @@ ContainerInsertAfter(Container* aContain
     aChild->SetNextSibling(oldFirstChild);
     aChild->SetPrevSibling(nsnull);
     if (oldFirstChild) {
       oldFirstChild->SetPrevSibling(aChild);
     } else {
       aContainer->mLastChild = aChild;
     }
     NS_ADDREF(aChild);
+    aContainer->DidInsertChild(aChild);
     return;
   }
   for (Layer *child = aContainer->GetFirstChild(); 
        child; child = child->GetNextSibling()) {
     if (aAfter == child) {
       Layer *oldNextSibling = child->GetNextSibling();
       child->SetNextSibling(aChild);
       aChild->SetNextSibling(oldNextSibling);
       if (oldNextSibling) {
         oldNextSibling->SetPrevSibling(aChild);
       } else {
         aContainer->mLastChild = aChild;
       }
       aChild->SetPrevSibling(child);
       NS_ADDREF(aChild);
+      aContainer->DidInsertChild(aChild);
       return;
     }
   }
   NS_WARNING("Failed to find aAfter layer!");
 }
 
 template<class Container>
 static void
@@ -87,16 +89,17 @@ ContainerRemoveChild(Container* aContain
     if (aContainer->mFirstChild) {
       aContainer->mFirstChild->SetPrevSibling(nsnull);
     } else {
       aContainer->mLastChild = nsnull;
     }
     aChild->SetNextSibling(nsnull);
     aChild->SetPrevSibling(nsnull);
     aChild->SetParent(nsnull);
+    aContainer->DidRemoveChild(aChild);
     NS_RELEASE(aChild);
     return;
   }
   Layer *lastChild = nsnull;
   for (Layer *child = aContainer->GetFirstChild(); child; 
        child = child->GetNextSibling()) {
     if (child == aChild) {
       // We're sure this is not our first child. So lastChild != NULL.
@@ -104,16 +107,17 @@ ContainerRemoveChild(Container* aContain
       if (child->GetNextSibling()) {
         child->GetNextSibling()->SetPrevSibling(lastChild);
       } else {
         aContainer->mLastChild = lastChild;
       }
       child->SetNextSibling(nsnull);
       child->SetPrevSibling(nsnull);
       child->SetParent(nsnull);
+      aContainer->DidRemoveChild(aChild);
       NS_RELEASE(aChild);
       return;
     }
     lastChild = child;
   }
 }
 
 template<class Container>
--- a/gfx/thebes/gfxAlphaRecovery.h
+++ b/gfx/thebes/gfxAlphaRecovery.h
@@ -36,43 +36,72 @@
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef _GFXALPHARECOVERY_H_
 #define _GFXALPHARECOVERY_H_
 
 #include "gfxContext.h"
 #include "gfxImageSurface.h"
 #include "mozilla/SSE.h"
+#include "nsRect.h"
 
 class THEBES_API gfxAlphaRecovery {
 public:
     struct Analysis {
         PRBool uniformColor;
         PRBool uniformAlpha;
         gfxFloat alpha;
         gfxFloat r, g, b;
     };
 
+    /**
+     * Some SIMD fast-paths only can be taken if the relative
+     * byte-alignment of images' pointers and strides meets certain
+     * criteria.  Aligning image pointers and strides by
+     * |GoodAlignmentLog2()| below will ensure that fast-paths aren't
+     * skipped because of misalignment.  Fast-paths may still be taken
+     * even if GoodAlignmentLog2() is not met, in some conditions.
+     */
+    static PRUint32 GoodAlignmentLog2() { return 4; /* for SSE2 */ }
+
     /* Given two surfaces of equal size with the same rendering, one onto a
      * black background and the other onto white, recovers alpha values from
      * the difference and sets the alpha values on the black surface.
      * The surfaces must have format RGB24 or ARGB32.
      * Returns PR_TRUE on success.
      */
     static PRBool RecoverAlpha (gfxImageSurface *blackSurface,
                                 const gfxImageSurface *whiteSurface,
                                 Analysis *analysis = nsnull);
 
 #ifdef MOZILLA_MAY_SUPPORT_SSE2
-    /* This does the save as the previous function, but uses SSE2
+    /* This does the same as the previous function, but uses SSE2
      * optimizations. Usually this should not be called directly.  Be sure to
      * check mozilla::supports_sse2() before calling this function.
      */
     static PRBool RecoverAlphaSSE2 (gfxImageSurface *blackSurface,
                                     const gfxImageSurface *whiteSurface);
+
+    /**
+     * A common use-case for alpha recovery is to paint into a
+     * temporary "white image", then paint onto a subrect of the
+     * surface, the "black image", into which alpha-recovered pixels
+     * are eventually to be written.  This function returns a rect
+     * aligned so that recovering alpha for that rect will hit SIMD
+     * fast-paths, if possible.  It's not always possible to align
+     * |aRect| so that fast-paths will be taken.
+     *
+     * The returned rect is always a superset of |aRect|.
+     */
+    static nsIntRect AlignRectForSubimageRecovery(const nsIntRect& aRect,
+                                                  gfxImageSurface* aSurface);
+#else
+    static nsIntRect AlignRectForSubimageRecovery(const nsIntRect& aRect,
+                                                  gfxImageSurface*)
+    { return aRect; }
 #endif
 
     /** from cairo-xlib-utils.c, modified */
     /**
      * Given the RGB data for two image surfaces, one a source image composited
      * with OVER onto a black background, and one a source image composited with 
      * OVER onto a white background, reconstruct the original image data into
      * black_data.
--- a/gfx/thebes/gfxAlphaRecoverySSE2.cpp
+++ b/gfx/thebes/gfxAlphaRecoverySSE2.cpp
@@ -155,8 +155,104 @@ gfxAlphaRecovery::RecoverAlphaSSE2(gfxIm
         blackData += blackSurf->Stride() - j * 4;
         whiteData += whiteSurf->Stride() - j * 4;
     }
 
     blackSurf->MarkDirty();
 
     return PR_TRUE;
 }
+
+static PRInt32
+ByteAlignment(PRInt32 aAlignToLog2, PRInt32 aX, PRInt32 aY=0, PRInt32 aStride=1)
+{
+    return (aX + aStride * aY) & ((1 << aAlignToLog2) - 1);
+}
+
+/*static*/ nsIntRect
+gfxAlphaRecovery::AlignRectForSubimageRecovery(const nsIntRect& aRect,
+                                               gfxImageSurface* aSurface)
+{
+    NS_ASSERTION(gfxASurface::ImageFormatARGB32 == aSurface->Format(),
+                 "Thebes grew support for non-ARGB32 COLOR_ALPHA?");
+    static const PRInt32 kByteAlignLog2 = GoodAlignmentLog2();
+    static const PRInt32 bpp = 4;
+    static const PRInt32 pixPerAlign = (1 << kByteAlignLog2) / bpp;
+    //
+    // We're going to create a subimage of the surface with size
+    // <sw,sh> for alpha recovery, and want a SIMD fast-path.  The
+    // rect <x,y, w,h> /needs/ to be redrawn, but it might not be
+    // properly aligned for SIMD.  So we want to find a rect <x',y',
+    // w',h'> that's a superset of what needs to be redrawn but is
+    // properly aligned.  Proper alignment is
+    //
+    //   BPP * (x' + y' * sw) \cong 0         (mod ALIGN)
+    //   BPP * w'             \cong BPP * sw  (mod ALIGN)
+    //
+    // (We assume the pixel at surface <0,0> is already ALIGN'd.)
+    // That rect (obviously) has to fit within the surface bounds, and
+    // we should also minimize the extra pixels redrawn only for
+    // alignment's sake.  So we also want
+    //
+    //  minimize <x',y', w',h'>
+    //   0 <= x' <= x
+    //   0 <= y' <= y
+    //   w <= w' <= sw
+    //   h <= h' <= sh
+    //
+    // This is a messy integer non-linear programming problem, except
+    // ... we can assume that ALIGN/BPP is a very small constant.  So,
+    // brute force is viable.  The algorithm below will find a
+    // solution if one exists, but isn't guaranteed to find the
+    // minimum solution.  (For SSE2, ALIGN/BPP = 4, so it'll do at
+    // most 64 iterations below).  In what's likely the common case,
+    // an already-aligned rectangle, it only needs 1 iteration.
+    //
+    // Is this alignment worth doing?  Recovering alpha will take work
+    // proportional to w*h (assuming alpha recovery computation isn't
+    // memory bound).  This analysis can lead to O(w+h) extra work
+    // (with small constants).  In exchange, we expect to shave off a
+    // ALIGN/BPP constant by using SIMD-ized alpha recovery.  So as
+    // w*h diverges from w+h, the win factor approaches ALIGN/BPP.  We
+    // only really care about the w*h >> w+h case anyway; others
+    // should be fast enough even with the overhead.  (Unless the cost
+    // of repainting the expanded rect is high, but in that case
+    // SIMD-ized alpha recovery won't make a difference so this code
+    // shouldn't be called.)
+    //
+    gfxIntSize surfaceSize = aSurface->GetSize();
+    const PRInt32 stride = bpp * surfaceSize.width;
+    if (stride != aSurface->Stride()) {
+        NS_WARNING("Unexpected stride, falling back on slow alpha recovery");
+        return aRect;
+    }
+
+    const PRInt32 x = aRect.x, y = aRect.y, w = aRect.width, h = aRect.height;
+    const PRInt32 sw = surfaceSize.width, sh = surfaceSize.height;
+    const PRInt32 strideAlign = ByteAlignment(kByteAlignLog2, stride);
+
+    PRInt32 dx, dy, dw;
+    // The outer two loops search for an aligned top-left pixel
+    for (dy = 0; (dy < pixPerAlign) && (y - dy >= 0) && (h + dy <= sh); ++dy) {
+        for (dx = 0; (dx < pixPerAlign) && (x - dx >= 0); ++dx) {
+            if (0 != ByteAlignment(kByteAlignLog2,
+                                   bpp * (x - dx), y - dy, stride)) {
+                continue;
+            }
+            // The inner searches for an aligned stride
+            for (dw = 0; (dw < pixPerAlign) && (w + dw + dx <= sw); ++dw) {
+                if (strideAlign == ByteAlignment(kByteAlignLog2,
+                                                 bpp * (w + dw + dx))) {
+                    goto FOUND_SOLUTION;
+                }
+            }
+        }
+    }
+
+    // Didn't find a solution.
+    return aRect;
+
+FOUND_SOLUTION:
+    nsIntRect solution = nsIntRect(x - dx, y - dy, w + dw + dx, h + dy);
+    NS_ABORT_IF_FALSE(nsIntRect(0,0, sw,sh).Contains(solution),
+                      "'Solution' extends outside surface bounds!");
+    return solution;
+}
--- a/gfx/thebes/gfxColor.h
+++ b/gfx/thebes/gfxColor.h
@@ -238,16 +238,20 @@ struct THEBES_API gfxRGBA {
         // if aString[0] is a number, parse it loosely as hex
     }
 #endif
 
     bool operator==(const gfxRGBA& other) const
     {
         return r == other.r && g == other.g && b == other.b && a == other.a;
     }
+    bool operator!=(const gfxRGBA& other) const
+    {
+        return !(*this == other);
+    }
 
     /**
      * Returns this color value as a packed 32-bit integer. This reconstructs
      * the int32 based on the given colorType, always in the native byte order.
      *
      * Note: gcc 4.2.3 on at least Ubuntu (x86) does something strange with
      * (PRUint8)(c * 255.0) << x, where the result is different than
      * double d = c * 255.0; v = ((PRUint8) d) << x. 
--- a/gfx/thebes/gfxImageSurface.cpp
+++ b/gfx/thebes/gfxImageSurface.cpp
@@ -32,16 +32,17 @@
  * 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 "prmem.h"
 
+#include "gfxAlphaRecovery.h"
 #include "gfxImageSurface.h"
 
 #include "cairo.h"
 
 gfxImageSurface::gfxImageSurface()
   : mSize(0, 0),
     mOwnsData(PR_FALSE),
     mFormat(ImageFormatUnknown),
@@ -91,29 +92,48 @@ gfxImageSurface::InitWithData(unsigned c
     // cairo_image_surface_create_for_data can return a 'null' surface
     // in out of memory conditions. The gfxASurface::Init call checks
     // the surface it receives to see if there is an error with the
     // surface and handles it appropriately. That is why there is
     // no check here.
     Init(surface);
 }
 
+static void*
+TryAllocAlignedBytes(size_t aSize)
+{
+    // Use fallible allocators here
+#if defined(HAVE_POSIX_MEMALIGN) || defined(HAVE_JEMALLOC_POSIX_MEMALIGN)
+    void* ptr;
+    // Try to align for fast alpha recovery.  This should only help
+    // cairo too, can't hurt.
+    return moz_posix_memalign(&ptr,
+                              1 << gfxAlphaRecovery::GoodAlignmentLog2(),
+                              aSize) ?
+             nsnull : ptr;
+#else
+    // Oh well, hope that luck is with us in the allocator
+    return moz_malloc(aSize);
+#endif
+}
+
 gfxImageSurface::gfxImageSurface(const gfxIntSize& size, gfxImageFormat format) :
     mSize(size), mOwnsData(PR_FALSE), mData(nsnull), mFormat(format)
 {
     mStride = ComputeStride();
 
     if (!CheckSurfaceSize(size))
         return;
 
     // if we have a zero-sized surface, just leave mData nsnull
     if (mSize.height * mStride > 0) {
 
-        // Use the fallible allocator here
-        mData = (unsigned char *) moz_malloc(mSize.height * mStride);
+        // This can fail to allocate memory aligned as we requested,
+        // or it can fail to allocate any memory at all.
+        mData = (unsigned char *) TryAllocAlignedBytes(mSize.height * mStride);
         if (!mData)
             return;
         memset(mData, 0, mSize.height * mStride);
     }
 
     mOwnsData = PR_TRUE;
 
     cairo_surface_t *surface =
--- a/gfx/thebes/gfxPoint.h
+++ b/gfx/thebes/gfxPoint.h
@@ -55,16 +55,23 @@ struct THEBES_API gfxIntSize {
     void SizeTo(PRInt32 _width, PRInt32 _height) {width = _width; height = _height;}
 
     int operator==(const gfxIntSize& s) const {
         return ((width == s.width) && (height == s.height));
     }
     int operator!=(const gfxIntSize& s) const {
         return ((width != s.width) || (height != s.height));
     }
+    bool operator<(const gfxIntSize& s) const {
+        return (operator<=(s) &&
+                (width < s.width || height < s.height));
+    }
+    bool operator<=(const gfxIntSize& s) const {
+        return (width <= s.width) && (height <= s.height);
+    }
     gfxIntSize operator+(const gfxIntSize& s) const {
         return gfxIntSize(width + s.width, height + s.height);
     }
     gfxIntSize operator-() const {
         return gfxIntSize(- width, - height);
     }
     gfxIntSize operator-(const gfxIntSize& s) const {
         return gfxIntSize(width - s.width, height - s.height);
--- a/js/src/xpconnect/loader/mozJSSubScriptLoader.cpp
+++ b/js/src/xpconnect/loader/mozJSSubScriptLoader.cpp
@@ -318,21 +318,28 @@ mozJSSubScriptLoader::LoadSubScript (con
 
         // For file URIs prepend the filename with the filename of the
         // calling script, and " -> ". See bug 418356.
         nsCAutoString tmp(JS_GetScriptFilename(cx, script));
         tmp.AppendLiteral(" -> ");
         tmp.Append(uriStr);
 
         uriStr = tmp;
-    }        
-        
-    rv = NS_OpenURI(getter_AddRefs(instream), uri, serv,
-                    nsnull, nsnull, nsIRequest::LOAD_NORMAL,
-                    getter_AddRefs(chan));
+    }
+
+    // Instead of calling NS_OpenURI, we create the channel ourselves and call
+    // SetContentType, to avoid expensive MIME type lookups (bug 632490).
+    rv = NS_NewChannel(getter_AddRefs(chan), uri, serv,
+                       nsnull, nsnull, nsIRequest::LOAD_NORMAL);
+    if (NS_SUCCEEDED(rv))
+    {
+        chan->SetContentType(NS_LITERAL_CSTRING("application/javascript"));
+        rv = chan->Open(getter_AddRefs(instream));
+    }
+
     if (NS_FAILED(rv))
     {
         errmsg = JS_NewStringCopyZ (cx, LOAD_ERROR_NOSTREAM);
         goto return_exception;
     }
     
     rv = chan->GetContentLength (&len);
     if (NS_FAILED(rv) || len == -1)
--- a/layout/base/nsDisplayItemTypes.h
+++ b/layout/base/nsDisplayItemTypes.h
@@ -73,16 +73,17 @@ enum Type {
   TYPE_LIST_FOCUS,
   TYPE_OPACITY,
   TYPE_OPTION_EVENT_GRABBER,
   TYPE_OUTLINE,
   TYPE_OWN_LAYER,
   TYPE_PAGE_CONTENT,
   TYPE_PAGE_SEQUENCE,
   TYPE_PLUGIN,
+  TYPE_PLUGIN_READBACK,
   TYPE_PRINT_PREVIEW_BACKGROUND,
   TYPE_PRINT_PLUGIN,
   TYPE_REMOTE,
   TYPE_REMOTE_SHADOW,
   TYPE_SCROLL_LAYER,
   TYPE_SELECTION_OVERLAY,
   TYPE_SOLID_COLOR,
   TYPE_TABLE_CELL_BACKGROUND,
--- a/layout/base/nsDisplayList.cpp
+++ b/layout/base/nsDisplayList.cpp
@@ -370,17 +370,17 @@ nsDisplayList::GetBounds(nsDisplayListBu
 
 PRBool
 nsDisplayList::ComputeVisibilityForRoot(nsDisplayListBuilder* aBuilder,
                                         nsRegion* aVisibleRegion) {
   nsRegion r;
   r.And(*aVisibleRegion, GetBounds(aBuilder));
   PRBool notUsed;
   return ComputeVisibilityForSublist(aBuilder, aVisibleRegion,
-                                     r.GetBounds(), notUsed);
+                                     r.GetBounds(), r.GetBounds(), notUsed);
 }
 
 static nsRegion
 TreatAsOpaque(nsDisplayItem* aItem, nsDisplayListBuilder* aBuilder,
               PRBool* aTransparentBackground)
 {
   nsRegion opaque = aItem->GetOpaqueRegion(aBuilder, aTransparentBackground);
   if (aBuilder->IsForPluginGeometry()) {
@@ -392,16 +392,17 @@ TreatAsOpaque(nsDisplayItem* aItem, nsDi
   }
   return opaque;
 }
 
 PRBool
 nsDisplayList::ComputeVisibilityForSublist(nsDisplayListBuilder* aBuilder,
                                            nsRegion* aVisibleRegion,
                                            const nsRect& aListVisibleBounds,
+                                           const nsRect& aAllowVisibleRegionExpansion,
                                            PRBool& aContainsRootContentDocBG) {
 #ifdef DEBUG
   nsRegion r;
   r.And(*aVisibleRegion, GetBounds(aBuilder));
   NS_ASSERTION(r.GetBounds() == aListVisibleBounds,
                "bad aListVisibleBounds");
 #endif
   mVisibleRect = aListVisibleBounds;
@@ -424,17 +425,17 @@ nsDisplayList::ComputeVisibilityForSubli
 
     nsRect bounds = item->GetBounds(aBuilder);
 
     nsRegion itemVisible;
     itemVisible.And(*aVisibleRegion, bounds);
     item->mVisibleRect = itemVisible.GetBounds();
 
     PRBool containsRootContentDocBG = PR_FALSE;
-    if (item->ComputeVisibility(aBuilder, aVisibleRegion, 
+    if (item->ComputeVisibility(aBuilder, aVisibleRegion, aAllowVisibleRegionExpansion,
                                 containsRootContentDocBG)) {
       if (containsRootContentDocBG) {
         aContainsRootContentDocBG = PR_TRUE;
       }
       anyVisible = PR_TRUE;
       PRBool transparentBackground = PR_FALSE;
       nsRegion opaque = TreatAsOpaque(item, aBuilder, &transparentBackground);
       // Subtract opaque item from the visible region
@@ -739,18 +740,21 @@ void nsDisplayList::Sort(nsDisplayListBu
 PRBool nsDisplayItem::RecomputeVisibility(nsDisplayListBuilder* aBuilder,
                                           nsRegion* aVisibleRegion) {
   nsRect bounds = GetBounds(aBuilder);
 
   nsRegion itemVisible;
   itemVisible.And(*aVisibleRegion, bounds);
   mVisibleRect = itemVisible.GetBounds();
 
+  // When we recompute visibility within layers we don't need to
+  // expand the visible region for content behind plugins (the plugin
+  // is not in the layer).
   PRBool notUsed;
-  if (!ComputeVisibility(aBuilder, aVisibleRegion, notUsed))
+  if (!ComputeVisibility(aBuilder, aVisibleRegion, nsRect(), notUsed))
     return PR_FALSE;
 
   PRBool forceTransparentBackground;
   nsRegion opaque = TreatAsOpaque(this, aBuilder, &forceTransparentBackground);
   aBuilder->SubtractFromVisibleRegion(aVisibleRegion, opaque);
   return PR_TRUE;
 }
 
@@ -758,19 +762,21 @@ void nsDisplaySolidColor::Paint(nsDispla
                                 nsIRenderingContext* aCtx) {
   aCtx->SetColor(mColor);
   aCtx->FillRect(mVisibleRect);
 }
 
 PRBool
 nsDisplaySolidColor::ComputeVisibility(nsDisplayListBuilder* aBuilder,
                                        nsRegion* aVisibleRegion,
+                                       const nsRect& aAllowVisibleRegionExpansion,
                                        PRBool& aContainsRootContentDocBG)
 {
   PRBool retval = nsDisplayItem::ComputeVisibility(aBuilder, aVisibleRegion,
+                                                   aAllowVisibleRegionExpansion,
                                                    aContainsRootContentDocBG);
   if (retval && IsRootContentDocBackground()) {
     aContainsRootContentDocBG = PR_TRUE;
   }
   return retval;
 }
 
 static void
@@ -918,19 +924,21 @@ nsDisplayBackground::HitTest(nsDisplayLi
   }
 
   aOutFrames->AppendElement(mFrame);
 }
 
 PRBool
 nsDisplayBackground::ComputeVisibility(nsDisplayListBuilder* aBuilder,
                                        nsRegion* aVisibleRegion,
+                                       const nsRect& aAllowVisibleRegionExpansion,
                                        PRBool& aContainsRootContentDocBG)
 {
   if (!nsDisplayItem::ComputeVisibility(aBuilder, aVisibleRegion,
+                                        aAllowVisibleRegionExpansion,
                                         aContainsRootContentDocBG)) {
     return PR_FALSE;
   }
 
   // Return false if the background was propagated away from this
   // frame. We don't want this display item to show up and confuse
   // anything.
   nsStyleContext* bgSC;
@@ -1193,18 +1201,20 @@ nsDisplayOutline::Paint(nsDisplayListBui
                                mVisibleRect,
                                nsRect(offset, mFrame->GetSize()),
                                mFrame->GetStyleContext());
 }
 
 PRBool
 nsDisplayOutline::ComputeVisibility(nsDisplayListBuilder* aBuilder,
                                     nsRegion* aVisibleRegion,
+                                    const nsRect& aAllowVisibleRegionExpansion,
                                     PRBool& aContainsRootContentDocBG) {
   if (!nsDisplayItem::ComputeVisibility(aBuilder, aVisibleRegion,
+                                        aAllowVisibleRegionExpansion,
                                         aContainsRootContentDocBG)) {
     return PR_FALSE;
   }
 
   const nsStyleOutline* outline = mFrame->GetStyleOutline();
   nsRect borderBox(ToReferenceFrame(), mFrame->GetSize());
   if (borderBox.Contains(aVisibleRegion->GetBounds()) &&
       !nsLayoutUtils::HasNonZeroCorner(outline->mOutlineRadius)) {
@@ -1238,18 +1248,20 @@ nsDisplayCaret::Paint(nsDisplayListBuild
   // Note: Because we exist, we know that the caret is visible, so we don't
   // need to check for the caret's visibility.
   mCaret->PaintCaret(aBuilder, aCtx, mFrame, ToReferenceFrame());
 }
 
 PRBool
 nsDisplayBorder::ComputeVisibility(nsDisplayListBuilder* aBuilder,
                                    nsRegion* aVisibleRegion,
+                                   const nsRect& aAllowVisibleRegionExpansion,
                                    PRBool& aContainsRootContentDocBG) {
   if (!nsDisplayItem::ComputeVisibility(aBuilder, aVisibleRegion,
+                                        aAllowVisibleRegionExpansion,
                                         aContainsRootContentDocBG)) {
     return PR_FALSE;
   }
 
   nsRect paddingRect = mFrame->GetPaddingRect() - mFrame->GetPosition() +
     ToReferenceFrame();
   const nsStyleBorder *styleBorder;
   if (paddingRect.Contains(aVisibleRegion->GetBounds()) &&
@@ -1337,18 +1349,20 @@ nsDisplayBoxShadowOuter::Paint(nsDisplay
 nsRect
 nsDisplayBoxShadowOuter::GetBounds(nsDisplayListBuilder* aBuilder) {
   return mFrame->GetVisualOverflowRectRelativeToSelf() + ToReferenceFrame();
 }
 
 PRBool
 nsDisplayBoxShadowOuter::ComputeVisibility(nsDisplayListBuilder* aBuilder,
                                            nsRegion* aVisibleRegion,
+                                           const nsRect& aAllowVisibleRegionExpansion,
                                            PRBool& aContainsRootContentDocBG) {
   if (!nsDisplayItem::ComputeVisibility(aBuilder, aVisibleRegion,
+                                        aAllowVisibleRegionExpansion,
                                         aContainsRootContentDocBG)) {
     return PR_FALSE;
   }
 
   // Store the actual visible region
   mVisibleRegion.And(*aVisibleRegion, mVisibleRect);
 
   nsPoint origin = ToReferenceFrame();
@@ -1383,18 +1397,20 @@ nsDisplayBoxShadowInner::Paint(nsDisplay
                                         borderRect, rects[i]);
     aCtx->PopState();
   }
 }
 
 PRBool
 nsDisplayBoxShadowInner::ComputeVisibility(nsDisplayListBuilder* aBuilder,
                                            nsRegion* aVisibleRegion,
+                                           const nsRect& aAllowVisibleRegionExpansion,
                                            PRBool& aContainsRootContentDocBG) {
   if (!nsDisplayItem::ComputeVisibility(aBuilder, aVisibleRegion,
+                                        aAllowVisibleRegionExpansion,
                                         aContainsRootContentDocBG)) {
     return PR_FALSE;
   }
 
   // Store the actual visible region
   mVisibleRegion.And(*aVisibleRegion, mVisibleRect);
   return PR_TRUE;
 }
@@ -1424,19 +1440,21 @@ nsDisplayWrapList::HitTest(nsDisplayList
 nsRect
 nsDisplayWrapList::GetBounds(nsDisplayListBuilder* aBuilder) {
   return mList.GetBounds(aBuilder);
 }
 
 PRBool
 nsDisplayWrapList::ComputeVisibility(nsDisplayListBuilder* aBuilder,
                                      nsRegion* aVisibleRegion,
+                                     const nsRect& aAllowVisibleRegionExpansion,
                                      PRBool& aContainsRootContentDocBG) {
   return mList.ComputeVisibilityForSublist(aBuilder, aVisibleRegion,
                                            mVisibleRect,
+                                           aAllowVisibleRegionExpansion,
                                            aContainsRootContentDocBG);
 }
 
 nsRegion
 nsDisplayWrapList::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
                                    PRBool* aForceTransparentSurface) {
   if (aForceTransparentSurface) {
     *aForceTransparentSurface = PR_FALSE;
@@ -1613,31 +1631,34 @@ nsDisplayOpacity::GetLayerState(nsDispla
   nsIFrame* activeScrolledRoot =
     nsLayoutUtils::GetActiveScrolledRootFor(mFrame, nsnull);
   return !ChildrenCanBeInactive(aBuilder, aManager, mList, activeScrolledRoot)
       ? LAYER_ACTIVE : LAYER_INACTIVE;
 }
 
 PRBool nsDisplayOpacity::ComputeVisibility(nsDisplayListBuilder* aBuilder,
                                            nsRegion* aVisibleRegion,
+                                           const nsRect& aAllowVisibleRegionExpansion,
                                            PRBool& aContainsRootContentDocBG) {
   // Our children are translucent so we should not allow them to subtract
   // area from aVisibleRegion. We do need to find out what is visible under
   // our children in the temporary compositing buffer, because if our children
   // paint our entire bounds opaquely then we don't need an alpha channel in
   // the temporary compositing buffer.
   nsRect bounds = GetBounds(aBuilder);
   nsRegion visibleUnderChildren;
   visibleUnderChildren.And(*aVisibleRegion, bounds);
   // do not pass up the aContainsRootContentDocBG value because anything under
   // us is not opaque
   PRBool notUsed;
+  nsRect allowExpansion;
+  allowExpansion.IntersectRect(bounds, aAllowVisibleRegionExpansion);
   return
     nsDisplayWrapList::ComputeVisibility(aBuilder, &visibleUnderChildren,
-                                         notUsed);
+                                         allowExpansion, notUsed);
 }
 
 PRBool nsDisplayOpacity::TryMerge(nsDisplayListBuilder* aBuilder, nsDisplayItem* aItem) {
   if (aItem->GetType() != TYPE_OPACITY)
     return PR_FALSE;
   // items for the same content element should be merged into a single
   // compositing group
   // aItem->GetUnderlyingFrame() returns non-null because it's nsDisplayOpacity
@@ -1703,38 +1724,42 @@ nsDisplayScrollLayer::BuildLayer(nsDispl
   RecordFrameMetrics(mFrame, layer, mVisibleRect, viewport, scrollId);
 
   return layer.forget();
 }
 
 PRBool
 nsDisplayScrollLayer::ComputeVisibility(nsDisplayListBuilder* aBuilder,
                                         nsRegion* aVisibleRegion,
+                                        const nsRect& aAllowVisibleRegionExpansion,
                                         PRBool& aContainsRootContentDocBG)
 {
   nsPresContext* presContext = mFrame->PresContext();
   nsIPresShell* presShell = presContext->GetPresShell();
 
   if (presShell->UsingDisplayPort()) {
     // The visible region for the children may be much bigger than the hole we
     // are viewing the children from, so that the compositor process has enough
     // content to asynchronously pan while content is being refreshed.
 
     nsRegion childVisibleRegion = presShell->GetDisplayPort() + aBuilder->ToReferenceFrame(mViewportFrame);
 
     nsRect boundedRect;
     boundedRect.IntersectRect(childVisibleRegion.GetBounds(), mList.GetBounds(aBuilder));
+    nsRect allowExpansion;
+    allowExpansion.IntersectRect(allowExpansion, boundedRect);
     PRBool visible = mList.ComputeVisibilityForSublist(
-      aBuilder, &childVisibleRegion, boundedRect, aContainsRootContentDocBG);
+      aBuilder, &childVisibleRegion, boundedRect, allowExpansion, aContainsRootContentDocBG);
     mVisibleRect = boundedRect;
 
     return visible;
 
   } else {
     return nsDisplayOwnLayer::ComputeVisibility(aBuilder, aVisibleRegion,
+                                                aAllowVisibleRegionExpansion,
                                                 aContainsRootContentDocBG);
   }
 }
 
 #ifdef NS_BUILD_REFCNT_LOGGING
 nsDisplayScrollLayer::~nsDisplayScrollLayer()
 {
   MOZ_COUNT_DTOR(nsDisplayScrollLayer);
@@ -1775,23 +1800,27 @@ nsDisplayClip::~nsDisplayClip() {
 
 void nsDisplayClip::Paint(nsDisplayListBuilder* aBuilder,
                           nsIRenderingContext* aCtx) {
   NS_ERROR("nsDisplayClip should have been flattened away for painting");
 }
 
 PRBool nsDisplayClip::ComputeVisibility(nsDisplayListBuilder* aBuilder,
                                         nsRegion* aVisibleRegion,
+                                        const nsRect& aAllowVisibleRegionExpansion,
                                         PRBool& aContainsRootContentDocBG) {
   nsRegion clipped;
   clipped.And(*aVisibleRegion, mClip);
 
   nsRegion finalClipped(clipped);
+  nsRect allowExpansion;
+  allowExpansion.IntersectRect(mClip, aAllowVisibleRegionExpansion);
   PRBool anyVisible =
     nsDisplayWrapList::ComputeVisibility(aBuilder, &finalClipped,
+                                         allowExpansion,
                                          aContainsRootContentDocBG);
 
   nsRegion removed;
   removed.Sub(clipped, finalClipped);
   aBuilder->SubtractFromVisibleRegion(aVisibleRegion, removed,
                                       aContainsRootContentDocBG);
 
   return anyVisible;
@@ -1875,23 +1904,24 @@ nsDisplayClipRoundedRect::WrapWithClone(
   return new (aBuilder)
     nsDisplayClipRoundedRect(aBuilder, aItem->GetUnderlyingFrame(), aItem,
                              mClip, mRadii);
 }
 
 PRBool nsDisplayClipRoundedRect::ComputeVisibility(
                                     nsDisplayListBuilder* aBuilder,
                                     nsRegion* aVisibleRegion,
+                                    const nsRect& aAllowVisibleRegionExpansion,
                                     PRBool& aContainsRootContentDocBG)
 {
   nsRegion clipped;
   clipped.And(*aVisibleRegion, mClip);
 
   PRBool notUsed;
-  return nsDisplayWrapList::ComputeVisibility(aBuilder, &clipped, notUsed);
+  return nsDisplayWrapList::ComputeVisibility(aBuilder, &clipped, nsRect(), notUsed);
   // FIXME: Remove a *conservative* opaque region from aVisibleRegion
   // (like in nsDisplayClip::ComputeVisibility).
 }
 
 PRBool nsDisplayClipRoundedRect::TryMerge(nsDisplayListBuilder* aBuilder, nsDisplayItem* aItem)
 {
   if (aItem->GetType() != TYPE_CLIP_ROUNDED_RECT)
     return PR_FALSE;
@@ -1944,28 +1974,32 @@ void nsDisplayZoom::HitTest(nsDisplayLis
 void nsDisplayZoom::Paint(nsDisplayListBuilder* aBuilder,
                           nsIRenderingContext* aCtx)
 {
   mList.PaintForFrame(aBuilder, aCtx, mFrame, nsDisplayList::PAINT_DEFAULT);
 }
 
 PRBool nsDisplayZoom::ComputeVisibility(nsDisplayListBuilder *aBuilder,
                                         nsRegion *aVisibleRegion,
+                                        const nsRect& aAllowVisibleRegionExpansion,
                                         PRBool& aContainsRootContentDocBG)
 {
   // Convert the passed in visible region to our appunits.
   nsRegion visibleRegion =
     aVisibleRegion->ConvertAppUnitsRoundOut(mParentAPD, mAPD);
   nsRegion originalVisibleRegion = visibleRegion;
 
   nsRect transformedVisibleRect =
     mVisibleRect.ConvertAppUnitsRoundOut(mParentAPD, mAPD);
+  nsRect allowExpansion =
+    aAllowVisibleRegionExpansion.ConvertAppUnitsRoundIn(mParentAPD, mAPD);
   PRBool retval =
     mList.ComputeVisibilityForSublist(aBuilder, &visibleRegion,
                                       transformedVisibleRect,
+                                      allowExpansion,
                                       aContainsRootContentDocBG);
 
   nsRegion removed;
   // removed = originalVisibleRegion - visibleRegion
   removed.Sub(originalVisibleRegion, visibleRegion);
   // Convert removed region to parent appunits.
   removed = removed.ConvertAppUnitsRoundIn(mAPD, mParentAPD);
   // aVisibleRegion = aVisibleRegion - removed (modulo any simplifications
@@ -2153,16 +2187,17 @@ nsDisplayTransform::GetLayerState(nsDisp
                                              aManager, 
                                              *mStoredList.GetList(), 
                                              activeScrolledRoot)
       ? LAYER_ACTIVE : LAYER_INACTIVE;
 }
 
 PRBool nsDisplayTransform::ComputeVisibility(nsDisplayListBuilder *aBuilder,
                                              nsRegion *aVisibleRegion,
+                                             const nsRect& aAllowVisibleRegionExpansion,
                                              PRBool& aContainsRootContentDocBG)
 {
   /* As we do this, we need to be sure to
    * untransform the visible rect, since we want everything that's painting to
    * think that it's painting in its original rectangular coordinate space. */
   nsRegion untransformedVisible =
     UntransformRect(mVisibleRect, mFrame, ToReferenceFrame());
   // Call RecomputeVisiblity instead of ComputeVisibilty since
@@ -2435,30 +2470,31 @@ void nsDisplaySVGEffects::Paint(nsDispla
                                 nsIRenderingContext* aCtx)
 {
   nsSVGIntegrationUtils::PaintFramesWithEffects(aCtx,
           mEffectsFrame, mVisibleRect, aBuilder, &mList);
 }
 
 PRBool nsDisplaySVGEffects::ComputeVisibility(nsDisplayListBuilder* aBuilder,
                                               nsRegion* aVisibleRegion,
+                                              const nsRect& aAllowVisibleRegionExpansion,
                                               PRBool& aContainsRootContentDocBG) {
   nsPoint offset = aBuilder->ToReferenceFrame(mEffectsFrame);
   nsRect dirtyRect =
     nsSVGIntegrationUtils::GetRequiredSourceForInvalidArea(mEffectsFrame,
                                                            mVisibleRect - offset) +
     offset;
 
   // Our children may be made translucent or arbitrarily deformed so we should
   // not allow them to subtract area from aVisibleRegion.
   nsRegion childrenVisible(dirtyRect);
   nsRect r;
   r.IntersectRect(dirtyRect, mList.GetBounds(aBuilder));
   PRBool notUsed;
-  mList.ComputeVisibilityForSublist(aBuilder, &childrenVisible, r, notUsed);
+  mList.ComputeVisibilityForSublist(aBuilder, &childrenVisible, r, nsRect(), notUsed);
   return PR_TRUE;
 }
 
 PRBool nsDisplaySVGEffects::TryMerge(nsDisplayListBuilder* aBuilder, nsDisplayItem* aItem)
 {
   if (aItem->GetType() != TYPE_SVG_EFFECTS)
     return PR_FALSE;
   // items for the same content element should be merged into a single
--- a/layout/base/nsDisplayList.h
+++ b/layout/base/nsDisplayList.h
@@ -700,23 +700,27 @@ public:
    * nsDisplayList::ComputeVisibility automatically subtracts the region
    * returned by GetOpaqueRegion, and automatically removes items whose bounds
    * do not intersect the visible area, so implementations of
    * nsDisplayItem::ComputeVisibility do not need to do these things.
    * nsDisplayList::ComputeVisibility will already have set mVisibleRect on
    * this item to the intersection of *aVisibleRegion and this item's bounds.
    * We rely on that, so this should only be called by
    * nsDisplayList::ComputeVisibility or nsDisplayItem::RecomputeVisibility.
-   * 
+   * aAllowVisibleRegionExpansion is a rect where we are allowed to
+   * expand the visible region and is only used for making sure the
+   * background behind a plugin is visible.
+   *
    * @return PR_TRUE if the item is visible, PR_FALSE if no part of the item
    * is visible. aContainsRootContentDocBG is set to true if this item contains
    * the background for the root content document.
    */
   virtual PRBool ComputeVisibility(nsDisplayListBuilder* aBuilder,
                                    nsRegion* aVisibleRegion,
+                                   const nsRect& aAllowVisibleRegionExpansion,
                                    PRBool& aContainsRootContentDocBG)
   { return !mVisibleRect.IsEmpty(); }
 
   /**
    * Try to merge with the other item (which is below us in the display
    * list). This gets used by nsDisplayClip to coalesce clipping operations
    * (optimization), by nsDisplayOpacity to merge rendering for the same
    * content element into a single opacity group (correctness), and will be
@@ -988,16 +992,17 @@ public:
    * of aVisibleRegion and GetBounds() for this list.
    * @return true if any item in the list is visible. aContainsRootContentDocBG
    * is set to true if the list contains the background for a root content
    * document.
    */
   PRBool ComputeVisibilityForSublist(nsDisplayListBuilder* aBuilder,
                                      nsRegion* aVisibleRegion,
                                      const nsRect& aListVisibleBounds,
+                                     const nsRect& aAllowVisibleRegionExpansion,
                                      PRBool& aContainsRootContentDocBG);
 
   /**
    * As ComputeVisibilityForSublist, but computes visibility for a root
    * list (a list that does not belong to an nsDisplayItem).
    *
    * @param aVisibleRegion the area that is visible
    */
@@ -1394,16 +1399,17 @@ public:
     MOZ_COUNT_DTOR(nsDisplayBorder);
   }
 #endif
 
   virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder);
   virtual void Paint(nsDisplayListBuilder* aBuilder, nsIRenderingContext* aCtx);
   virtual PRBool ComputeVisibility(nsDisplayListBuilder* aBuilder,
                                    nsRegion* aVisibleRegion,
+                                   const nsRect& aAllowVisibleRegionExpansion,
                                    PRBool& aContainsRootContentDocBG);
   NS_DISPLAY_DECL_NAME("Border", TYPE_BORDER)
 
 protected:
   PRPackedBool mSnappingEnabled;
 };
 
 /**
@@ -1452,16 +1458,17 @@ public:
     *aColor = mColor;
     return PR_TRUE;
   }
 
   virtual void Paint(nsDisplayListBuilder* aBuilder, nsIRenderingContext* aCtx);
 
   virtual PRBool ComputeVisibility(nsDisplayListBuilder* aBuilder,
                                    nsRegion* aVisibleRegion,
+                                   const nsRect& aAllowVisibleRegionExpansion,
                                    PRBool& aContainsRootContentDocBG);
 
   PRBool IsRootContentDocBackground() {
     return mIsRootContentDocBackground;
   }
 
   NS_DISPLAY_DECL_NAME("SolidColor", TYPE_SOLID_COLOR)
 
@@ -1482,16 +1489,17 @@ public:
     MOZ_COUNT_DTOR(nsDisplayBackground);
   }
 #endif
 
   virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
                        HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames);
   virtual PRBool ComputeVisibility(nsDisplayListBuilder* aBuilder,
                                    nsRegion* aVisibleRegion,
+                                   const nsRect& aAllowVisibleRegionExpansion,
                                    PRBool& aContainsRootContentDocBG);
   virtual nsRegion GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
                                    PRBool* aForceTransparentSurface = nsnull);
   virtual PRBool IsVaryingRelativeToMovingFrame(nsDisplayListBuilder* aBuilder,
                                                 nsIFrame* aFrame);
   virtual PRBool IsUniform(nsDisplayListBuilder* aBuilder, nscolor* aColor);
   virtual PRBool IsFixedAndCoveringViewport(nsDisplayListBuilder* aBuilder);
   virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder);
@@ -1521,16 +1529,17 @@ public:
     MOZ_COUNT_DTOR(nsDisplayBoxShadowOuter);
   }
 #endif
 
   virtual void Paint(nsDisplayListBuilder* aBuilder, nsIRenderingContext* aCtx);
   virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder);
   virtual PRBool ComputeVisibility(nsDisplayListBuilder* aBuilder,
                                    nsRegion* aVisibleRegion,
+                                   const nsRect& aAllowVisibleRegionExpansion,
                                    PRBool& aContainsRootContentDocBG);
   NS_DISPLAY_DECL_NAME("BoxShadowOuter", TYPE_BOX_SHADOW_OUTER)
 
 private:
   nsRegion mVisibleRegion;
 };
 
 /**
@@ -1546,16 +1555,17 @@ public:
   virtual ~nsDisplayBoxShadowInner() {
     MOZ_COUNT_DTOR(nsDisplayBoxShadowInner);
   }
 #endif
 
   virtual void Paint(nsDisplayListBuilder* aBuilder, nsIRenderingContext* aCtx);
   virtual PRBool ComputeVisibility(nsDisplayListBuilder* aBuilder,
                                    nsRegion* aVisibleRegion,
+                                   const nsRect& aAllowVisibleRegionExpansion,
                                    PRBool& aContainsRootContentDocBG);
   NS_DISPLAY_DECL_NAME("BoxShadowInner", TYPE_BOX_SHADOW_INNER)
 
 private:
   nsRegion mVisibleRegion;
 };
 
 /**
@@ -1572,16 +1582,17 @@ public:
     MOZ_COUNT_DTOR(nsDisplayOutline);
   }
 #endif
 
   virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder);
   virtual void Paint(nsDisplayListBuilder* aBuilder, nsIRenderingContext* aCtx);
   virtual PRBool ComputeVisibility(nsDisplayListBuilder* aBuilder,
                                    nsRegion* aVisibleRegion,
+                                   const nsRect& aAllowVisibleRegionExpansion,
                                    PRBool& aContainsRootContentDocBG);
   NS_DISPLAY_DECL_NAME("Outline", TYPE_OUTLINE)
 };
 
 /**
  * A class that lets you receive events within the frame bounds but never paints.
  */
 class nsDisplayEventReceiver : public nsDisplayItem {
@@ -1634,16 +1645,17 @@ public:
   virtual nsRegion GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
                                    PRBool* aForceTransparentSurface = nsnull);
   virtual PRBool IsUniform(nsDisplayListBuilder* aBuilder, nscolor* aColor);
   virtual PRBool IsVaryingRelativeToMovingFrame(nsDisplayListBuilder* aBuilder,
                                                 nsIFrame* aFrame);
   virtual void Paint(nsDisplayListBuilder* aBuilder, nsIRenderingContext* aCtx);
   virtual PRBool ComputeVisibility(nsDisplayListBuilder* aBuilder,
                                    nsRegion* aVisibleRegion,
+                                   const nsRect& aAllowVisibleRegionExpansion,
                                    PRBool& aContainsRootContentDocBG);
   virtual PRBool TryMerge(nsDisplayListBuilder* aBuilder, nsDisplayItem* aItem) {
     NS_WARNING("This list should already have been flattened!!!");
     return PR_FALSE;
   }
   NS_DISPLAY_DECL_NAME("WrapList", TYPE_WRAP_LIST)
 
   virtual nsRect GetComponentAlphaBounds(nsDisplayListBuilder* aBuilder);
@@ -1719,16 +1731,17 @@ public:
   virtual nsRegion GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
                                    PRBool* aForceTransparentSurface = nsnull);
   virtual already_AddRefed<Layer> BuildLayer(nsDisplayListBuilder* aBuilder,
                                              LayerManager* aManager);
   virtual LayerState GetLayerState(nsDisplayListBuilder* aBuilder,
                                    LayerManager* aManager);
   virtual PRBool ComputeVisibility(nsDisplayListBuilder* aBuilder,
                                    nsRegion* aVisibleRegion,
+                                   const nsRect& aAllowVisibleRegionExpansion,
                                    PRBool& aContainsRootContentDocBG);  
   virtual PRBool TryMerge(nsDisplayListBuilder* aBuilder, nsDisplayItem* aItem);
   NS_DISPLAY_DECL_NAME("Opacity", TYPE_OPACITY)
 };
 
 /**
  * A display item that has no purpose but to ensure its contents get
  * their own layer.
@@ -1786,16 +1799,17 @@ public:
   virtual ~nsDisplayScrollLayer();
 #endif
 
   virtual already_AddRefed<Layer> BuildLayer(nsDisplayListBuilder* aBuilder,
                                              LayerManager* aManager);
 
   virtual PRBool ComputeVisibility(nsDisplayListBuilder* aBuilder,
                                    nsRegion* aVisibleRegion,
+                                   const nsRect& aAllowVisibleRegionExpansion,
                                    PRBool& aContainsRootContentDocBG);
 
 private:
   nsIFrame* mViewportFrame;
 };
 #endif
 
 /**
@@ -1817,16 +1831,17 @@ public:
 #ifdef NS_BUILD_REFCNT_LOGGING
   virtual ~nsDisplayClip();
 #endif
   
   virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder);
   virtual void Paint(nsDisplayListBuilder* aBuilder, nsIRenderingContext* aCtx);
   virtual PRBool ComputeVisibility(nsDisplayListBuilder* aBuilder,
                                    nsRegion* aVisibleRegion,
+                                   const nsRect& aAllowVisibleRegionExpansion,
                                    PRBool& aContainsRootContentDocBG);
   virtual PRBool TryMerge(nsDisplayListBuilder* aBuilder, nsDisplayItem* aItem);
   NS_DISPLAY_DECL_NAME("Clip", TYPE_CLIP)
   virtual PRUint32 GetPerFrameKey() { return 0; }
   
   const nsRect& GetClipRect() { return mClip; }
   void SetClipRect(const nsRect& aRect) { mClip = aRect; }
 
@@ -1859,16 +1874,17 @@ public:
 #endif
 
   virtual nsRegion GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
                                    PRBool* aForceTransparentSurface = nsnull);
   virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
                        HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames);
   virtual PRBool ComputeVisibility(nsDisplayListBuilder* aBuilder,
                                    nsRegion* aVisibleRegion,
+                                   const nsRect& aAllowVisibleRegionExpansion,
                                    PRBool& aContainsRootContentDocBG);
   virtual PRBool TryMerge(nsDisplayListBuilder* aBuilder, nsDisplayItem* aItem);
   NS_DISPLAY_DECL_NAME("ClipRoundedRect", TYPE_CLIP_ROUNDED_RECT)
 
   virtual nsDisplayWrapList* WrapWithClone(nsDisplayListBuilder* aBuilder,
                                            nsDisplayItem* aItem);
 
   void GetRadii(nscoord aRadii[8]) {
@@ -1900,16 +1916,17 @@ public:
 #endif
   
   virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder);
   virtual void Paint(nsDisplayListBuilder* aBuilder, nsIRenderingContext* aCtx);
   virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
                        HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames);
   virtual PRBool ComputeVisibility(nsDisplayListBuilder* aBuilder,
                                    nsRegion* aVisibleRegion,
+                                   const nsRect& aAllowVisibleRegionExpansion,
                                    PRBool& aContainsRootContentDocBG);
   NS_DISPLAY_DECL_NAME("Zoom", TYPE_ZOOM)
 
   // Get the app units per dev pixel ratio of the child document.
   PRInt32 GetChildAppUnitsPerDevPixel() { return mAPD; }
   // Get the app units per dev pixel ratio of the parent document.
   PRInt32 GetParentAppUnitsPerDevPixel() { return mParentAPD; }
 
@@ -1935,16 +1952,17 @@ public:
   virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
                        HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames);
   virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder) {
     return mBounds + aBuilder->ToReferenceFrame(mEffectsFrame);
   }
   virtual void Paint(nsDisplayListBuilder* aBuilder, nsIRenderingContext* aCtx);
   virtual PRBool ComputeVisibility(nsDisplayListBuilder* aBuilder,
                                    nsRegion* aVisibleRegion,
+                                   const nsRect& aAllowVisibleRegionExpansion,
                                    PRBool& aContainsRootContentDocBG);  
   virtual PRBool TryMerge(nsDisplayListBuilder* aBuilder, nsDisplayItem* aItem);
   NS_DISPLAY_DECL_NAME("SVGEffects", TYPE_SVG_EFFECTS)
 
   nsIFrame* GetEffectsFrame() { return mEffectsFrame; }
 
 private:
   nsIFrame* mEffectsFrame;
@@ -1999,16 +2017,17 @@ public:
                                    PRBool* aForceTransparentSurface = nsnull);
   virtual PRBool IsUniform(nsDisplayListBuilder *aBuilder, nscolor* aColor);
   virtual LayerState GetLayerState(nsDisplayListBuilder* aBuilder,
                                    LayerManager* aManager);
   virtual already_AddRefed<Layer> BuildLayer(nsDisplayListBuilder* aBuilder,
                                              LayerManager* aManager);
   virtual PRBool ComputeVisibility(nsDisplayListBuilder *aBuilder,
                                    nsRegion *aVisibleRegion,
+                                   const nsRect& aAllowVisibleRegionExpansion,
                                    PRBool& aContainsRootContentDocBG);
   virtual PRBool TryMerge(nsDisplayListBuilder *aBuilder, nsDisplayItem *aItem);
 
   /**
    * TransformRect takes in as parameters a rectangle (in aFrame's coordinate
    * space) and returns the smallest rectangle (in aFrame's coordinate space)
    * containing the transformed image of that rectangle.  That is, it takes
    * the four corners of the rectangle, transforms them according to the
--- a/layout/generic/nsCanvasFrame.h
+++ b/layout/generic/nsCanvasFrame.h
@@ -168,20 +168,22 @@ public:
   nsDisplayCanvasBackground(nsDisplayListBuilder* aBuilder, nsIFrame *aFrame)
     : nsDisplayBackground(aBuilder, aFrame)
   {
     mExtraBackgroundColor = NS_RGBA(0,0,0,0);
   }
 
   virtual PRBool ComputeVisibility(nsDisplayListBuilder* aBuilder,
                                    nsRegion* aVisibleRegion,
+                                   const nsRect& aAllowVisibleRegionExpansion,
                                    PRBool& aContainsRootContentDocBG)
   {
     PRBool retval = NS_GET_A(mExtraBackgroundColor) > 0 ||
            nsDisplayBackground::ComputeVisibility(aBuilder, aVisibleRegion,
+                                                  aAllowVisibleRegionExpansion,
                                                   aContainsRootContentDocBG);
     if (retval && mFrame->PresContext()->IsRootContentDocument()) {
       aContainsRootContentDocBG = PR_TRUE;
     }
     return retval;
   }
   virtual nsRegion GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
                                    PRBool* aForceTransparentSurface = nsnull)
--- a/layout/generic/nsObjectFrame.cpp
+++ b/layout/generic/nsObjectFrame.cpp
@@ -158,16 +158,17 @@ enum { XKeyPress = KeyPress };
 #ifdef XP_WIN
 #include "gfxWindowsNativeDrawing.h"
 #include "gfxWindowsSurface.h"
 #endif
 
 #include "gfxImageSurface.h"
 #include "gfxUtils.h"
 #include "Layers.h"
+#include "ReadbackLayer.h"
 
 // accessibility support
 #ifdef ACCESSIBILITY
 #include "nsAccessibilityService.h"
 #endif
 
 #ifdef MOZ_LOGGING
 #define FORCE_PR_LOG 1 /* Allow logging in the release build */
@@ -232,16 +233,18 @@ using mozilla::DefaultXDisplay;
 #ifdef PR_LOGGING 
 static PRLogModuleInfo *nsObjectFrameLM = PR_NewLogModule("nsObjectFrame");
 #endif /* PR_LOGGING */
 
 #if defined(XP_MACOSX) && !defined(NP_NO_CARBON)
 #define MAC_CARBON_PLUGINS
 #endif
 
+using namespace mozilla;
+using namespace mozilla::plugins;
 using namespace mozilla::layers;
 
 // special class for handeling DOM context menu events because for
 // some reason it starves other mouse events if implemented on the
 // same class
 class nsPluginDOMContextMenuListener : public nsIDOMContextMenuListener
 {
 public:
@@ -459,36 +462,51 @@ public:
                                      nsIDOMClientRect* position,
                                      nsIDOMClientRect* clip);
 #endif
 
   void NotifyPaintWaiter(nsDisplayListBuilder* aBuilder);
   // Return true if we set image with valid surface
   PRBool SetCurrentImage(ImageContainer* aContainer);
 
+  // Methods to update the background image we send to async plugins.
+  // The eventual target of these operations is PluginInstanceParent,
+  // but it takes several hops to get there.
+  void SetBackgroundUnknown();
+  already_AddRefed<gfxContext> BeginUpdateBackground(const nsIntRect& aRect);
+  void EndUpdateBackground(gfxContext* aContext, const nsIntRect& aRect);
+
   PRBool UseLayers()
   {
     PRBool useAsyncRendering;
     return (mInstance &&
             NS_SUCCEEDED(mInstance->UseAsyncPainting(&useAsyncRendering)) &&
             useAsyncRendering &&
             (!mPluginWindow ||
              mPluginWindow->type == NPWindowTypeDrawable));
   }
 
+
 private:
   // return FALSE if LayerSurface dirty (newly created and don't have valid plugin content yet)
   PRBool IsUpToDate()
   {
     nsRefPtr<gfxASurface> readyToUse;
     return NS_SUCCEEDED(mInstance->GetSurface(getter_AddRefs(readyToUse))) &&
            readyToUse && readyToUse->GetSize() == gfxIntSize(mPluginWindow->width,
                                                              mPluginWindow->height);
   }
 
+  already_AddRefed<nsIPluginInstance_MOZILLA_2_0_BRANCH>
+  GetInstance()
+  {
+    nsCOMPtr<nsIPluginInstance_MOZILLA_2_0_BRANCH> inst = do_QueryInterface(mInstance);
+    return inst.forget();
+  }
+
   void FixUpURLS(const nsString &name, nsAString &value);
 
   nsPluginNativeWindow       *mPluginWindow;
   nsCOMPtr<nsIPluginInstance> mInstance;
   nsObjectFrame              *mObjectFrame; // owns nsPluginInstanceOwner
   nsCOMPtr<nsIContent>        mContent;
   nsCString                   mDocumentBase;
   char                       *mTagText;
@@ -601,16 +619,63 @@ private:
   PRBool UpdateVisibility(PRBool aVisible);
 
 #endif
 
   nsRefPtr<gfxASurface> mLayerSurface;
   PRPackedBool          mWaitingForPaint;
 };
 
+class PluginBackgroundSink : public ReadbackSink {
+public:
+  PluginBackgroundSink(nsObjectFrame* aFrame, PRUint64 aStartSequenceNumber)
+    : mLastSequenceNumber(aStartSequenceNumber), mFrame(aFrame) {}
+  ~PluginBackgroundSink()
+  {
+    if (mFrame) {
+      mFrame->mBackgroundSink = nsnull;
+    }
+  }
+
+  virtual void SetUnknown(PRUint64 aSequenceNumber)
+  {
+    if (!AcceptUpdate(aSequenceNumber))
+      return;
+    mFrame->mInstanceOwner->SetBackgroundUnknown();
+  }
+
+  virtual already_AddRefed<gfxContext>
+      BeginUpdate(const nsIntRect& aRect, PRUint64 aSequenceNumber)
+  {
+    if (!AcceptUpdate(aSequenceNumber))
+      return nsnull;
+    return mFrame->mInstanceOwner->BeginUpdateBackground(aRect);
+  }
+
+  virtual void EndUpdate(gfxContext* aContext, const nsIntRect& aRect)
+  {
+    return mFrame->mInstanceOwner->EndUpdateBackground(aContext, aRect);
+  }
+
+  void Destroy() { mFrame = nsnull; }
+
+protected:
+  PRBool AcceptUpdate(PRUint64 aSequenceNumber) {
+    if (aSequenceNumber > mLastSequenceNumber && mFrame &&
+        mFrame->mInstanceOwner) {
+      mLastSequenceNumber = aSequenceNumber;
+      return PR_TRUE;
+    }
+    return PR_FALSE;
+  }
+
+  PRUint64 mLastSequenceNumber;
+  nsObjectFrame* mFrame;
+};
+
   // Mac specific code to fix up port position and clip
 #ifdef XP_MACOSX
 
   enum { ePluginPaintEnable, ePluginPaintDisable };
 
 #endif // XP_MACOSX
 
 nsObjectFrame::nsObjectFrame(nsStyleContext* aContext)
@@ -646,17 +711,16 @@ nsObjectFrame::CreateAccessible()
 NS_IMETHODIMP nsObjectFrame::GetPluginPort(HWND *aPort)
 {
   *aPort = (HWND) mInstanceOwner->GetPluginPortFromWidget();
   return NS_OK;
 }
 #endif
 #endif
 
-
 static NS_DEFINE_CID(kWidgetCID, NS_CHILD_CID);
 
 NS_IMETHODIMP 
 nsObjectFrame::Init(nsIContent*      aContent,
                     nsIFrame*        aParent,
                     nsIFrame*        aPrevInFlow)
 {
   NS_PRECONDITION(aContent, "How did that happen?");
@@ -684,16 +748,20 @@ nsObjectFrame::DestroyFrom(nsIFrame* aDe
 
   // StopPluginInternal might have disowned the widget; if it has,
   // mWidget will be null.
   if (mWidget) {
     mInnerView->DetachWidgetEventHandler(mWidget);
     mWidget->Destroy();
   }
 
+  if (mBackgroundSink) {
+    mBackgroundSink->Destroy();
+  }
+
   nsObjectFrameSuper::DestroyFrom(aDestructRoot);
 }
 
 /* virtual */ void
 nsObjectFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext)
 {
   if (HasView()) {
     nsIView* view = GetView();
@@ -1248,57 +1316,120 @@ nsObjectFrame::PaintPrintPlugin(nsIFrame
                                 const nsRect& aDirtyRect, nsPoint aPt)
 {
   nsPoint pt = aPt + aFrame->GetUsedBorderAndPadding().TopLeft();
   nsIRenderingContext::AutoPushTranslation translate(aCtx, pt.x, pt.y);
   // FIXME - Bug 385435: Doesn't aDirtyRect need translating too?
   static_cast<nsObjectFrame*>(aFrame)->PrintPlugin(*aCtx, aDirtyRect);
 }
 
-nsRect
-nsDisplayPlugin::GetBounds(nsDisplayListBuilder* aBuilder)
-{
-  nsRect r = mFrame->GetContentRect() - mFrame->GetPosition() +
-    ToReferenceFrame();
-  if (aBuilder->IsForPluginGeometry()) {
-    // Return the geometry we want, not the geometry we have (which is based
-    // on the surface the plugin last gave us)
-    return r;
-  }
-
-  nsObjectFrame* f = static_cast<nsObjectFrame*>(mFrame);
-  if (mozilla::LAYER_ACTIVE == f->GetLayerState(aBuilder, nsnull)) {
+class nsDisplayPluginReadback : public nsDisplayItem {
+public:
+  nsDisplayPluginReadback(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame)
+    : nsDisplayItem(aBuilder, aFrame)
+  {
+    MOZ_COUNT_CTOR(nsDisplayPluginReadback);
+  }
+#ifdef NS_BUILD_REFCNT_LOGGING
+  virtual ~nsDisplayPluginReadback() {
+    MOZ_COUNT_DTOR(nsDisplayPluginReadback);
+  }
+#endif
+
+  virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder);
+  virtual PRBool ComputeVisibility(nsDisplayListBuilder* aBuilder,
+                                   nsRegion* aVisibleRegion,
+                                   const nsRect& aAllowVisibleRegionExpansion,
+                                   PRBool& aContainsRootContentDocBG);
+
+  NS_DISPLAY_DECL_NAME("PluginReadback", TYPE_PLUGIN_READBACK)
+
+  virtual already_AddRefed<Layer> BuildLayer(nsDisplayListBuilder* aBuilder,
+                                             LayerManager* aManager)
+  {
+    return static_cast<nsObjectFrame*>(mFrame)->BuildLayer(aBuilder, aManager, this);
+  }
+
+  virtual LayerState GetLayerState(nsDisplayListBuilder* aBuilder,
+                                   LayerManager* aManager)
+  {
+    return LAYER_ACTIVE;
+  }
+};
+
+static nsRect
+GetDisplayItemBounds(nsDisplayListBuilder* aBuilder, nsDisplayItem* aItem, nsIFrame* aFrame)
+{
+  nsRect r = aFrame->GetContentRect() - aFrame->GetPosition() +
+    aItem->ToReferenceFrame();
+
+  nsObjectFrame* f = static_cast<nsObjectFrame*>(aFrame);
+  if (LAYER_ACTIVE == f->GetLayerState(aBuilder)) {
     ImageContainer* c = f->GetImageContainer();
     if (c) {
       gfxIntSize size = c->GetCurrentSize();
       PRInt32 appUnitsPerDevPixel = f->PresContext()->AppUnitsPerDevPixel();
       nsSize sizeAppUnits(size.width*appUnitsPerDevPixel, size.height*appUnitsPerDevPixel);
       r += nsPoint((r.width - sizeAppUnits.width) / 2,
                    (r.height - sizeAppUnits.height) / 2);
       r.SizeTo(sizeAppUnits);
     }
   }
   return r;
 }
 
+nsRect
+nsDisplayPluginReadback::GetBounds(nsDisplayListBuilder* aBuilder)
+{
+  return GetDisplayItemBounds(aBuilder, this, mFrame);
+}
+
+PRBool
+nsDisplayPluginReadback::ComputeVisibility(nsDisplayListBuilder* aBuilder,
+                                           nsRegion* aVisibleRegion,
+                                           const nsRect& aAllowVisibleRegionExpansion,
+                                           PRBool& aContainsRootContentDocBG)
+{
+  if (!nsDisplayItem::ComputeVisibility(aBuilder, aVisibleRegion,
+                                        aAllowVisibleRegionExpansion,
+                                        aContainsRootContentDocBG))
+    return PR_FALSE;
+
+  nsRect expand;
+  expand.IntersectRect(aAllowVisibleRegionExpansion, GetBounds(aBuilder));
+  // *Add* our bounds to the visible region so that stuff underneath us is
+  // likely to be made visible, so we can use it for a background! This is
+  // a bit crazy since we normally only subtract from the visible region.
+  aVisibleRegion->Or(*aVisibleRegion, expand);
+  return PR_TRUE;
+}
+
+nsRect
+nsDisplayPlugin::GetBounds(nsDisplayListBuilder* aBuilder)
+{
+  return GetDisplayItemBounds(aBuilder, this, mFrame);
+}
+
 void
 nsDisplayPlugin::Paint(nsDisplayListBuilder* aBuilder,
                        nsIRenderingContext* aCtx)
 {
   nsObjectFrame* f = static_cast<nsObjectFrame*>(mFrame);
   f->PaintPlugin(aBuilder, *aCtx, mVisibleRect, GetBounds(aBuilder));
 }
 
 PRBool
 nsDisplayPlugin::ComputeVisibility(nsDisplayListBuilder* aBuilder,
                                    nsRegion* aVisibleRegion,
+                                   const nsRect& aAllowVisibleRegionExpansion,
                                    PRBool& aContainsRootContentDocBG)
 {
   mVisibleRegion.And(*aVisibleRegion, GetBounds(aBuilder));  
   return nsDisplayItem::ComputeVisibility(aBuilder, aVisibleRegion,
+                                          aAllowVisibleRegionExpansion,
                                           aContainsRootContentDocBG);
 }
 
 nsRegion
 nsDisplayPlugin::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
                                  PRBool* aForceTransparentSurface)
 {
   if (aForceTransparentSurface) {
@@ -1451,20 +1582,22 @@ nsObjectFrame::IsOpaque() const
 #endif
 }
 
 NS_IMETHODIMP
 nsObjectFrame::BuildDisplayList(nsDisplayListBuilder*   aBuilder,
                                 const nsRect&           aDirtyRect,
                                 const nsDisplayListSet& aLists)
 {
+  AddStateBits(NS_OBJECT_NEEDS_SET_IMAGE);
+
   // XXX why are we painting collapsed object frames?
   if (!IsVisibleOrCollapsedForPainting(aBuilder))
     return NS_OK;
-    
+
   nsresult rv = DisplayBorderBackgroundOutline(aBuilder, aLists);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsPresContext::nsPresContextType type = PresContext()->Type();
 
   // If we are painting in Print Preview do nothing....
   if (type == nsPresContext::eContext_PrintPreview)
     return NS_OK;
@@ -1499,16 +1632,22 @@ nsObjectFrame::BuildDisplayList(nsDispla
   }
 
   // determine if we are printing
   if (type == nsPresContext::eContext_Print) {
     rv = replacedContent.AppendNewToTop(new (aBuilder)
         nsDisplayGeneric(aBuilder, this, PaintPrintPlugin, "PrintPlugin",
                          nsDisplayItem::TYPE_PRINT_PLUGIN));
   } else {
+    if (aBuilder->IsPaintingToWindow() && GetLayerState(aBuilder) == LAYER_ACTIVE) {
+      rv = replacedContent.AppendNewToTop(new (aBuilder)
+          nsDisplayPluginReadback(aBuilder, this));
+      NS_ENSURE_SUCCESS(rv, rv);
+    }
+
     rv = replacedContent.AppendNewToTop(new (aBuilder)
         nsDisplayPlugin(aBuilder, this));
   }
   NS_ENSURE_SUCCESS(rv, rv);
 
   WrapReplacedContentForBorderRadius(aBuilder, &replacedContent, aLists);
 
   return NS_OK;
@@ -1802,24 +1941,54 @@ nsPluginInstanceOwner::SetCurrentImage(I
   cairoData.mSurface = mLayerSurface.get();
   cairoData.mSize = mLayerSurface->GetSize();
   pluginImage->SetData(cairoData);
   aContainer->SetCurrentImage(image);
 
   return PR_TRUE;
 }
 
-mozilla::LayerState
-nsObjectFrame::GetLayerState(nsDisplayListBuilder* aBuilder,
-                             LayerManager* aManager)
-{
-  if (!mInstanceOwner || !mInstanceOwner->UseLayers())
-    return mozilla::LAYER_NONE;
-
-  return mozilla::LAYER_ACTIVE;
+void
+nsPluginInstanceOwner::SetBackgroundUnknown()
+{
+  nsCOMPtr<nsIPluginInstance_MOZILLA_2_0_BRANCH> inst = GetInstance();
+  if (inst) {
+    inst->SetBackgroundUnknown();
+  }
+}
+
+already_AddRefed<gfxContext>
+nsPluginInstanceOwner::BeginUpdateBackground(const nsIntRect& aRect)
+{
+  nsIntRect rect = aRect;
+  nsCOMPtr<nsIPluginInstance_MOZILLA_2_0_BRANCH> inst = GetInstance();
+  nsRefPtr<gfxContext> ctx;
+  if (inst &&
+      NS_SUCCEEDED(inst->BeginUpdateBackground(&rect, getter_AddRefs(ctx)))) {
+    return ctx.forget();
+  }
+  return nsnull;
+}
+
+void
+nsPluginInstanceOwner::EndUpdateBackground(gfxContext* aContext,
+                                           const nsIntRect& aRect)
+{
+  nsIntRect rect = aRect;
+  nsCOMPtr<nsIPluginInstance_MOZILLA_2_0_BRANCH> inst = GetInstance();
+  if (inst) {
+    inst->EndUpdateBackground(aContext, &rect);
+  }
+}
+
+LayerState
+nsObjectFrame::GetLayerState(nsDisplayListBuilder* aBuilder)
+{
+  return mInstanceOwner && mInstanceOwner->UseLayers() ?
+      LAYER_ACTIVE : LAYER_NONE;
 }
 
 already_AddRefed<Layer>
 nsObjectFrame::BuildLayer(nsDisplayListBuilder* aBuilder,
                           LayerManager* aManager,
                           nsDisplayItem* aItem)
 {
   if (!mInstanceOwner)
@@ -1828,58 +1997,84 @@ nsObjectFrame::BuildLayer(nsDisplayListB
   NPWindow* window = nsnull;
   mInstanceOwner->GetWindow(window);
   if (!window)
     return nsnull;
 
   if (window->width <= 0 || window->height <= 0)
     return nsnull;
 
+  // Create image
+  nsRefPtr<ImageContainer> container = GetImageContainer(aManager);
+  if (!container)
+    return nsnull;
+  if (GetStateBits() & NS_OBJECT_NEEDS_SET_IMAGE) {
+    RemoveStateBits(NS_OBJECT_NEEDS_SET_IMAGE);
+    if (!mInstanceOwner->SetCurrentImage(container)) {
+      return nsnull;
+    }
+  }
+  gfxIntSize size = container->GetCurrentSize();
+
   nsRect area = GetContentRect() + aBuilder->ToReferenceFrame(GetParent());
   gfxRect r = nsLayoutUtils::RectToGfxRect(area, PresContext()->AppUnitsPerDevPixel());
   // to provide crisper and faster drawing.
   r.Round();
   nsRefPtr<Layer> layer =
     (aBuilder->LayerBuilder()->GetLeafLayerFor(aBuilder, aManager, aItem));
 
-  if (!layer) {
-    mInstanceOwner->NotifyPaintWaiter(aBuilder);
-    // Initialize ImageLayer
-    layer = aManager->CreateImageLayer();
-  }
-
-  if (!layer)
-    return nsnull;
-
-  NS_ASSERTION(layer->GetType() == Layer::TYPE_IMAGE, "ObjectFrame works only with ImageLayer");
-  // Create image
-  nsRefPtr<ImageContainer> container = GetImageContainer(aManager);
-  if (!container)
-    return nsnull;
-
-  if (!mInstanceOwner->SetCurrentImage(container)) {
-    return nsnull;
-  }
-
-  ImageLayer* imglayer = static_cast<ImageLayer*>(layer.get());
-  imglayer->SetContainer(container);
-  imglayer->SetFilter(nsLayoutUtils::GetGraphicsFilterForFrame(this));
-
-  layer->SetContentFlags(IsOpaque() ? Layer::CONTENT_OPAQUE : 0);
+  if (aItem->GetType() == nsDisplayItem::TYPE_PLUGIN) {
+    if (!layer) {
+      mInstanceOwner->NotifyPaintWaiter(aBuilder);
+      // Initialize ImageLayer
+      layer = aManager->CreateImageLayer();
+      if (!layer)
+        return nsnull;
+    }
+    NS_ASSERTION(layer->GetType() == Layer::TYPE_IMAGE, "Bad layer type");
+
+    ImageLayer* imglayer = static_cast<ImageLayer*>(layer.get());
+    imglayer->SetContainer(container);
+    imglayer->SetFilter(nsLayoutUtils::GetGraphicsFilterForFrame(this));
+
+    layer->SetContentFlags(IsOpaque() ? Layer::CONTENT_OPAQUE : 0);
+  } else {
+    NS_ASSERTION(aItem->GetType() == nsDisplayItem::TYPE_PLUGIN_READBACK,
+                 "Unknown item type");
+    if (!layer) {
+      layer = aManager->CreateReadbackLayer();
+      if (!layer)
+        return nsnull;
+    }
+    NS_ASSERTION(layer->GetType() == Layer::TYPE_READBACK, "Bad layer type");
+
+    ReadbackLayer* readback = static_cast<ReadbackLayer*>(layer.get());
+    if (readback->GetSize() != nsIntSize(size.width, size.height)) {
+      // This will destroy any old background sink and notify us that the
+      // background is now unknown
+      readback->SetSink(nsnull);
+      NS_ASSERTION(!mBackgroundSink, "Should have been cleared");
+
+      readback->SetSize(nsIntSize(size.width, size.height));
+
+      mBackgroundSink =
+        new PluginBackgroundSink(this,
+                                 readback->AllocateSequenceNumber());
+      readback->SetSink(mBackgroundSink);
+      // The layer has taken ownership of our sink. When either the sink dies
+      // or the frame dies, the connection from the surviving object is nulled out.
+    }
+  }
 
   // Set a transform on the layer to draw the plugin in the right place
   gfxMatrix transform;
-  // Center plugin if layer size != frame rect
-  r.pos.x += (r.Width() - container->GetCurrentSize().width) / 2;
-  r.pos.y += (r.Height() - container->GetCurrentSize().height) / 2;
   transform.Translate(r.pos);
 
   layer->SetTransform(gfx3DMatrix::From2D(transform));
-  nsRefPtr<Layer> result = layer.forget();
-  return result.forget();
+  return layer.forget();
 }
 
 void
 nsObjectFrame::PaintPlugin(nsDisplayListBuilder* aBuilder,
                            nsIRenderingContext& aRenderingContext,
                            const nsRect& aDirtyRect, const nsRect& aPluginRect)
 {
   // Screen painting code
@@ -2034,17 +2229,17 @@ nsObjectFrame::PaintPlugin(nsDisplayList
 
       gfxWindowsNativeDrawing nativeDraw(ctx, frameGfxRect);
 #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.
         NPEvent pluginEvent;
-        pluginEvent.event = mozilla::plugins::DoublePassRenderingEvent();
+        pluginEvent.event = DoublePassRenderingEvent();
         pluginEvent.wParam = 0;
         pluginEvent.lParam = 0;
         if (pluginEvent.event)
           inst->HandleEvent(&pluginEvent, nsnull);
       }
 #endif
       do {
         HDC hdc = nativeDraw.BeginNativeDrawing();
--- a/layout/generic/nsObjectFrame.h
+++ b/layout/generic/nsObjectFrame.h
@@ -57,19 +57,22 @@ class nsIAccessible;
 #endif
 
 class nsPluginInstanceOwner;
 class nsIPluginHost;
 class nsIPluginInstance;
 class nsPresContext;
 class nsDisplayPlugin;
 class nsIDOMElement;
+class PluginBackgroundSink;
 
 #define nsObjectFrameSuper nsFrame
 
+#define NS_OBJECT_NEEDS_SET_IMAGE NS_FRAME_STATE_BIT(31)
+
 class nsObjectFrame : public nsObjectFrameSuper,
                       public nsIObjectFrame,
                       public nsIReflowCallback {
 public:
   typedef mozilla::LayerState LayerState;
   typedef mozilla::layers::Layer Layer;
   typedef mozilla::layers::LayerManager LayerManager;
   typedef mozilla::layers::ImageContainer ImageContainer;
@@ -171,22 +174,25 @@ public:
   // for a given aRoot, this walks the frame tree looking for the next outFrame
   static nsIObjectFrame* GetNextObjectFrame(nsPresContext* aPresContext,
                                             nsIFrame* aRoot);
 
   // nsIReflowCallback
   virtual PRBool ReflowFinished();
   virtual void ReflowCallbackCanceled();
 
+  /**
+   * Builds either an ImageLayer or a ReadbackLayer, depending on the type
+   * of aItem (TYPE_PLUGIN or TYPE_PLUGIN_READBACK respectively).
+   */
   already_AddRefed<Layer> BuildLayer(nsDisplayListBuilder* aBuilder,
                                      LayerManager* aManager,
                                      nsDisplayItem* aItem);
 
-  virtual LayerState GetLayerState(nsDisplayListBuilder* aBuilder,
-                                   LayerManager* aManager);
+  virtual LayerState GetLayerState(nsDisplayListBuilder* aBuilder);
 
   ImageContainer* GetImageContainer(LayerManager* aManager = nsnull);
 
   /**
    * If aContent has a nsObjectFrame, then prepare it for a DocShell swap.
    * @see nsSubDocumentFrame::BeginSwapDocShells.
    * There will be a call to EndSwapDocShells after we were moved to the
    * new view tree.
@@ -267,16 +273,17 @@ protected:
   nsresult SetAbsoluteScreenPosition(nsIDOMElement* element,
                                      nsIDOMClientRect* position,
                                      nsIDOMClientRect* clip);
 
   void NotifyPluginReflowObservers();
 
   friend class nsPluginInstanceOwner;
   friend class nsDisplayPlugin;
+  friend class PluginBackgroundSink;
 
 private:
   
   class PluginEventNotifier : public nsRunnable {
   public:
     PluginEventNotifier(const nsString &aEventType) : 
       mEventType(aEventType) {}
     
@@ -284,16 +291,21 @@ private:
   private:
     nsString mEventType;
   };
   
   nsRefPtr<nsPluginInstanceOwner> mInstanceOwner;
   nsIView*                        mInnerView;
   nsCOMPtr<nsIWidget>             mWidget;
   nsIntRect                       mWindowlessRect;
+  /**
+   * This is owned by the ReadbackLayer for this nsObjectFrame. It is
+   * automatically cleared if the PluginBackgroundSink is destroyed.
+   */
+  PluginBackgroundSink*           mBackgroundSink;
 
   // For assertions that make it easier to determine if a crash is due
   // to the underlying problem described in bug 136927, and to prevent
   // reentry into instantiation.
   PRBool mPreventInstantiation;
 
   PRPackedBool mReflowCallbackPosted;
 
@@ -317,16 +329,17 @@ public:
 
   virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder);
   virtual nsRegion GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
                                    PRBool* aForceTransparentSurface = nsnull);
   virtual void Paint(nsDisplayListBuilder* aBuilder,
                      nsIRenderingContext* aCtx);
   virtual PRBool ComputeVisibility(nsDisplayListBuilder* aBuilder,
                                    nsRegion* aVisibleRegion,
+                                   const nsRect& aAllowVisibleRegionExpansion,
                                    PRBool& aContainsRootContentDocBG);
 
   NS_DISPLAY_DECL_NAME("Plugin", TYPE_PLUGIN)
 
   // Compute the desired position and clip region of the plugin's widget.
   // This will only be called for plugins which have been registered
   // with the root pres context for geometry updates.
   // The widget, its new position, size and clip region are appended as
@@ -340,16 +353,16 @@ public:
                                              LayerManager* aManager)
   {
     return static_cast<nsObjectFrame*>(mFrame)->BuildLayer(aBuilder, aManager, this);
   }
 
   virtual LayerState GetLayerState(nsDisplayListBuilder* aBuilder,
                                    LayerManager* aManager)
   {
-    return static_cast<nsObjectFrame*>(mFrame)->GetLayerState(aBuilder, aManager);
+    return static_cast<nsObjectFrame*>(mFrame)->GetLayerState(aBuilder);
   }
 
 private:
   nsRegion mVisibleRegion;
 };
 
 #endif /* nsObjectFrame_h___ */
--- a/layout/reftests/bugs/reftest.list
+++ b/layout/reftests/bugs/reftest.list
@@ -1553,17 +1553,17 @@ random-if(layersGPUAccelerated) fails-if
 == 582037-2a.html 582037-2-ref.html
 == 582037-2b.html 582037-2-ref.html
 asserts(0-1) == 582146-1.html about:blank
 == 582476-1.svg 582476-1-ref.svg
 == 584400-dash-length.svg 584400-dash-length-ref.svg
 == 584699-1.html 584699-1-ref.html
 == 585598-2.xhtml 585598-2-ref.xhtml
 == 586400-1.html 586400-1-ref.html
-== 586683-1.html 586683-1-ref.html
+fails-if(cocoaWidget) == 586683-1.html 586683-1-ref.html
 == 589615-1a.xhtml 589615-1-ref.html
 == 589615-1b.html 589615-1-ref.html
 == 589672-1.html 589672-1-ref.html
 == 593544-1.html 593544-1-ref.html
 == 594333-1.html 594333-1-ref.html
 == 594624-1.html 594624-1-ref.html
 == 594737-1.html 594737-1-ref.html
 == 597721-1.html 597721-1-ref.html
--- a/layout/tools/reftest/reftest.js
+++ b/layout/tools/reftest/reftest.js
@@ -566,16 +566,17 @@ function ReadManifest(aURL)
                 stat = m[1];
                 // Note: m[2] contains the parentheses, and we want them.
                 cond = Components.utils.evalInSandbox(m[2], sandbox);
             } else if (item.match(/^(fails|random|skip)$/)) {
                 stat = item;
                 cond = true;
             } else if (item == "needs-focus") {
                 needs_focus = true;
+                cond = false;
             } else if ((m = item.match(/^asserts\((\d+)(-\d+)?\)$/))) {
                 cond = false;
                 minAsserts = Number(m[1]);
                 maxAsserts = (m[2] == undefined) ? minAsserts
                                                  : Number(m[2].substring(1));
             } else if ((m = item.match(/^asserts-if\((.*?),(\d+)(-\d+)?\)$/))) {
                 cond = false;
                 if (Components.utils.evalInSandbox("(" + m[1] + ")", sandbox)) {
--- a/modules/plugin/base/public/nsIPluginInstance.idl
+++ b/modules/plugin/base/public/nsIPluginInstance.idl
@@ -43,23 +43,27 @@ interface nsIOutputStream;
 
 %{C++
 #include "npapi.h"
 #include "nsStringGlue.h"
 #include "gfxASurface.h"
 struct JSContext;
 struct JSObject;
 class gfxASurface;
+class gfxContext;
+struct nsIntRect;
 
 #define NPRUNTIME_JSCLASS_NAME "NPObject JS wrapper class"
 %}
 
 [ptr] native JSContextPtr(JSContext);
 [ptr] native JSObjectPtr(JSObject);
 [ptr] native gfxASurfacePtr(gfxASurface);
+[ptr] native gfxContextPtr(gfxContext);
+[ptr] native nsIntRectPtr(nsIntRect);
 
 [uuid(84994340-E120-4051-824F-D4EE8AEF1A3E)]
 interface nsIPluginInstance : nsISupports
 {
     /**
      * Initializes a newly created plugin instance.
      * 
      * @param aOwner - the plugin instance owner
@@ -242,8 +246,23 @@ interface nsIPluginInstance : nsISupport
      */
     void getSurface(out gfxASurfacePtr aSurface);
 
     /**
      * @return true if plugin module supports async rendering
      */
     PRBool useAsyncPainting();
 };
+
+
+// XXX kill me after branching
+[noscript, uuid(324f3c02-4fbd-430b-8afa-db083d3867fc)]
+interface nsIPluginInstance_MOZILLA_2_0_BRANCH : nsIPluginInstance
+{
+    /**
+     * This is the second leg in the trip to PluginInstanceParent.  It
+     * approximately follows the ReadbackSink API.
+     */
+
+    void setBackgroundUnknown();
+    void beginUpdateBackground(in nsIntRectPtr rect, out gfxContextPtr ctx);
+    void endUpdateBackground(in gfxContextPtr ctx, in nsIntRectPtr rect);
+};
--- a/modules/plugin/base/src/PluginPRLibrary.cpp
+++ b/modules/plugin/base/src/PluginPRLibrary.cpp
@@ -250,9 +250,37 @@ nsresult
 PluginPRLibrary::GetSurface(NPP instance, gfxASurface** aSurface)
 {
   nsNPAPIPluginInstance* inst = (nsNPAPIPluginInstance*)instance->ndata;
   NS_ENSURE_TRUE(inst, NS_ERROR_NULL_POINTER);
   *aSurface = nsnull;
   return NS_OK;
 }
 
+nsresult
+PluginPRLibrary::SetBackgroundUnknown(NPP instance)
+{
+  nsNPAPIPluginInstance* inst = (nsNPAPIPluginInstance*)instance->ndata;
+  NS_ENSURE_TRUE(inst, NS_ERROR_NULL_POINTER);
+  NS_ERROR("Unexpected use of async APIs for in-process plugin.");
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+nsresult
+PluginPRLibrary::BeginUpdateBackground(NPP instance,
+                                       const nsIntRect&, gfxContext** aCtx)
+{
+  nsNPAPIPluginInstance* inst = (nsNPAPIPluginInstance*)instance->ndata;
+  NS_ENSURE_TRUE(inst, NS_ERROR_NULL_POINTER);
+  NS_ERROR("Unexpected use of async APIs for in-process plugin.");
+  *aCtx = nsnull;
+  return NS_OK;
+}
+
+nsresult
+PluginPRLibrary::EndUpdateBackground(NPP instance,
+                                     gfxContext*, const nsIntRect&)
+{
+  NS_RUNTIMEABORT("This should never be called");
+  return NS_ERROR_NOT_AVAILABLE;
+}
+
 } // namespace mozilla
--- a/modules/plugin/base/src/PluginPRLibrary.h
+++ b/modules/plugin/base/src/PluginPRLibrary.h
@@ -137,16 +137,24 @@ public:
 
     virtual nsresult NPP_ClearSiteData(const char* site, uint64_t flags,
                                        uint64_t maxAge);
     virtual nsresult NPP_GetSitesWithData(InfallibleTArray<nsCString>& result);
 
     virtual nsresult AsyncSetWindow(NPP instance, NPWindow* window);
     virtual nsresult GetSurface(NPP instance, gfxASurface** aSurface);
     NS_OVERRIDE virtual bool UseAsyncPainting() { return false; }
+    NS_OVERRIDE
+    virtual nsresult SetBackgroundUnknown(NPP instance);
+    NS_OVERRIDE
+    virtual nsresult BeginUpdateBackground(NPP instance,
+                                           const nsIntRect&, gfxContext** aCtx);
+    NS_OVERRIDE
+    virtual nsresult EndUpdateBackground(NPP instance,
+                                         gfxContext* aCtx, const nsIntRect&);
 
 private:
     NP_InitializeFunc mNP_Initialize;
     NP_ShutdownFunc mNP_Shutdown;
     NP_GetMIMEDescriptionFunc mNP_GetMIMEDescription;
 #if defined(XP_UNIX) && !defined(XP_MACOSX)
     NP_GetValueFunc mNP_GetValue;
 #endif
--- a/modules/plugin/base/src/nsNPAPIPluginInstance.cpp
+++ b/modules/plugin/base/src/nsNPAPIPluginInstance.cpp
@@ -54,23 +54,23 @@
 
 #include "nsIDocument.h"
 #include "nsIScriptGlobalObject.h"
 #include "nsIScriptContext.h"
 #include "nsDirectoryServiceDefs.h"
 #include "nsJSNPRuntime.h"
 #include "nsPluginStreamListenerPeer.h"
 
+using namespace mozilla;
 using namespace mozilla::plugins::parent;
-using mozilla::TimeStamp;
 
 static NS_DEFINE_IID(kIOutputStreamIID, NS_IOUTPUTSTREAM_IID);
 static NS_DEFINE_IID(kIPluginStreamListenerIID, NS_IPLUGINSTREAMLISTENER_IID);
 
-NS_IMPL_ISUPPORTS1(nsNPAPIPluginInstance, nsIPluginInstance)
+NS_IMPL_ISUPPORTS2(nsNPAPIPluginInstance, nsIPluginInstance, nsIPluginInstance_MOZILLA_2_0_BRANCH)
 
 nsNPAPIPluginInstance::nsNPAPIPluginInstance(nsNPAPIPlugin* plugin)
   :
 #ifdef XP_MACOSX
 #ifdef NP_NO_QUICKDRAW
     mDrawingModel(NPDrawingModelCoreGraphics),
 #else
     mDrawingModel(NPDrawingModelQuickDraw),
@@ -804,46 +804,55 @@ nsNPAPIPluginInstance::ShouldCache(PRBoo
 
 NS_IMETHODIMP
 nsNPAPIPluginInstance::IsWindowless(PRBool* isWindowless)
 {
   *isWindowless = mWindowless;
   return NS_OK;
 }
 
+class NS_STACK_CLASS AutoPluginLibraryCall
+{
+public:
+  AutoPluginLibraryCall(nsNPAPIPluginInstance* aThis)
+    : mThis(aThis), mGuard(aThis), mLibrary(nsnull)
+  {
+    nsNPAPIPlugin* plugin = mThis->GetPlugin();
+    if (plugin)
+      mLibrary = plugin->GetLibrary();
+  }
+  operator bool() { return !!mLibrary; }
+  PluginLibrary* operator->() { return mLibrary; }
+
+private:
+  nsNPAPIPluginInstance* mThis;
+  PluginDestructionGuard mGuard;
+  PluginLibrary* mLibrary;
+};
+
 NS_IMETHODIMP
 nsNPAPIPluginInstance::AsyncSetWindow(NPWindow* window)
 {
   if (RUNNING != mRunning)
     return NS_OK;
 
-  PluginDestructionGuard guard(this);
-
-  if (!mPlugin)
-    return NS_ERROR_FAILURE;
-
-  PluginLibrary* library = mPlugin->GetLibrary();
+  AutoPluginLibraryCall library(this);
   if (!library)
     return NS_ERROR_FAILURE;
 
   return library->AsyncSetWindow(&mNPP, window);
 }
 
 NS_IMETHODIMP
 nsNPAPIPluginInstance::GetSurface(gfxASurface** aSurface)
 {
   if (RUNNING != mRunning)
     return NS_OK;
 
-  PluginDestructionGuard guard(this);
-
-  if (!mPlugin)
-    return NS_ERROR_FAILURE;
-
-  PluginLibrary* library = mPlugin->GetLibrary();
+  AutoPluginLibraryCall library(this);
   if (!library)
     return NS_ERROR_FAILURE;
 
   return library->GetSurface(&mNPP, aSurface);
 }
 
 
 NS_IMETHODIMP
@@ -856,30 +865,66 @@ nsNPAPIPluginInstance::NotifyPainted(voi
 NS_IMETHODIMP
 nsNPAPIPluginInstance::UseAsyncPainting(PRBool* aIsAsync)
 {
   if (!mUsePluginLayersPref) {
     *aIsAsync = mUsePluginLayersPref;
     return NS_OK;
   }
 
-  PluginDestructionGuard guard(this);
-
-  if (!mPlugin)
-    return NS_ERROR_FAILURE;
-
-  PluginLibrary* library = mPlugin->GetLibrary();
+  AutoPluginLibraryCall library(this);
   if (!library)
     return NS_ERROR_FAILURE;
 
   *aIsAsync = library->UseAsyncPainting();
   return NS_OK;
 }
 
 NS_IMETHODIMP
+nsNPAPIPluginInstance::SetBackgroundUnknown()
+{
+  if (RUNNING != mRunning)
+    return NS_OK;
+
+  AutoPluginLibraryCall library(this);
+  if (!library)
+    return NS_ERROR_FAILURE;
+
+  return library->SetBackgroundUnknown(&mNPP);
+}
+
+NS_IMETHODIMP
+nsNPAPIPluginInstance::BeginUpdateBackground(nsIntRect* aRect,
+                                             gfxContext** aContext)
+{
+  if (RUNNING != mRunning)
+    return NS_OK;
+
+  AutoPluginLibraryCall library(this);
+  if (!library)
+    return NS_ERROR_FAILURE;
+
+  return library->BeginUpdateBackground(&mNPP, *aRect, aContext);
+}
+
+NS_IMETHODIMP
+nsNPAPIPluginInstance::EndUpdateBackground(gfxContext* aContext,
+                                           nsIntRect* aRect)
+{
+  if (RUNNING != mRunning)
+    return NS_OK;
+
+  AutoPluginLibraryCall library(this);
+  if (!library)
+    return NS_ERROR_FAILURE;
+
+  return library->EndUpdateBackground(&mNPP, aContext, *aRect);
+}
+
+NS_IMETHODIMP
 nsNPAPIPluginInstance::IsTransparent(PRBool* isTransparent)
 {
   *isTransparent = mTransparent;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsNPAPIPluginInstance::GetFormValue(nsAString& aValue)
--- a/modules/plugin/base/src/nsNPAPIPluginInstance.h
+++ b/modules/plugin/base/src/nsNPAPIPluginInstance.h
@@ -62,24 +62,25 @@ class nsNPAPITimer
 {
 public:
   NPP npp;
   uint32_t id;
   nsCOMPtr<nsITimer> timer;
   void (*callback)(NPP npp, uint32_t timerID);
 };
 
-class nsNPAPIPluginInstance : public nsIPluginInstance
+class nsNPAPIPluginInstance : public nsIPluginInstance_MOZILLA_2_0_BRANCH
 {
 private:
   typedef mozilla::PluginLibrary PluginLibrary;
 
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIPLUGININSTANCE
+  NS_DECL_NSIPLUGININSTANCE_MOZILLA_2_0_BRANCH
 
   nsNPAPIPlugin* GetPlugin();
 
   nsresult GetNPP(NPP * aNPP);
 
   void SetURI(nsIURI* uri);
   nsIURI* GetURI();
 
new file mode 100644
--- /dev/null
+++ b/modules/plugin/test/crashtests/626602-1.html
@@ -0,0 +1,109 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<head>
+  <style type="text/css">
+#one {
+  position:absolute;
+  left:0px; top:0px;
+  z-index:4;
+}
+#two {
+  position:absolute;
+  top:100px; left:100px;
+  background-color:rgb(0,0,0,0);
+  z-index:3;
+}
+#three {
+  position:absolute;
+  left:100px; top:100px;
+  width:200px; height:200px;
+  background-color: rgb(255,0,0);
+  opacity:0.6;
+  z-index:2;
+}
+#four {
+  position:absolute;
+  top:100px; left:100px;
+  z-index:1;
+}
+  </style>
+  <script type="text/javascript">
+var plugin, div, canvas;
+function start() {
+  plugin = document.getElementById("four");
+  div = document.getElementById("three");
+  canvas = document.getElementById("two");
+  paintCanvas();
+
+  mozRequestAnimationFrame(moveSomething);
+}
+
+function paintCanvas() {
+  var ctx = canvas.getContext("2d");
+  ctx.fillStyle = "rgba(255,0,0, 0.6)";
+  ctx.fillRect(0,0, 200,200);
+}
+
+var i = 0, numLoops = 20;
+var pluginIn = true, divIn = true, canvasIn = true;
+function moveSomething() {
+  var didSomething = (0 === (i % 2)) ? moveSomethingOut() : moveSomethingIn();
+  if (!didSomething && ++i >= numLoops) {
+    return finish();
+  }
+
+  mozRequestAnimationFrame(moveSomething);
+}
+
+function finish() {
+  document.documentElement.removeAttribute("class");
+}
+
+function moveSomethingOut() {
+  if (pluginIn) {
+    plugin.style.left = "400px";
+    pluginIn = false;
+  } else if (divIn) {
+    div.style.left = "400px";
+    divIn = false;
+  } else if (canvasIn) {
+    canvas.style.left = "400px";
+    canvasIn = false;
+  } else {
+    return false;
+  }
+  return true;
+}
+
+function moveSomethingIn() {
+  if (!pluginIn) {
+    plugin.style.left = "100px";
+    pluginIn = true;
+  } else if (!divIn) {
+    div.style.left = "100px";
+    divIn = true;
+  } else if (!canvasIn) {
+    canvas.style.left = "100px";
+    canvasIn = true;
+  } else {
+    return false;
+  }
+  return true;
+}
+
+function reset() {
+
+}
+  </script>
+</style>
+</head>
+<body onload="start();">
+  <embed id="four" type="application/x-test" width="200" height="200"
+         drawmode="solid" color="FFFF0000"></embed>
+  <div id="three"></div>
+  <canvas id="two" width="200" height="200"></canvas>
+  <embed id="one" type="application/x-test" width="400" height="400"
+         drawmode="solid" color="9900FF00"></embed>
+</body>
+</html>
+
--- a/modules/plugin/test/crashtests/crashtests.list
+++ b/modules/plugin/test/crashtests/crashtests.list
@@ -4,8 +4,9 @@ load 110650-1.html
 skip-if(browserIsRemote||!haveTestPlugin||cocoaWidget) script 539897-1.html # browserIsRemote is bug XXXXXX
 skip-if(browserIsRemote||!haveTestPlugin) script 540114-1.html # browserIsRemote is bug XXXXXX
 load 570884.html
 # This test relies on the reading of screenX/Y forcing a round trip to
 # the X server, which is a bad assumption for <browser remote>.
 # Plugin arch is going to change anyway with OOP content so skipping
 # this test for now is OK.
 skip-if(browserIsRemote||!haveTestPlugin||http.platform!="X11"||!testPluginIsOOP()) load 598862.html
+load 626602-1.html
new file mode 100644
--- /dev/null
+++ b/modules/plugin/test/reftest/plugin-background-1-step.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<head>
+  <link rel="stylesheet" type="text/css" href="plugin-background.css"></link>
+  <script type="text/javascript">
+var NUM_STEPS = 1;
+  </script>
+  <script type="text/javascript" src="plugin-background.js"></script>
+</head>
+<body>
+  <div id="bad">Test some plugin stuff.</div>
+  <div id="good"></div>
+
+  <embed id="plugin" type="application/x-test" width="199" height="199"
+         drawmode="solid" color="330000FF"></embed>
+
+  <div id="topbar"></div>
+  <div id="leftbar"></div>
+  <div id="rightbar"></div>
+  <div id="bottombar"></div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/modules/plugin/test/reftest/plugin-background-10-step.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<head>
+  <link rel="stylesheet" type="text/css" href="plugin-background.css"></link>
+  <script type="text/javascript">
+var NUM_STEPS = 10;
+  </script>
+  <script type="text/javascript" src="plugin-background.js"></script>
+</head>
+<body>
+  <div id="bad">Test some plugin stuff.</div>
+  <div id="good"></div>
+
+  <embed id="plugin" type="application/x-test" width="199" height="199"
+         drawmode="solid" color="330000FF"></embed>
+
+  <div id="topbar"></div>
+  <div id="leftbar"></div>
+  <div id="rightbar"></div>
+  <div id="bottombar"></div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/modules/plugin/test/reftest/plugin-background-2-step.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<head>
+  <link rel="stylesheet" type="text/css" href="plugin-background.css"></link>
+  <script type="text/javascript">
+var NUM_STEPS = 2;
+  </script>
+  <script type="text/javascript" src="plugin-background.js"></script>
+</head>
+<body>
+  <div id="bad">Test some plugin stuff.</div>
+  <div id="good"></div>
+
+  <embed id="plugin" type="application/x-test" width="199" height="199"
+         drawmode="solid" color="330000FF"></embed>
+
+  <div id="topbar"></div>
+  <div id="leftbar"></div>
+  <div id="rightbar"></div>
+  <div id="bottombar"></div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/modules/plugin/test/reftest/plugin-background-5-step.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<head>
+  <link rel="stylesheet" type="text/css" href="plugin-background.css"></link>
+  <script type="text/javascript">
+var NUM_STEPS = 5;
+  </script>
+  <script type="text/javascript" src="plugin-background.js"></script>
+</head>
+<body>
+  <div id="bad">Test some plugin stuff.</div>
+  <div id="good"></div>
+
+  <embed id="plugin" type="application/x-test" width="199" height="199"
+         drawmode="solid" color="330000FF"></embed>
+
+  <div id="topbar"></div>
+  <div id="leftbar"></div>
+  <div id="rightbar"></div>
+  <div id="bottombar"></div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/modules/plugin/test/reftest/plugin-background-ref.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <link rel="stylesheet" type="text/css" href="plugin-background.css"></link>
+</head>
+<body>
+  <div id="bad">Test some plugin stuff.</div>
+  <div id="good"></div>
+
+  <div id="plugin"></div>
+
+  <div id="topbar"></div>
+  <div id="leftbar"></div>
+  <div id="rightbar"></div>
+  <div id="bottombar"></div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/modules/plugin/test/reftest/plugin-background.css
@@ -0,0 +1,58 @@
+div {
+  position: absolute;
+}
+#bad {
+  left:220px; top:0px;
+  z-index: 0;
+}
+#good {
+  left:0px; top:0px;
+  width:220px; height:220px;
+  background-color: rgba(0,255,0, 0.6);
+  z-index: 0;
+}
+
+#topbar {
+  left:0px; top:0px;
+  width:220px; height:20px;
+  background-color: rgb(0,0,0);
+  z-index: 2;
+}
+#topbar {
+  left:0px; top:0px;
+  width:220px; height:20px;
+  background-color: rgb(0,0,0);
+  z-index: 2;
+}
+#leftbar {
+  left:0px; top:0px;
+  width:20px; height:220px;
+  background-color: rgb(0,0,0);
+  z-index: 2;
+}
+#rightbar {
+  left:200px; top:0px;
+  width:20px; height:220px;
+  background-color: rgb(0,0,0);
+  z-index: 2;
+}
+#bottombar {
+  left:0px; top:200px;
+  width:220px; height:20px;
+  background-color: rgb(0,0,0);
+  z-index: 2;
+}
+
+div#plugin {
+  position: absolute;
+  left:1px; top:1px;
+  width:199px; height:199px;
+  background-color: rgba(0,0,255, 0.2);
+  z-index: 1;
+}
+
+embed#plugin {
+  position: absolute;
+  left:1px; top:1px;
+  z-index: 1;
+}
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/modules/plugin/test/reftest/plugin-background.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <link rel="stylesheet" type="text/css" href="plugin-background.css"></link>
+</head>
+<body>
+  <div id="bad">Test some plugin stuff.</div>
+  <div id="good"></div>
+
+  <embed id="plugin" type="application/x-test" width="199" height="199"
+         drawmode="solid" color="330000FF"></embed>
+
+  <div id="topbar"></div>
+  <div id="leftbar"></div>
+  <div id="rightbar"></div>
+  <div id="bottombar"></div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/modules/plugin/test/reftest/plugin-background.js
@@ -0,0 +1,75 @@
+// The including script sets this for us
+//var NUM_STEPS;
+
+var plugin;
+var left = 1, top = 1, width = 199, height = 199;
+function movePluginTo(x, y, w, h) {
+    left = x; top = y;  width = w; height = h;
+    plugin.width = w;
+    plugin.height = h;
+    plugin.style.left = left + "px";
+    plugin.style.top = top + "px";
+}
+function deltaInBounds(dx,dy, dw,dh) {
+    var l = dx + left;
+    var r = l + width + dw;
+    var t = dy + top;
+    var b = t + height + dh;
+    return (0 <= l && l <= 20 &&
+            0 <= t && t <= 20 &&
+            200 <= r && r <= 220 &&
+            200 <= b && b <= 220);
+}
+
+var initialFrame;
+function start() {
+    window.removeEventListener("MozReftestInvalidate", start, false);
+
+    window.addEventListener("MozAfterPaint", step, false);
+    window.addEventListener("MozPaintWaitFinished", step, false);
+
+    initialFrame = window.mozPaintCount;
+    plugin = document.getElementById("plugin");
+
+    movePluginTo(0,0, 200,200);
+}
+
+var steps = 0;
+var which = "move"; // or "grow"
+var dx = 1, dy = 1, dw = 1, dh = 1;
+function step() {
+    if (++steps >= NUM_STEPS) {
+        window.removeEventListener("MozAfterPaint", step, false);
+        window.removeEventListener("MozPaintWaitFinished", step, false);
+        return finish();
+    }
+
+    var didSomething = false;
+    if (which == "grow") {
+        if (deltaInBounds(0,0, dw,dh)) {
+            movePluginTo(left,top, width+dw, height+dh);
+            didSomething = true;
+        } else {
+            dw = -dw;  dh = -dh;
+        }
+    } else {
+        // "move"
+        if (deltaInBounds(dx,dy, 0,0)) {
+            movePluginTo(left+dx,top+dy, width, height);
+            didSomething = true;
+        } else {
+            dx = -dx;  dy = -dy;
+        }
+    }
+    which = (which == "grow") ? "move" : "grow";
+
+    if (!didSomething) {
+        step();
+    }
+}
+
+function finish() {
+    document.documentElement.removeAttribute("class");
+}
+
+window.addEventListener("MozReftestInvalidate", start, false);
new file mode 100644
--- /dev/null
+++ b/modules/plugin/test/reftest/plugin-busy-alpha-zindex.html
@@ -0,0 +1,49 @@
+<!doctype html>
+<html>
+<head>
+  <style type="text/css">
+#one {
+  position:absolute;
+  left:0px; top:0px;
+  z-index:4;
+}
+#two {
+  position:absolute;
+  top:100px; left:100px;
+  background-color:rgb(0,0,0,0);
+  z-index:3;
+}
+#three {
+  position:absolute;
+  left:100px; top:100px;
+  width:200px; height:200px;
+  background-color: rgb(255,0,0);
+  opacity:0.6;
+  z-index:2;
+}
+#four {
+  position:absolute;
+  top:100px; left:100px;
+  z-index:1;
+}
+  </style>
+  <script type="text/javascript">
+function paintCanvas() {
+  var canvas = document.getElementById("two");
+  var ctx = canvas.getContext("2d");
+  ctx.fillStyle = "rgba(255,0,0, 0.6)";
+  ctx.fillRect(0,0, 200,200);
+}
+  </script>
+</style>
+</head>
+<body onload="paintCanvas();">
+  <embed id="four" type="application/x-test" width="200" height="200"
+         drawmode="solid" color="FFFF0000"></embed>
+  <div id="three"></div>
+  <canvas id="two" width="200" height="200"></canvas>
+  <embed id="one" type="application/x-test" width="400" height="400"
+         drawmode="solid" color="9900FF00"></embed>
+</body>
+</html>
+
new file mode 100644
--- /dev/null
+++ b/modules/plugin/test/reftest/plugin-canvas-alpha-zindex.html
@@ -0,0 +1,34 @@
+<!doctype html>
+<html>
+<head>
+  <style type="text/css">
+#one {
+  position:absolute;
+  left:0px; top:0px;
+  z-index:1;
+}
+#two {
+  position:absolute;
+  top:100px; left:100px;
+// Set these using the canvas API
+//  width:200px; height:200px;
+//  background-color: rgb(255,0,0);
+  z-index:0;
+}
+  </style>
+  <script type="text/javascript">
+function paintCanvas() {
+  var canvas = document.getElementById("two");
+  var ctx = canvas.getContext("2d");
+  ctx.fillStyle = "rgb(255,0,0)";
+  ctx.fillRect(0,0, 200,200);
+}
+  </script>
+</style>
+</head>
+<body onload="paintCanvas();">
+  <canvas width="200" height="200" id="two"></canvas>
+  <embed id="one" type="application/x-test" width="400" height="400" drawmode="solid" color="9900FF00"></embed>
+</body>
+</html>
+
new file mode 100644
--- /dev/null
+++ b/modules/plugin/test/reftest/plugin-transform-alpha-zindex.html
@@ -0,0 +1,26 @@
+<!doctype html>
+<html>
+<head>
+<style type="text/css">
+#one {
+  position:absolute;
+  left:0px; top:0px;
+  z-index:1;
+}
+#two {
+  position:absolute;
+  top:0px; left:0px;
+  width:200px; height:200px;
+  z-index:0;
+  background-color: rgb(255,0,0);
+  -moz-transform-origin: 0 0;
+  -moz-transform: translate(100px,100px);
+}
+</style>
+</head>
+<body>
+  <div id="two"></div>
+  <embed id="one" type="application/x-test" width="400" height="400" drawmode="solid" color="9900FF00"></embed>
+</body>
+</html>
+
--- a/modules/plugin/test/reftest/reftest.list
+++ b/modules/plugin/test/reftest/reftest.list
@@ -4,8 +4,16 @@ fails-if(!haveTestPlugin) == plugin-sani
 fails-if(!haveTestPlugin) == plugin-alpha-zindex.html div-alpha-zindex.html
 fails-if(!haveTestPlugin) == plugin-alpha-opacity.html div-alpha-opacity.html
 random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) fails-if(!haveTestPlugin) == windowless-clipping-1.html windowless-clipping-1-ref.html # bug 631832
 random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) fails-if(!haveTestPlugin) == border-padding-1.html border-padding-1-ref.html # bug 629430
 random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) fails-if(!haveTestPlugin) == border-padding-2.html border-padding-2-ref.html # bug 629430
 random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) fails-if(!haveTestPlugin) == border-padding-3.html border-padding-3-ref.html # bug 629430
 random-if(cocoaWidget||d2d) fails-if(!haveTestPlugin) skip-if(!testPluginIsOOP()) == pluginproblemui-direction-1.html pluginproblemui-direction-1-ref.html
 fails-if(!haveTestPlugin) skip-if(!testPluginIsOOP()) == pluginproblemui-direction-2.html pluginproblemui-direction-2-ref.html
+fails-if(!haveTestPlugin) == plugin-canvas-alpha-zindex.html div-alpha-zindex.html
+fails-if(!haveTestPlugin) == plugin-transform-alpha-zindex.html div-alpha-zindex.html
+random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) fails-if(!haveTestPlugin) == plugin-busy-alpha-zindex.html div-alpha-zindex.html
+fails-if(!haveTestPlugin) == plugin-background.html plugin-background-ref.html
+random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) fails-if(!haveTestPlugin) == plugin-background-1-step.html plugin-background-ref.html
+random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) fails-if(!haveTestPlugin) == plugin-background-2-step.html plugin-background-ref.html
+fails-if(!haveTestPlugin) == plugin-background-5-step.html plugin-background-ref.html
+fails-if(!haveTestPlugin) == plugin-background-10-step.html plugin-background-ref.html
--- 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;
--- a/netwerk/base/public/Makefile.in
+++ b/netwerk/base/public/Makefile.in
@@ -54,16 +54,17 @@ SDK_XPIDLSRCS   = \
 		nsIStreamListener.idl \
 		nsIIOService.idl \
 		nsIURI.idl \
 		nsIURL.idl \
 		nsIFileURL.idl \
 		nsIUploadChannel.idl \
 		nsIUnicharStreamListener.idl \
 		nsITraceableChannel.idl \
+		nsIX509Cert.idl \
 		$(NULL)
 
 XPIDLSRCS	= \
 		nsIApplicationCache.idl \
 		nsIApplicationCacheChannel.idl \
 		nsIApplicationCacheContainer.idl \
 		nsIApplicationCacheService.idl \
 		nsIAuthInformation.idl \
@@ -138,16 +139,18 @@ XPIDLSRCS	= \
 		nsIProxiedChannel.idl \
 		nsIRandomGenerator.idl \
 		nsIStrictTransportSecurityService.idl \
 		nsIURIWithPrincipal.idl \
 		nsIURIClassifier.idl \
 		nsIRedirectResultListener.idl \
 		mozIThirdPartyUtil.idl \
 		nsISerializationHelper.idl \
+		nsISSLStatus.idl \
+		nsISSLStatusProvider.idl \
 		$(NULL)
 
 ifdef MOZ_IPC
 XPIDLSRCS	+= \
 		nsIChildChannel.idl \
 		nsIParentChannel.idl \
 		nsIParentRedirectingChannel.idl \
 		nsIRedirectChannelRegistrar.idl
rename from security/manager/ssl/public/nsISSLStatus.idl
rename to netwerk/base/public/nsISSLStatus.idl
rename from security/manager/boot/public/nsISSLStatusProvider.idl
rename to netwerk/base/public/nsISSLStatusProvider.idl
rename from security/manager/ssl/public/nsIX509Cert.idl
rename to netwerk/base/public/nsIX509Cert.idl
--- a/netwerk/protocol/http/nsHttpNTLMAuth.cpp
+++ b/netwerk/protocol/http/nsHttpNTLMAuth.cpp
@@ -17,16 +17,17 @@
  * The Initial Developer of the Original Code is
  * Netscape Communications Corporation.
  * Portions created by the Initial Developer are Copyright (C) 2003
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
  *   Darin Fisher <darin@meer.net>
  *   Jim Mathies <jmathies@mozilla.com>
+ *   Guillermo Robla Vicario <groblavicario@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
@@ -47,16 +48,19 @@
 
 //-----------------------------------------------------------------------------
 
 #include "nsIPrefBranch.h"
 #include "nsIPrefService.h"
 #include "nsIServiceManager.h"
 #include "nsIHttpAuthenticableChannel.h"
 #include "nsIURI.h"
+#include "nsIX509Cert.h"
+#include "nsISSLStatus.h"
+#include "nsISSLStatusProvider.h"
 
 static const char kAllowProxies[] = "network.automatic-ntlm-auth.allow-proxies";
 static const char kTrustedURIs[]  = "network.automatic-ntlm-auth.trusted-uris";
 static const char kForceGeneric[] = "network.auth.force-generic-ntlm";
 
 // XXX MatchesBaseURI and TestPref are duplicated in nsHttpNegotiateAuth.cpp,
 // but since that file lives in a separate library we cannot directly share it.
 // bug 236865 addresses this problem.
@@ -230,16 +234,19 @@ nsHttpNTLMAuth::ChallengeReceived(nsIHtt
                                   PRBool          isProxyAuth,
                                   nsISupports   **sessionState,
                                   nsISupports   **continuationState,
                                   PRBool         *identityInvalid)
 {
     LOG(("nsHttpNTLMAuth::ChallengeReceived [ss=%p cs=%p]\n",
          *sessionState, *continuationState));
 
+    // Use the native NTLM if available
+    mUseNative = PR_TRUE;
+
     // NOTE: we don't define any session state, but we do use the pointer.
 
     *identityInvalid = PR_FALSE;
 
     // Start a new auth sequence if the challenge is exactly "NTLM".
     // If native NTLM auth apis are available and enabled through prefs,
     // try to use them.
     if (PL_strcasecmp(challenge, "NTLM") == 0) {
@@ -293,16 +300,18 @@ nsHttpNTLMAuth::ChallengeReceived(nsIHtt
                     return NS_ERROR_OUT_OF_MEMORY;
                 NS_ADDREF(*sessionState);
             }
 
             // Use our internal NTLM implementation. Note, this is less secure,
             // see bug 520607 for details.
             LOG(("Trying to fall back on internal ntlm auth.\n"));
             module = do_CreateInstance(NS_AUTH_MODULE_CONTRACTID_PREFIX "ntlm");
+	    
+            mUseNative = PR_FALSE;
 
             // Prompt user for domain, username, and password.
             *identityInvalid = PR_TRUE;
         }
 
         // If this fails, then it means that we cannot do NTLM auth.
         if (!module) {
             LOG(("No ntlm auth modules available.\n"));
@@ -361,18 +370,75 @@ nsHttpNTLMAuth::GenerateCredentials(nsIH
             return rv;
         serviceName.AppendLiteral("HTTP@");
         serviceName.Append(host);
         // initialize auth module
         rv = module->Init(serviceName.get(), nsIAuthModule::REQ_DEFAULT, domain, user, pass);
         if (NS_FAILED(rv))
             return rv;
 
+// This update enables updated Windows machines (Win7 or patched previous
+// versions) and Linux machines running Samba (updated for Channel 
+// Binding), to perform Channel Binding when authenticating using NTLMv2 
+// and an outer secure channel.
+// 
+// Currently only implemented for Windows, linux support will be landing in 
+// a separate patch, update this #ifdef accordingly then.
+#if defined (XP_WIN) /* || defined (LINUX) */
+        PRBool isHttps;
+        rv = uri->SchemeIs("https", &isHttps);
+        if (NS_FAILED(rv))
+            return rv;
+            
+        // When the url starts with https, we should retrieve the server 
+        // certificate and compute the CBT, but only when we are using
+        // the native NTLM implementation and not the internal one.
+        if (isHttps && mUseNative) {
+            nsCOMPtr<nsIChannel> channel = do_QueryInterface(authChannel, &rv);
+            if (NS_FAILED(rv))
+                return rv;
+
+            nsCOMPtr<nsISupports> security;
+            rv = channel->GetSecurityInfo(getter_AddRefs(security));
+            if (NS_FAILED(rv))
+                return rv;
+
+            nsCOMPtr<nsISSLStatusProvider> 
+                        statusProvider(do_QueryInterface(security));
+            NS_ENSURE_TRUE(statusProvider, NS_ERROR_FAILURE);
+
+            rv = statusProvider->GetSSLStatus(getter_AddRefs(security));
+            if (NS_FAILED(rv))
+                return rv;
+
+            nsCOMPtr<nsISSLStatus> status(do_QueryInterface(security));
+            NS_ENSURE_TRUE(status, NS_ERROR_FAILURE);
+
+            nsCOMPtr<nsIX509Cert> cert;
+            rv = status->GetServerCert(getter_AddRefs(cert));
+            if (NS_FAILED(rv))
+                return rv;
+
+            PRUint32 length;
+            PRUint8* certArray;
+            cert->GetRawDER(&length, &certArray);						  
+			
+            // If there is a server certificate, we pass it along the
+            // first time we call GetNextToken().
+            inBufLen = length;
+            inBuf = certArray;
+        } else { 
+            // If there is no server certificate, we don't pass anything.
+            inBufLen = 0;
+            inBuf = nsnull;
+        }
+#else // Extended protection update is just for Linux and Windows machines.
         inBufLen = 0;
         inBuf = nsnull;
+#endif
     }
     else {
         // decode challenge; skip past "NTLM " to the start of the base64
         // encoded data.
         int len = strlen(challenge);
         if (len < 6)
             return NS_ERROR_UNEXPECTED; // bogus challenge
         challenge += 5;
--- a/netwerk/protocol/http/nsHttpNTLMAuth.h
+++ b/netwerk/protocol/http/nsHttpNTLMAuth.h
@@ -15,16 +15,17 @@
  *
  * The Initial Developer of the Original Code is
  * Netscape Communications Corporation.
  * Portions created by the Initial Developer are Copyright (C) 2003
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
  *   Darin Fisher <darin@netscape.com>
+ *   Guillermo Robla Vicario <groblavicario@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
@@ -43,11 +44,16 @@
 class nsHttpNTLMAuth : public nsIHttpAuthenticator
 {
 public:
     NS_DECL_ISUPPORTS
     NS_DECL_NSIHTTPAUTHENTICATOR
 
     nsHttpNTLMAuth() {}
     virtual ~nsHttpNTLMAuth() {}
+
+private:
+    // This flag indicates whether we are using the native NTLM implementation
+    // or the internal one.
+    PRBool  mUseNative;
 };
 
 #endif // !nsHttpNTLMAuth_h__
--- a/netwerk/protocol/http/nsHttpTransaction.cpp
+++ b/netwerk/protocol/http/nsHttpTransaction.cpp
@@ -682,16 +682,19 @@ nsHttpTransaction::Restart()
 char *
 nsHttpTransaction::LocateHttpStart(char *buf, PRUint32 len,
                                    PRBool aAllowPartialMatch)
 {
     NS_ASSERTION(!aAllowPartialMatch || mLineBuf.IsEmpty(), "ouch");
 
     static const char HTTPHeader[] = "HTTP/1.";
     static const PRInt32 HTTPHeaderLen = sizeof(HTTPHeader) - 1;
+    
+    if (aAllowPartialMatch && (len < HTTPHeaderLen))
+        return (PL_strncasecmp(buf, HTTPHeader, len) == 0) ? buf : nsnull;
 
     // mLineBuf can contain partial match from previous search
     if (!mLineBuf.IsEmpty()) {
         NS_ASSERTION(mLineBuf.Length() < HTTPHeaderLen, "ouch");
         PRInt32 checkChars = PR_MIN(len, HTTPHeaderLen - mLineBuf.Length());
         if (PL_strncasecmp(buf, HTTPHeader + mLineBuf.Length(),
                            checkChars) == 0) {
             mLineBuf.Append(buf, checkChars);
@@ -709,23 +712,19 @@ nsHttpTransaction::LocateHttpStart(char 
         // pattern. Start the search again.
         mLineBuf.Truncate();
     }
 
     while (len > 0) {
         if (PL_strncasecmp(buf, HTTPHeader, PR_MIN(len, HTTPHeaderLen)) == 0) {
             if (len < HTTPHeaderLen) {
                 // partial HTTPHeader sequence found
-                if (aAllowPartialMatch) {
-                    return buf;
-                } else {
-                    // save partial match to mLineBuf
-                    mLineBuf.Assign(buf, len);
-                    return 0;
-                }
+                // save partial match to mLineBuf
+                mLineBuf.Assign(buf, len);
+                return 0;
             }
 
             // whole HTTPHeader sequence found
             return buf;
         }
         buf++;
         len--;
     }
@@ -823,17 +822,17 @@ nsHttpTransaction::ParseHead(char *buf,
         // Normally we insist on seeing HTTP/1.x in the first few bytes,
         // but if we are on a persistent connection and the previous transaction
         // was not supposed to have any content then we need to be prepared
         // to skip over a response body that the server may have sent even
         // though it wasn't allowed.
         if (!mConnection || !mConnection->LastTransactionExpectedNoContent()) {
             // tolerate only minor junk before the status line
             mHttpResponseMatched = PR_TRUE;
-            char *p = LocateHttpStart(buf, PR_MIN(count, 8), PR_TRUE);
+            char *p = LocateHttpStart(buf, PR_MIN(count, 11), PR_TRUE);
             if (!p) {
                 // Treat any 0.9 style response of a put as a failure.
                 if (mRequestHead->Method() == nsHttp::Put)
                     return NS_ERROR_ABORT;
 
                 mResponseHead->ParseStatusLine("");
                 mHaveStatusLine = PR_TRUE;
                 mHaveAllHeaders = PR_TRUE;
--- a/security/manager/boot/public/Makefile.in
+++ b/security/manager/boot/public/Makefile.in
@@ -45,13 +45,12 @@ include $(DEPTH)/config/autoconf.mk
 MODULE = pipboot
 GRE_MODULE	= 1
 
 SDK_XPIDLSRCS = \
     nsISecurityWarningDialogs.idl \
     $(NULL)
 
 XPIDLSRCS = \
-    nsISSLStatusProvider.idl \
     nsIBufEntropyCollector.idl \
     $(NULL)
 
 include $(topsrcdir)/config/rules.mk
--- a/security/manager/ssl/public/Makefile.in
+++ b/security/manager/ssl/public/Makefile.in
@@ -48,17 +48,16 @@ include $(DEPTH)/config/autoconf.mk
 MODULE = pipnss
 GRE_MODULE	= 1
 
 SDK_XPIDLSRCS = \
     nsIASN1Object.idl \
     nsIASN1Sequence.idl \
     nsICertificateDialogs.idl \
     nsICRLInfo.idl \
-    nsIX509Cert.idl \
     nsIX509CertDB.idl \
     nsIX509CertValidity.idl \
     $(NULL)
 
 XPIDLSRCS = \
     nsISSLCertErrorDialog.idl \
     nsIBadCertListener2.idl \
     nsISSLErrorListener.idl \
@@ -75,17 +74,16 @@ XPIDLSRCS = \
     nsIPKCS11Slot.idl \
     nsIPK11TokenDB.idl \
     nsICertPickDialogs.idl \
     nsIClientAuthDialogs.idl \
     nsIDOMCryptoDialogs.idl \
     nsIGenKeypairInfoDlg.idl \
     nsITokenDialogs.idl \
     nsITokenPasswordDialogs.idl \
-    nsISSLStatus.idl \
     nsIKeygenThread.idl \
     nsICMSSecureMessage.idl \
     nsIUserCertPicker.idl \
     nsIASN1PrintableItem.idl \
     nsICMSDecoder.idl \
     nsICMSEncoder.idl \
     nsICMSMessageErrors.idl \
     nsICMSMessage.idl \
--- a/toolkit/components/console/hudservice/HUDService.jsm
+++ b/toolkit/components/console/hudservice/HUDService.jsm
@@ -2691,30 +2691,19 @@ HUD_SERVICE.prototype =
     else {
       hud = this.hudReferences[hudId];
       if (aContentWindow == aContentWindow.top) {
         // TODO: name change?? doesn't actually re-attach the console
         hud.reattachConsole(aContentWindow);
       }
     }
 
-    // Need to detect that the console component has been paved over. Do this by
-    // checking whether its global object is equal to that of an object
-    // returned by our native ConsoleAPI nsIDOMGlobalPropertyInitializer.
+    // Need to detect that the console component has been paved over.
     let consoleObject = unwrap(aContentWindow).console;
-    let consoleGlobal = Cu.getGlobalForObject(consoleObject);
-
-    let nativeConsoleObj = Cc["@mozilla.org/console-api;1"].
-                           createInstance(Ci.nsIDOMGlobalPropertyInitializer).
-                           init(aContentWindow);
-    let nativeConsoleGlobal = Cu.getGlobalForObject(nativeConsoleObj);
-
-    // Need a "===" comparison because backstagepass objects have strange
-    // behavior with ==
-    if (consoleGlobal !== nativeConsoleGlobal)
+    if (!("__mozillaConsole__" in consoleObject))
       this.logWarningAboutReplacedAPI(hudId);
 
     // register the controller to handle "select all" properly
     this.createController(xulWindow);
   },
 
   /**
    * Adds the command controller to the XUL window if it's not already present.
--- a/toolkit/mozapps/extensions/content/extensions-content.js
+++ b/toolkit/mozapps/extensions/content/extensions-content.js
@@ -44,31 +44,29 @@ const Cu = Components.utils;
 const MSG_INSTALL_ENABLED  = "WebInstallerIsInstallEnabled";
 const MSG_INSTALL_ADDONS   = "WebInstallerInstallAddonsFromWebpage";
 const MSG_INSTALL_CALLBACK = "WebInstallerInstallCallback";
 
 var gIoService = Components.classes["@mozilla.org/network/io-service;1"]
                            .getService(Components.interfaces.nsIIOService);
 
 function createInstallTrigger(window) {
-  return {
+  let chromeObject = {
     window: window,
 
     __exposedProps__: {
       SKIN: "r",
       LOCALE: "r",
       CONTENT: "r",
       PACKAGE: "r",
       enabled: "r",
       updateEnabled: "r",
       install: "r",
       installChrome: "r",
-      startSoftwareUpdate: "r",
-      toString: "r",
-      toSource: "r", // XXX workaround for bug 582100
+      startSoftwareUpdate: "r"
     },
 
     // == Public interface ==
 
     SKIN: Ci.amIInstallTrigger.SKIN,
     LOCALE: Ci.amIInstallTrigger.LOCALE,
     CONTENT: Ci.amIInstallTrigger.CONTENT,
     PACKAGE: Ci.amIInstallTrigger.PACKAGE,
@@ -183,16 +181,36 @@ function createInstallTrigger(window) {
           Ci.nsIScriptSecurityManager.DISALLOW_INHERIT_PRINCIPAL);
         return true;
       }
       catch(e) {
         return false;
       }
     }
   };
+
+  let sandbox = Cu.Sandbox(window);
+  let obj = Cu.evalInSandbox(
+    "(function (x) {\
+       var bind = Function.bind;\
+       return {\
+         enabled: bind.call(x.enabled, x),\
+         updateEnabled: bind.call(x.updateEnabled, x),\
+         install: bind.call(x.install, x),\
+         installChrome: bind.call(x.installChrome, x),\
+         startSoftwareUpdate: bind.call(x.startSoftwareUpdate, x)\
+       };\
+     })", sandbox)(chromeObject);
+
+  obj.SKIN = chromeObject.SKIN;
+  obj.LOCALE = chromeObject.LOCALE;
+  obj.CONTENT = chromeObject.CONTENT;
+  obj.PACKAGE = chromeObject.PACKAGE;
+
+  return obj;
 };
 
 /**
  * Child part of InstallTrigger e10s handling.
  *
  * Sets up InstallTrigger for newly-created windows,
  * that will relay messages for InstallTrigger
  * activity. We also process the parameters for
--- a/toolkit/system/gnome/nsAlertsIconListener.cpp
+++ b/toolkit/system/gnome/nsAlertsIconListener.cpp
@@ -41,16 +41,21 @@
 #include "imgIRequest.h"
 #include "nsNetUtil.h"
 #include "nsIImageToPixbuf.h"
 #include "nsIStringBundle.h"
 #include "nsIObserverService.h"
 
 #include <gdk/gdk.h>
 
+// Compatibility macro for <libnotify-0.7
+#ifndef NOTIFY_CHECK_VERSION
+#define NOTIFY_CHECK_VERSION(x,y,z) 0
+#endif
+
 static PRBool gHasActions = PR_FALSE;
 
 static void notify_action_cb(NotifyNotification *notification,
                              gchar *action, gpointer user_data)
 {
   nsAlertsIconListener* alert = static_cast<nsAlertsIconListener*> (user_data);
   alert->SendCallback();
 }
@@ -213,17 +218,23 @@ nsAlertsIconListener::OnStopFrame(imgIRe
   return NS_OK;
 }
 
 nsresult
 nsAlertsIconListener::ShowAlert(GdkPixbuf* aPixbuf)
 {
   mNotification = notify_notification_new(mAlertTitle.get(),
                                           mAlertText.get(),
-                                          NULL, NULL);
+                                          NULL
+// >=libnotify-0.7.0 has no support for attaching to widgets
+#if !NOTIFY_CHECK_VERSION(0,7,0)
+                                          , NULL
+#endif
+                                          );
+
   if (!mNotification)
     return NS_ERROR_OUT_OF_MEMORY;
 
   if (aPixbuf)
     notify_notification_set_icon_from_pixbuf(mNotification, aPixbuf);
 
   NS_ADDREF(this);
   if (mAlertHasAction) {