Bug 626602. Part 1: Create ReadbackLayer API to enable collection of background pixels in a layer tree. r=bas,sr=cjones
authorRobert O'Callahan <robert@ocallahan.org>
Tue, 08 Feb 2011 18:44:13 -0600
changeset 62199 c7f068259a8ae0f6e4e0323e84ca7f52033d1066
parent 62198 0fc46487e027ddba0cfb4c72b13e6aac4eaccc8c
child 62200 5300b2ae26ade1f858f4489eea872a82ebcf8485
push idunknown
push userunknown
push dateunknown
reviewersbas, cjones
bugs626602
milestone2.0b12pre
Bug 626602. Part 1: Create ReadbackLayer API to enable collection of background pixels in a layer tree. r=bas,sr=cjones
gfx/layers/Layers.cpp
gfx/layers/Layers.h
gfx/layers/Makefile.in
gfx/layers/ReadbackLayer.h
gfx/layers/ReadbackProcessor.cpp
gfx/layers/ReadbackProcessor.h
gfx/layers/basic/BasicLayers.cpp
gfx/layers/d3d10/ContainerLayerD3D10.cpp
gfx/layers/d3d9/ContainerLayerD3D9.cpp
gfx/layers/opengl/ContainerLayerOGL.cpp
gfx/thebes/gfxColor.h
--- 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)
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
@@ -231,38 +231,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 +283,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:
--- a/gfx/layers/d3d10/ContainerLayerD3D10.cpp
+++ b/gfx/layers/d3d10/ContainerLayerD3D10.cpp
@@ -67,31 +67,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 +103,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 +121,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*
--- a/gfx/layers/d3d9/ContainerLayerD3D9.cpp
+++ b/gfx/layers/d3d9/ContainerLayerD3D9.cpp
@@ -66,31 +66,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 +102,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 +120,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*
--- 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/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.