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>
Wed, 16 Feb 2011 16:43:30 -0600
changeset 62697 f66564b1510dbc20e7a88205c9ea834073c44368
parent 62696 aa7b2f04d225f08c59d830fdaf41575ce71d1334
child 62698 993a5de16346bfce428a979846fb1feed9f2d836
push id18833
push usercjones@mozilla.com
push dateWed, 16 Feb 2011 22:44:38 +0000
treeherdermozilla-central@0f777e59d48c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbas, cjones
bugs626602
milestone2.0b12pre
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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.
  */
@@ -390,16 +392,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
@@ -499,23 +506,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.
@@ -924,22 +933,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
@@ -952,16 +965,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:
@@ -1021,22 +1039,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.
    */
@@ -1049,16 +1073,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.