Bug 564993. Part 5: Change ThebesLayer painting to be callback-based; move layer tree construction to FrameLayerBuilder. r=Bas,mats,sr=vlad
authorRobert O'Callahan <robert@ocallahan.org>
Fri, 21 May 2010 15:20:48 +1200
changeset 42593 9088249086458245908fa25040c3fd45f47ee689
parent 42592 d87b05b33161a5d8d9847289f7429f54d42895cd
child 42594 c2223399d4b4b91c44519fa7a1dec1cb983733c4
push idunknown
push userunknown
push dateunknown
reviewersBas, mats, vlad
bugs564993
milestone1.9.3a5pre
Bug 564993. Part 5: Change ThebesLayer painting to be callback-based; move layer tree construction to FrameLayerBuilder. r=Bas,mats,sr=vlad
embedding/browser/webBrowser/nsWebBrowser.cpp
gfx/layers/Layers.h
gfx/layers/basic/BasicLayers.cpp
gfx/layers/basic/BasicLayers.h
gfx/layers/opengl/CanvasLayerOGL.cpp
gfx/layers/opengl/CanvasLayerOGL.h
gfx/layers/opengl/ColorLayerOGL.cpp
gfx/layers/opengl/ColorLayerOGL.h
gfx/layers/opengl/ContainerLayerOGL.cpp
gfx/layers/opengl/ContainerLayerOGL.h
gfx/layers/opengl/ImageLayerOGL.cpp
gfx/layers/opengl/ImageLayerOGL.h
gfx/layers/opengl/LayerManagerOGL.cpp
gfx/layers/opengl/LayerManagerOGL.h
gfx/layers/opengl/ThebesLayerOGL.cpp
gfx/layers/opengl/ThebesLayerOGL.h
layout/base/FrameLayerBuilder.cpp
layout/base/FrameLayerBuilder.h
layout/base/Makefile.in
layout/base/nsDisplayList.cpp
layout/base/nsDisplayList.h
layout/base/nsPresShell.cpp
--- a/embedding/browser/webBrowser/nsWebBrowser.cpp
+++ b/embedding/browser/webBrowser/nsWebBrowser.cpp
@@ -1661,16 +1661,29 @@ NS_IMETHODIMP nsWebBrowser::EnsureDocShe
    NS_ENSURE_TRUE(mDocShellTreeOwner, NS_ERROR_OUT_OF_MEMORY);
 
    NS_ADDREF(mDocShellTreeOwner);
    mDocShellTreeOwner->WebBrowser(this);
    
    return NS_OK;
 }
 
+static void DrawThebesLayer(ThebesLayer* aLayer,
+                            gfxContext* aContext,
+                            const nsIntRegion& aRegionToDraw,
+                            void* aCallbackData)
+{
+  nscolor* color = static_cast<nscolor*>(aCallbackData);
+  aContext->NewPath();
+  aContext->SetColor(gfxRGBA(*color));
+  nsIntRect dirtyRect = aRegionToDraw.GetBounds();
+  aContext->Rectangle(gfxRect(dirtyRect.x, dirtyRect.y, dirtyRect.width, dirtyRect.height));
+  aContext->Fill();  
+}
+
 /* static */
 nsEventStatus nsWebBrowser::HandleEvent(nsGUIEvent *aEvent)
 {
   nsWebBrowser  *browser = nsnull;
   void          *data = nsnull;
   nsIWidget     *widget = aEvent->widget;
 
   if (!widget)
@@ -1691,29 +1704,17 @@ nsEventStatus nsWebBrowser::HandleEvent(
       layerManager->BeginTransaction();
       nsRefPtr<ThebesLayer> root = layerManager->CreateThebesLayer();
       nsPaintEvent* paintEvent = static_cast<nsPaintEvent*>(aEvent);
       nsIntRect dirtyRect = paintEvent->region.GetBounds();
       if (root) {
           root->SetVisibleRegion(dirtyRect);
           layerManager->SetRoot(root);
       }
-      layerManager->EndConstruction();
-      if (root) {
-          nsIntRegion toDraw;
-          gfxContext* ctx = root->BeginDrawing(&toDraw);
-          if (ctx) {
-              ctx->NewPath();
-              ctx->SetColor(gfxRGBA(browser->mBackgroundColor));
-              ctx->Rectangle(gfxRect(dirtyRect.x, dirtyRect.y, dirtyRect.width, dirtyRect.height));
-              ctx->Fill();
-          }
-      }
-      root->EndDrawing();
-      layerManager->EndTransaction();
+      layerManager->EndTransaction(DrawThebesLayer, &browser->mBackgroundColor);
       return nsEventStatus_eConsumeDoDefault;
     }
 
   case NS_ACTIVATE: {
 #if defined(DEBUG_smaug)
     nsCOMPtr<nsIDOMDocument> domDocument = do_GetInterface(browser->mDocShell);
     nsAutoString documentURI;
     if (domDocument) {
--- a/gfx/layers/Layers.h
+++ b/gfx/layers/Layers.h
@@ -138,24 +138,39 @@ public:
    * Start a new transaction. Nested transactions are not allowed so
    * there must be no transaction currently in progress. 
    * This transaction will render the contents of the layer tree to
    * the given target context. The rendering will be complete when
    * EndTransaction returns.
    */
   virtual void BeginTransactionWithTarget(gfxContext* aTarget) = 0;
   /**
-   * Finish the construction phase of the transaction and enter the
-   * drawing phase.
+   * Function called to draw the contents of each ThebesLayer.
+   * aRegionToDraw contains the region that needs to be drawn.
+   * This would normally be a subregion of the visible region. Drawing is
+   * not necessarily clipped to aRegionToDraw.
+   * The callee must draw all of aRegionToDraw.
+   * 
+   * aContext must not be used after the call has returned.
+   * We guarantee that buffered contents in the visible
+   * region are valid once drawing is complete.
    */
-  virtual void EndConstruction() = 0;
+  typedef void (* DrawThebesLayerCallback)(ThebesLayer* aLayer,
+                                           gfxContext* aContext,
+                                           const nsIntRegion& aRegionToDraw,
+                                           void* aCallbackData);
   /**
-   * Complete the transaction.
+   * Finish the construction phase of the transaction, perform the
+   * drawing phase, and end the transaction.
+   * During the drawing phase, all ThebesLayers in the tree are
+   * drawn in tree order, exactly once each, except for those layers
+   * where it is known that the visible region is empty.
    */
-  virtual void EndTransaction() = 0;
+  virtual void EndTransaction(DrawThebesLayerCallback aCallback,
+                              void* aCallbackData) = 0;
 
   /**
    * CONSTRUCTION PHASE ONLY
    * Set the root layer.
    */
   virtual void SetRoot(Layer* aLayer) = 0;
 
   /**
@@ -192,16 +207,18 @@ public:
   /**
    * Type of layer manager his is. This is to be used sparsely in order to
    * avoid a lot of Layers backend specific code. It should be used only when
    * Layers backend specific functionality is necessary.
    */
   virtual LayersBackend GetBackendType() = 0;
 };
 
+class ThebesLayer;
+
 /**
  * A Layer represents anything that can be rendered onto a destination
  * surface.
  */
 class THEBES_API Layer {
   NS_INLINE_DECL_REFCOUNTING(Layer)  
 
 public:
@@ -293,16 +310,22 @@ public:
   virtual Layer* GetFirstChild() { return nsnull; }
   const gfx3DMatrix& GetTransform() { return mTransform; }
 
   // This setter and getter can be used anytime.
   void SetUserData(void* aData) { mUserData = aData; }
   void* GetUserData() { return mUserData; }
 
   /**
+   * Dynamic downcast to a Thebes layer. Returns null if this is not
+   * a ThebesLayer.
+   */
+  virtual ThebesLayer* AsThebesLayer() { return nsnull; }
+  
+  /**
    * Only the implementation should call this. This is per-implementation
    * private data. Normally, all layers with a given layer manager
    * use the same type of ImplData.
    */
   void* ImplData() { return mImplData; }
 
   /**
    * Only the implementation should use these methods.
@@ -353,46 +376,17 @@ public:
   /**
    * CONSTRUCTION PHASE ONLY
    * Tell this layer that the content in some region has changed and
    * will need to be repainted. This area is removed from the valid
    * region.
    */
   virtual void InvalidateRegion(const nsIntRegion& aRegion) = 0;
 
-  /**
-   * DRAWING PHASE ONLY
-   * Start drawing into the layer. On return, aRegionToDraw contains the
-   * region that needs to be drawn in by the caller. This would normally
-   * be a subregion of the visible region. Drawing is not necessarily
-   * clipped to aRegionToDraw.
-   * 
-   * No other layer operations are allowed until we call EndDrawing on this
-   * layer. During the drawing phase, all ThebesLayers in the tree must be
-   * drawn in tree order, exactly once each, except for those layers
-   * where it is known that the visible region is empty. (Calling
-   * BeginDrawing on non-visible layers is allowed, but aRegionToDraw
-   * will return empty.)
-   * 
-   * When an empty region is returned in aRegionToDraw, BeginDrawing
-   * may return a null context.
-   * 
-   * The layer system will hold a reference to the returned gfxContext*
-   * until EndDrawing is called. The returned gfxContext must not be used
-   * after EndDrawing is called.
-   */
-  virtual gfxContext* BeginDrawing(nsIntRegion* aRegionToDraw) = 0;
-  /**
-   * DRAWING PHASE ONLY
-   * We've finished drawing into this layer. At this point the caller
-   * must have drawn all of aRegionToDraw that was returned by
-   * BeginDrawing, and we guarantee that buffered contents in the visible
-   * region are now valid.
-   */
-  virtual void EndDrawing() = 0;
+  virtual ThebesLayer* AsThebesLayer() { return this; }
 
 protected:
   ThebesLayer(LayerManager* aManager, void* aImplData)
     : Layer(aManager, aImplData) {}
 };
 
 /**
  * A Layer which other layers render into. It holds references to its
--- a/gfx/layers/basic/BasicLayers.cpp
+++ b/gfx/layers/basic/BasicLayers.cpp
@@ -89,17 +89,19 @@ public:
   const nsIntRegion& GetVisibleRegion() { return mVisibleRegion; }
 
   /**
    * 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) {}
+  virtual void Paint(gfxContext* aContext,
+                     LayerManager::DrawThebesLayerCallback aCallback,
+                     void* aCallbackData) {}
 
 protected:
   nsIntRegion mVisibleRegion;
 };
 
 static BasicImplData*
 ToData(Layer* aLayer)
 {
@@ -235,50 +237,38 @@ public:
     mVisibleRegion = aRegion;
   }
   virtual void InvalidateRegion(const nsIntRegion& aRegion)
   {
     NS_ASSERTION(BasicManager()->InConstruction(),
                  "Can only set properties in construction phase");
   }
 
-  virtual gfxContext* BeginDrawing(nsIntRegion* aRegionToDraw);
-  virtual void EndDrawing();
+  virtual void Paint(gfxContext* aContext,
+                     LayerManager::DrawThebesLayerCallback aCallback,
+                     void* aCallbackData);
 
 protected:
   BasicLayerManager* BasicManager()
   {
     return static_cast<BasicLayerManager*>(mManager);
   }
 };
 
-gfxContext*
-BasicThebesLayer::BeginDrawing(nsIntRegion* aRegionToDraw)
+void
+BasicThebesLayer::Paint(gfxContext* aContext,
+                        LayerManager::DrawThebesLayerCallback aCallback,
+                        void* aCallbackData)
 {
-  NS_ASSERTION(BasicManager()->IsBeforeInTree(BasicManager()->GetLastPainted(), this),
-               "Painting layers out of order");
   NS_ASSERTION(BasicManager()->InDrawing(),
                "Can only draw in drawing phase");
   gfxContext* target = BasicManager()->GetTarget();
-  if (!target)
-    return nsnull;
-
-  BasicManager()->AdvancePaintingTo(this);
+  NS_ASSERTION(target, "We shouldn't be called if there's no target");
 
-  *aRegionToDraw = mVisibleRegion;
-  return target;
-}
-
-void
-BasicThebesLayer::EndDrawing()
-{
-  NS_ASSERTION(BasicManager()->InDrawing(),
-               "Can only draw in drawing phase");
-  NS_ASSERTION(BasicManager()->GetLastPainted() == this,
-               "Not currently drawing this layer");
+  aCallback(this, target, mVisibleRegion, aCallbackData);
 }
 
 class BasicImageLayer : public ImageLayer, BasicImplData {
 public:
   BasicImageLayer(BasicLayerManager* aLayerManager) :
     ImageLayer(aLayerManager, static_cast<BasicImplData*>(this))
   {
     MOZ_COUNT_CTOR(BasicImageLayer);
@@ -290,27 +280,31 @@ public:
 
   virtual void SetVisibleRegion(const nsIntRegion& aRegion)
   {
     NS_ASSERTION(BasicManager()->InConstruction(),
                  "Can only set properties in construction phase");
     mVisibleRegion = aRegion;
   }
 
-  virtual void Paint(gfxContext* aContext);
+  virtual void Paint(gfxContext* aContext,
+                     LayerManager::DrawThebesLayerCallback aCallback,
+                     void* aCallbackData);
 
 protected:
   BasicLayerManager* BasicManager()
   {
     return static_cast<BasicLayerManager*>(mManager);
   }
 };
 
 void
-BasicImageLayer::Paint(gfxContext* aContext)
+BasicImageLayer::Paint(gfxContext* aContext,
+                       LayerManager::DrawThebesLayerCallback aCallback,
+                       void* aCallbackData)
 {
   if (!mContainer)
     return;
 
   gfxIntSize size;
   nsRefPtr<gfxASurface> surface = mContainer->GetCurrentAsSurface(&size);
   if (!surface) {
     return;
@@ -360,27 +354,31 @@ public:
 
   virtual void SetVisibleRegion(const nsIntRegion& aRegion)
   {
     NS_ASSERTION(BasicManager()->InConstruction(),
                  "Can only set properties in construction phase");
     mVisibleRegion = aRegion;
   }
 
-  virtual void Paint(gfxContext* aContext);
+  virtual void Paint(gfxContext* aContext,
+                     LayerManager::DrawThebesLayerCallback aCallback,
+                     void* aCallbackData);
 
 protected:
   BasicLayerManager* BasicManager()
   {
     return static_cast<BasicLayerManager*>(mManager);
   }
 };
 
 void
-BasicColorLayer::Paint(gfxContext* aContext)
+BasicColorLayer::Paint(gfxContext* aContext,
+                       LayerManager::DrawThebesLayerCallback aCallback,
+                       void* aCallbackData)
 {
   aContext->SetColor(mColor);
   aContext->Paint();
 }
 
 class BasicCanvasLayer : public CanvasLayer,
                          BasicImplData
 {
@@ -392,17 +390,19 @@ public:
   }
   virtual ~BasicCanvasLayer()
   {
     MOZ_COUNT_DTOR(BasicCanvasLayer);
   }
 
   virtual void Initialize(const Data& aData);
   virtual void Updated(const nsIntRect& aRect);
-  virtual void Paint(gfxContext* aContext);
+  virtual void Paint(gfxContext* aContext,
+                     LayerManager::DrawThebesLayerCallback aCallback,
+                     void* aCallbackData);
 
 protected:
   nsRefPtr<gfxASurface> mSurface;
   nsRefPtr<mozilla::gl::GLContext> mGLContext;
 
   nsIntRect mBounds;
   nsIntRect mUpdatedRect;
 
@@ -479,17 +479,19 @@ BasicCanvasLayer::Updated(const nsIntRec
   }
 
   // sanity
   NS_ASSERTION(mUpdatedRect.IsEmpty() || mBounds.Contains(mUpdatedRect),
                "CanvasLayer: Updated rect bigger than bounds!");
 }
 
 void
-BasicCanvasLayer::Paint(gfxContext* aContext)
+BasicCanvasLayer::Paint(gfxContext* aContext,
+                        LayerManager::DrawThebesLayerCallback aCallback,
+                        void* aCallbackData)
 {
   nsRefPtr<gfxPattern> pat = new gfxPattern(mSurface);
 
   pat->SetFilter(mFilter);
   pat->SetExtend(gfxPattern::EXTEND_PAD);
 
   gfxRect r(0, 0, mBounds.width, mBounds.height);
   gfxMatrix m;
@@ -506,17 +508,17 @@ BasicCanvasLayer::Paint(gfxContext* aCon
   if (mNeedsYFlip) {
     aContext->SetMatrix(m);
   }
 
   mUpdatedRect.Empty();
 }
 
 BasicLayerManager::BasicLayerManager(gfxContext* aContext) :
-  mDefaultTarget(aContext), mLastPainted(nsnull)
+  mDefaultTarget(aContext)
 #ifdef DEBUG
   , mPhase(PHASE_NONE)
 #endif
 {
   MOZ_COUNT_CTOR(BasicLayerManager);
 }
 
 BasicLayerManager::~BasicLayerManager()
@@ -549,34 +551,33 @@ BasicLayerManager::BeginTransactionWithT
   NS_ASSERTION(mPhase == PHASE_NONE, "Nested transactions not allowed");
 #ifdef DEBUG
   mPhase = PHASE_CONSTRUCTION;
 #endif
   mTarget = aTarget;
 }
 
 void
-BasicLayerManager::EndConstruction()
+BasicLayerManager::EndTransaction(DrawThebesLayerCallback aCallback,
+                                  void* aCallbackData)
 {
   NS_ASSERTION(mRoot, "Root not set");
   NS_ASSERTION(mPhase == PHASE_CONSTRUCTION, "Should be in construction phase");
 #ifdef DEBUG
   mPhase = PHASE_DRAWING;
 #endif
-}
 
-void
-BasicLayerManager::EndTransaction()
-{
-  NS_ASSERTION(mPhase == PHASE_DRAWING, "Should be in drawing phase");
+  if (mTarget) {
+    PaintLayer(mRoot, aCallback, aCallbackData);
+    mTarget = nsnull;
+  }
+
 #ifdef DEBUG
   mPhase = PHASE_NONE;
 #endif
-  AdvancePaintingTo(nsnull);
-  mTarget = nsnull;
   // No retained layers supported for now
   mRoot = nsnull;
 }
 
 void
 BasicLayerManager::SetRoot(Layer* aLayer)
 {
   NS_ASSERTION(aLayer, "Root can't be null");
@@ -620,20 +621,24 @@ UseOpaqueSurface(Layer* aLayer)
   // in the container.
   BasicContainerLayer* parent =
     static_cast<BasicContainerLayer*>(aLayer->GetParent());
   return parent && parent->GetFirstChild() == aLayer &&
          UseOpaqueSurface(parent);
 }
 
 void
-BasicLayerManager::BeginPaintingLayer(Layer* aLayer)
+BasicLayerManager::PaintLayer(Layer* aLayer,
+                              DrawThebesLayerCallback aCallback,
+                              void* aCallbackData)
 {
   PRBool needsGroup = NeedsGroup(aLayer);
-  if ((needsGroup || NeedsState(aLayer)) && mTarget) {
+  PRBool needsSaveRestore = needsGroup || NeedsState(aLayer);
+
+ if (needsSaveRestore) {
     mTarget->Save();
 
     if (aLayer->GetClipRect()) {
       const nsIntRect& r = *aLayer->GetClipRect();
       mTarget->NewPath();
       mTarget->Rectangle(gfxRect(r.x, r.y, r.width, r.height), PR_TRUE);
       mTarget->Clip();
     }
@@ -662,76 +667,32 @@ BasicLayerManager::BeginPaintingLayer(La
       mTarget->SetMatrix(currentMatrix);
 
       gfxASurface::gfxContentType type = UseOpaqueSurface(aLayer)
           ? gfxASurface::CONTENT_COLOR : gfxASurface::CONTENT_COLOR_ALPHA;
       mTarget->PushGroup(type);
     }
   }
 
-  mLastPainted = aLayer;
+  ToData(aLayer)->Paint(mTarget, aCallback, aCallbackData);
+  for (Layer* child = aLayer->GetFirstChild(); child;
+       child = child->GetNextSibling()) {
+    PaintLayer(child, aCallback, aCallbackData);
+  }
 
-  // For layers that paint themselves (e.g., BasicImageLayer), paint
-  // them now.
-  ToData(aLayer)->Paint(mTarget);
-}
-
-void
-BasicLayerManager::EndPaintingLayer()
-{
-  PRBool needsGroup = NeedsGroup(mLastPainted);
-  if ((needsGroup || NeedsState(mLastPainted)) && mTarget) {
+  if (needsSaveRestore) {
     if (needsGroup) {
       mTarget->PopGroupToSource();
-      mTarget->Paint(mLastPainted->GetOpacity());
+      mTarget->Paint(aLayer->GetOpacity());
     }
 
     mTarget->Restore();
   }
 }
 
-void
-BasicLayerManager::AdvancePaintingTo(BasicThebesLayer* aLayer)
-{
-  NS_ASSERTION(!aLayer || IsBeforeInTree(mLastPainted, aLayer),
-               "Painting layers out of order");
-
-  // Traverse the layer tree from mLastPainted to aLayer, calling
-  // BeginPaintingLayer and EndPaintingLayer as we enter or exit layers.
-  do {
-    Layer* firstChild;
-    Layer* nextSibling;
-    // Advance mLastPainted one step through the tree in preorder
-    if (!mLastPainted) {
-      // This is the first AdvancePaintingTo call. Start at the root.
-      BeginPaintingLayer(mRoot);
-    } else if ((firstChild = mLastPainted->GetFirstChild()) != nsnull) {
-      // Descend into our first child, if there is one.
-      BeginPaintingLayer(firstChild);
-    } else if ((nextSibling = mLastPainted->GetNextSibling()) != nsnull) {
-      // There are no children to descend into. Leave this layer and
-      // advance to our next sibling, if there is one.
-      EndPaintingLayer();
-      BeginPaintingLayer(nextSibling);
-    } else {
-      // There are no children to descend into and we have no next sibling.
-      // Exit layers until we find a layer which has a next sibling
-      // (or we exit the root).
-      do {
-        EndPaintingLayer();
-        mLastPainted = mLastPainted->GetParent();
-      } while (mLastPainted && !mLastPainted->GetNextSibling());
-      if (mLastPainted) {
-        EndPaintingLayer();
-        BeginPaintingLayer(mLastPainted->GetNextSibling());
-      }
-    }
-  } while (mLastPainted != aLayer);
-}
-
 already_AddRefed<ThebesLayer>
 BasicLayerManager::CreateThebesLayer()
 {
   NS_ASSERTION(InConstruction(), "Only allowed in construction phase");
   nsRefPtr<ThebesLayer> layer = new BasicThebesLayer(this);
   return layer.forget();
 }
 
@@ -762,59 +723,10 @@ BasicLayerManager::CreateColorLayer()
 already_AddRefed<CanvasLayer>
 BasicLayerManager::CreateCanvasLayer()
 {
   NS_ASSERTION(InConstruction(), "Only allowed in construction phase");
   nsRefPtr<CanvasLayer> layer = new BasicCanvasLayer(this);
   return layer.forget();
 }
 
-#ifdef DEBUG
-static void
-AppendAncestors(Layer* aLayer, nsTArray<Layer*>* aAncestors)
-{
-  while (aLayer) {
-    aAncestors->AppendElement(aLayer);
-    aLayer = aLayer->GetParent();
-  }
-}
-
-PRBool
-BasicLayerManager::IsBeforeInTree(Layer* aBefore, Layer* aLayer)
-{
-  if (!aBefore) {
-    return PR_TRUE;
-  }
-  nsAutoTArray<Layer*,8> beforeAncestors, afterAncestors;
-  AppendAncestors(aBefore, &beforeAncestors);
-  AppendAncestors(aLayer, &afterAncestors);
-  PRInt32 beforeIndex = beforeAncestors.Length() - 1;
-  PRInt32 afterIndex = afterAncestors.Length() - 1;
-  NS_ASSERTION(beforeAncestors[beforeIndex] == mRoot, "aBefore not in tree?");
-  NS_ASSERTION(afterAncestors[afterIndex] == mRoot, "aLayer not in tree?");
-  --beforeIndex;
-  --afterIndex;
-  while (beforeIndex >= 0 && afterIndex >= 0) {
-    if (beforeAncestors[beforeIndex] != afterAncestors[afterIndex]) {
-      BasicContainerLayer* parent =
-        static_cast<BasicContainerLayer*>(beforeAncestors[beforeIndex + 1]);
-      for (Layer* child = parent->GetFirstChild();
-           child != afterAncestors[afterIndex];
-           child = child->GetNextSibling()) {
-        if (child == beforeAncestors[beforeIndex]) {
-          return PR_TRUE;
-        }
-      }
-      return PR_FALSE;
-    }
-    --beforeIndex;
-    --afterIndex;
-  }
-  if (afterIndex > 0) {
-    // aBefore is an ancestor of aLayer, so it's before aLayer in preorder
-    return PR_TRUE;
-  }
-  return PR_FALSE;
-}
-#endif
-
 }
 }
--- a/gfx/layers/basic/BasicLayers.h
+++ b/gfx/layers/basic/BasicLayers.h
@@ -73,62 +73,46 @@ public:
   /**
    * Set the default target context that will be used when BeginTransaction
    * is called. This can only be called outside a transaction.
    */
   void SetDefaultTarget(gfxContext* aContext);
 
   virtual void BeginTransaction();
   virtual void BeginTransactionWithTarget(gfxContext* aTarget);
-  virtual void EndConstruction();
-  virtual void EndTransaction();
+  virtual void EndTransaction(DrawThebesLayerCallback aCallback,
+                              void* aCallbackData);
 
   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 LayersBackend GetBackendType() { return LAYERS_BASIC; }
 
 #ifdef DEBUG
   PRBool InConstruction() { return mPhase == PHASE_CONSTRUCTION; }
   PRBool InDrawing() { return mPhase == PHASE_DRAWING; }
-  PRBool IsBeforeInTree(Layer* aBefore, Layer* aLayer);
 #endif
-  // Prepares mTarget for painting into aLayer. Layers are painted
-  // in tree order, so this method essentially traverses the layer tree
-  // in preorder from mLastPainted to aLayer, doing whatever's needed
-  // as we exit container layers and enter new container layers, and
-  // drawing any non-ThebesLayers we encounter.
-  void AdvancePaintingTo(BasicThebesLayer* aLayer);
-  Layer* GetLastPainted() { return mLastPainted; }
   gfxContext* GetTarget() { return mTarget; }
 
 private:
-  // This gets called when we start painting aLayer. It can change
-  // the state of mTarget by saving state, setting clipping and/or
-  // pushing a group.
-  void BeginPaintingLayer(Layer* aLayer);
-  // This gets called when we finish painting aLayer. It can change
-  // the state of mTarget by popping a group and/or restoring the state.
-  void EndPaintingLayer();
+  // Paints aLayer to mTarget.
+  void PaintLayer(Layer* aLayer,
+                  DrawThebesLayerCallback aCallback,
+                  void* aCallbackData);
 
   nsRefPtr<Layer> mRoot;
   // The default context for BeginTransaction.
   nsRefPtr<gfxContext> mDefaultTarget;
-  // The context to draw into. This is always the context that
-  // our ThebesLayers will return.
+  // The context to draw into.
   nsRefPtr<gfxContext> mTarget;
-  // The most recently painted layer during the drawing phase of
-  // a transaction, or null if no layer has been painted in this
-  // transaction.
-  Layer* mLastPainted;
 
 #ifdef DEBUG
   enum TransactionPhase { PHASE_NONE, PHASE_CONSTRUCTION, PHASE_DRAWING };
   TransactionPhase mPhase;
 #endif
 };
 
 }
--- a/gfx/layers/opengl/CanvasLayerOGL.cpp
+++ b/gfx/layers/opengl/CanvasLayerOGL.cpp
@@ -193,17 +193,19 @@ CanvasLayerOGL::Updated(const nsIntRect&
     }
   }
 
   // sanity
   NS_ASSERTION(mUpdatedRect.IsEmpty() || mBounds.Contains(mUpdatedRect), "CanvasLayer: Updated rect bigger than bounds!");
 }
 
 void
-CanvasLayerOGL::RenderLayer(int aPreviousDestination)
+CanvasLayerOGL::RenderLayer(int aPreviousDestination,
+                            DrawThebesLayerCallback aCallback,
+                            void* aCallbackData)
 {
   LayerManagerOGL *glManager = static_cast<LayerManagerOGL*>(mManager);
   GLContext *gl = glManager->gl();
   glManager->MakeCurrent();
 
   float quadTransform[4][4];
   // Transform the quad to the size of the canvas.
   memset(&quadTransform, 0, sizeof(quadTransform));
--- a/gfx/layers/opengl/CanvasLayerOGL.h
+++ b/gfx/layers/opengl/CanvasLayerOGL.h
@@ -61,17 +61,19 @@ public:
 
   // CanvasLayer implementation
   virtual void Initialize(const Data& aData);
   virtual void Updated(const nsIntRect& aRect);
 
   // LayerOGL implementation
   virtual LayerType GetType() { return TYPE_CANVAS; }
   virtual Layer* GetLayer() { return this; }
-  virtual void RenderLayer(int aPreviousDestination);
+  virtual void RenderLayer(int aPreviousDestination,
+                           DrawThebesLayerCallback aCallback,
+                           void* aCallbackData);
 
 protected:
   nsRefPtr<gfxASurface> mSurface;
   nsRefPtr<GLContext> mGLContext;
 
   unsigned int mTexture;
 
   nsIntRect mBounds;
--- a/gfx/layers/opengl/ColorLayerOGL.cpp
+++ b/gfx/layers/opengl/ColorLayerOGL.cpp
@@ -48,17 +48,17 @@ ColorLayerOGL::GetType()
 
 Layer*
 ColorLayerOGL::GetLayer()
 {
   return this;
 }
 
 void
-ColorLayerOGL::RenderLayer(int)
+ColorLayerOGL::RenderLayer(int, DrawThebesLayerCallback, void*)
 {
   static_cast<LayerManagerOGL*>(mManager)->MakeCurrent();
 
   // XXX we might be able to improve performance by using glClear
 
   float quadTransform[4][4];
   nsIntRect visibleRect = mVisibleRegion.GetBounds();
   // Transform the quad to the size of the visible area.
--- a/gfx/layers/opengl/ColorLayerOGL.h
+++ b/gfx/layers/opengl/ColorLayerOGL.h
@@ -56,17 +56,19 @@ public:
 
   virtual void SetVisibleRegion(const nsIntRegion& aRegion) { mVisibleRegion = aRegion; }
 
   // LayerOGL Implementation
   virtual LayerType GetType();
 
   virtual Layer* GetLayer();
 
-  virtual void RenderLayer(int aPreviousDestination);
+  virtual void RenderLayer(int aPreviousDestination,
+                           DrawThebesLayerCallback aCallback,
+                           void* aCallbackData);
 
 protected:
   nsIntRegion mVisibleRegion;
 };
 
 } /* layers */
 } /* mozilla */
 #endif /* GFX_COLORLAYEROGL_H */
--- a/gfx/layers/opengl/ContainerLayerOGL.cpp
+++ b/gfx/layers/opengl/ContainerLayerOGL.cpp
@@ -42,16 +42,25 @@ namespace layers {
 
 ContainerLayerOGL::ContainerLayerOGL(LayerManagerOGL *aManager)
   : ContainerLayer(aManager, NULL)
   , LayerOGL(aManager)
 {
   mImplData = static_cast<LayerOGL*>(this);
 }
 
+ContainerLayerOGL::~ContainerLayerOGL()
+{
+  LayerOGL *nextChild;
+  for (LayerOGL *child = GetFirstChildOGL(); child; child = nextChild) {
+    nextChild = child->GetNextSibling();
+    child->GetLayer()->Release();
+  }
+}
+
 const nsIntRect&
 ContainerLayerOGL::GetVisibleRect()
 {
   return mVisibleRect;
 }
 
 void
 ContainerLayerOGL::SetVisibleRegion(const nsIntRegion &aRegion)
@@ -59,16 +68,17 @@ ContainerLayerOGL::SetVisibleRegion(cons
   mVisibleRect = aRegion.GetBounds();
 }
 
 void
 ContainerLayerOGL::InsertAfter(Layer* aChild, Layer* aAfter)
 {
   LayerOGL *newChild = static_cast<LayerOGL*>(aChild->ImplData());
   aChild->SetParent(this);
+  NS_ADDREF(aChild);
   if (!aAfter) {
     LayerOGL *oldFirstChild = GetFirstChildOGL();
     mFirstChild = newChild->GetLayer();
     newChild->SetNextSibling(oldFirstChild);
     return;
   }
   for (LayerOGL *child = GetFirstChildOGL(); 
     child; child = child->GetNextSibling()) {
@@ -82,26 +92,28 @@ ContainerLayerOGL::InsertAfter(Layer* aC
   NS_WARNING("Failed to find aAfter layer!");
 }
 
 void
 ContainerLayerOGL::RemoveChild(Layer *aChild)
 {
   if (GetFirstChild() == aChild) {
     mFirstChild = GetFirstChildOGL()->GetNextSibling()->GetLayer();
+    NS_RELEASE(aChild);
     return;
   }
   LayerOGL *lastChild = NULL;
   for (LayerOGL *child = GetFirstChildOGL(); child; 
     child = child->GetNextSibling()) {
     if (child->GetLayer() == aChild) {
       // We're sure this is not our first child. So lastChild != NULL.
       lastChild->SetNextSibling(child->GetNextSibling());
       child->SetNextSibling(NULL);
       child->GetLayer()->SetParent(NULL);
+      NS_RELEASE(aChild);
       return;
     }
     lastChild = child;
   }
 }
 
 LayerOGL::LayerType
 ContainerLayerOGL::GetType()
@@ -120,17 +132,19 @@ ContainerLayerOGL::GetFirstChildOGL()
 {
   if (!mFirstChild) {
     return nsnull;
   }
   return static_cast<LayerOGL*>(mFirstChild->ImplData());
 }
 
 void
-ContainerLayerOGL::RenderLayer(int aPreviousFrameBuffer)
+ContainerLayerOGL::RenderLayer(int aPreviousFrameBuffer,
+                               DrawThebesLayerCallback aCallback,
+                               void* aCallbackData)
 {
   /**
    * Setup our temporary texture for rendering the contents of this container.
    */
   GLuint containerSurface;
   GLuint frameBuffer;
   RGBLayerProgram *rgbProgram =
     static_cast<LayerManagerOGL*>(mManager)->GetRGBLayerProgram();
@@ -197,17 +211,17 @@ ContainerLayerOGL::RenderLayer(int aPrev
       gl()->fScissor(clipRect->x - GetVisibleRect().x,
                    clipRect->y - GetVisibleRect().y,
                    clipRect->width,
                    clipRect->height);
     } else {
       gl()->fScissor(0, 0, GetVisibleRect().width, GetVisibleRect().height);
     }
 
-    layerToRender->RenderLayer(frameBuffer);
+    layerToRender->RenderLayer(frameBuffer, aCallback, aCallbackData);
     layerToRender = layerToRender->GetNextSibling();
   }
 
   if (GetOpacity() != 1.0) {
     // Unbind the current framebuffer and rebind the previous one.
     gl()->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, aPreviousFrameBuffer);
     gl()->fDeleteFramebuffers(1, &frameBuffer);
 
--- a/gfx/layers/opengl/ContainerLayerOGL.h
+++ b/gfx/layers/opengl/ContainerLayerOGL.h
@@ -44,16 +44,17 @@
 namespace mozilla {
 namespace layers {
 
 class ContainerLayerOGL : public ContainerLayer, 
                           public LayerOGL
 {
 public:
   ContainerLayerOGL(LayerManagerOGL *aManager);
+  ~ContainerLayerOGL();
 
   const nsIntRect &GetVisibleRect();
 
   /** ContainerLayer implementation */
   void SetVisibleRegion(const nsIntRegion& aRegion);
 
   void InsertAfter(Layer* aChild, Layer* aAfter);
 
@@ -63,17 +64,19 @@ public:
   LayerType GetType();
 
   Layer* GetLayer();
 
   LayerOGL* GetFirstChildOGL();
 
   PRBool IsEmpty();
 
-  void RenderLayer(int aPreviousFrameBuffer);
+  void RenderLayer(int aPreviousFrameBuffer,
+                   DrawThebesLayerCallback aCallback,
+                   void* aCallbackData);
 private:
   nsIntRect mVisibleRect;
 
   GLuint mTexture;
 };
 
 } /* layers */
 } /* mozilla */
--- a/gfx/layers/opengl/ImageLayerOGL.cpp
+++ b/gfx/layers/opengl/ImageLayerOGL.cpp
@@ -264,17 +264,18 @@ ImageLayerOGL::GetType()
 
 Layer*
 ImageLayerOGL::GetLayer()
 {
   return this;
 }
 
 void
-ImageLayerOGL::RenderLayer(int)
+ImageLayerOGL::RenderLayer(int, DrawThebesLayerCallback aCallback,
+                           void* aCallbackData)
 {
   if (!GetContainer()) {
     return;
   }
 
   LayerManagerOGL *manager = static_cast<LayerManagerOGL*>(mManager);
   manager->MakeCurrent();
 
--- a/gfx/layers/opengl/ImageLayerOGL.h
+++ b/gfx/layers/opengl/ImageLayerOGL.h
@@ -170,17 +170,19 @@ public:
     mImplData = static_cast<LayerOGL*>(this);
   }
 
   // LayerOGL Implementation
   virtual LayerType GetType();
 
   virtual Layer* GetLayer();
 
-  virtual void RenderLayer(int aPreviousDestination);
+  virtual void RenderLayer(int aPreviousDestination,
+                           DrawThebesLayerCallback aCallback,
+                           void* aCallbackData);
 };
 
 class THEBES_API PlanarYCbCrImageOGL : public PlanarYCbCrImage
 {
   typedef mozilla::gl::GLContext GLContext;
 
 public:
   PlanarYCbCrImageOGL(RecycleBin *aRecycleBin);
--- a/gfx/layers/opengl/LayerManagerOGL.cpp
+++ b/gfx/layers/opengl/LayerManagerOGL.cpp
@@ -277,24 +277,20 @@ LayerManagerOGL::BeginTransaction()
 
 void
 LayerManagerOGL::BeginTransactionWithTarget(gfxContext *aTarget)
 {
   mTarget = aTarget;
 }
 
 void
-LayerManagerOGL::EndConstruction()
+LayerManagerOGL::EndTransaction(DrawThebesLayerCallback aCallback,
+                                void* aCallbackData)
 {
-}
-
-void
-LayerManagerOGL::EndTransaction()
-{
-  Render();
+  Render(aCallback, aCallbackData);
   mTarget = NULL;
 }
 
 void
 LayerManagerOGL::SetRoot(Layer *aLayer)
 {
   mRootLayer =  static_cast<LayerOGL*>(aLayer->ImplData());;
 }
@@ -353,17 +349,18 @@ LayerManagerOGL::SetClippingEnabled(PRBo
 
 void
 LayerManagerOGL::MakeCurrent()
 {
     mGLContext->MakeCurrent();
 }
 
 void
-LayerManagerOGL::Render()
+LayerManagerOGL::Render(DrawThebesLayerCallback aCallback,
+                        void* aCallbackData)
 {
   nsIntRect rect;
   mWidget->GetBounds(rect);
   GLint width = rect.width;
   GLint height = rect.height;
 
   MakeCurrent();
   SetupBackBuffer();
@@ -379,17 +376,17 @@ LayerManagerOGL::Render()
   if (mRootLayer) {
     const nsIntRect *clipRect = mRootLayer->GetLayer()->GetClipRect();
     if (clipRect) {
       mGLContext->fScissor(clipRect->x, clipRect->y, clipRect->width, clipRect->height);
     } else {
       mGLContext->fScissor(0, 0, width, height);
     }
 
-    mRootLayer->RenderLayer(mFrameBuffer);
+    mRootLayer->RenderLayer(mFrameBuffer, aCallback, aCallbackData);
   }
 
   if (mTarget) {
     CopyToTarget();
   } else {
     /**
      * Draw our backbuffer to the screen without using vertex or fragment
      * shaders. We're fine with just calculating the viewport coordinates
--- a/gfx/layers/opengl/LayerManagerOGL.h
+++ b/gfx/layers/opengl/LayerManagerOGL.h
@@ -211,17 +211,18 @@ public:
    * LayerManager implementation.
    */
   void BeginTransaction();
 
   void BeginTransactionWithTarget(gfxContext* aTarget);
 
   void EndConstruction();
 
-  void EndTransaction();
+  virtual void EndTransaction(DrawThebesLayerCallback aCallback,
+                              void* aCallbackData);
 
   void SetRoot(Layer* aLayer);
   
   virtual already_AddRefed<ThebesLayer> CreateThebesLayer();
 
   virtual already_AddRefed<ContainerLayer> CreateContainerLayer();
 
   virtual already_AddRefed<ImageLayer> CreateImageLayer();
@@ -288,17 +289,18 @@ private:
 
   /**
    * Region we're clipping our current drawing to.
    */
   nsIntRegion mClippingRegion;
   /**
    * Render the current layer tree to the active target.
    */
-  void Render();
+  void Render(DrawThebesLayerCallback aCallback,
+              void* aCallbackData);
   /**
    * Setup the pipeline.
    */
   void SetupPipeline();
   /**
    * Setup the backbuffer.
    *
    * \return PR_TRUE if setup was succesful
@@ -311,16 +313,18 @@ private:
 };
 
 /**
  * General information and tree management for OGL layers.
  */
 class LayerOGL
 {
 public:
+  typedef LayerManager::DrawThebesLayerCallback DrawThebesLayerCallback;
+
   LayerOGL(LayerManagerOGL *aManager);
 
   enum LayerType {
     TYPE_THEBES,
     TYPE_CONTAINER,
     TYPE_IMAGE,
     TYPE_COLOR,
     TYPE_CANVAS
@@ -331,17 +335,18 @@ public:
   LayerOGL *GetNextSibling();
   virtual LayerOGL *GetFirstChildOGL() { return nsnull; }
 
   void SetNextSibling(LayerOGL *aParent);
   void SetFirstChild(LayerOGL *aParent);
 
   virtual Layer* GetLayer() = 0;
 
-  virtual void RenderLayer(int aPreviousFrameBuffer) = 0;
+  virtual void RenderLayer(int aPreviousFrameBuffer, DrawThebesLayerCallback aCallback,
+                           void* aCallbackData) = 0;
 
   typedef mozilla::gl::GLContext GLContext;
 
   GLContext *gl() const { return mOGLManager->gl(); }
 protected:
   LayerManagerOGL *mOGLManager;
   LayerOGL *mNextSibling;
 };
--- a/gfx/layers/opengl/ThebesLayerOGL.cpp
+++ b/gfx/layers/opengl/ThebesLayerOGL.cpp
@@ -123,125 +123,100 @@ void
 ThebesLayerOGL::InvalidateRegion(const nsIntRegion &aRegion)
 {
   nsIntRegion invalidatedRegion;
   invalidatedRegion.Or(aRegion, mInvalidatedRect);
   invalidatedRegion.And(invalidatedRegion, mVisibleRect);
   mInvalidatedRect = invalidatedRegion.GetBounds();
 }
 
-gfxContext *
-ThebesLayerOGL::BeginDrawing(nsIntRegion *aRegion)
-{
-  if (mInvalidatedRect.IsEmpty()) {
-    aRegion->SetEmpty();
-    return NULL;
-  }
-  if (!mTexture) {
-    aRegion->SetEmpty();
-    return NULL;
-  }
-  *aRegion = mInvalidatedRect;
-
-  gfxASurface::gfxImageFormat imageFormat;
-
-  if (UseOpaqueSurface(this)) {
-    imageFormat = gfxASurface::ImageFormatRGB24;
-  } else {
-    imageFormat = gfxASurface::ImageFormatARGB32;
-  }
-
-  mDestinationSurface =
-    gfxPlatform::GetPlatform()->
-      CreateOffscreenSurface(gfxIntSize(mInvalidatedRect.width,
-                                        mInvalidatedRect.height),
-                             imageFormat);
-
-  mContext = new gfxContext(mDestinationSurface);
-  mContext->Translate(gfxPoint(-mInvalidatedRect.x, -mInvalidatedRect.y));
-  return mContext.get();
-}
-
-void
-ThebesLayerOGL::EndDrawing()
-{
-  static_cast<LayerManagerOGL*>(mManager)->MakeCurrent();
-
-  nsRefPtr<gfxImageSurface> imageSurface;
-
-  gfxASurface::gfxImageFormat imageFormat;
-
-  if (UseOpaqueSurface(this)) {
-    imageFormat = gfxASurface::ImageFormatRGB24;
-  } else {
-    imageFormat = gfxASurface::ImageFormatARGB32;
-  }
-
-  switch (mDestinationSurface->GetType()) {
-    case gfxASurface::SurfaceTypeImage:
-      imageSurface = static_cast<gfxImageSurface*>(mDestinationSurface.get());
-      break;
-#ifdef XP_WIN
-    case gfxASurface::SurfaceTypeWin32:
-      imageSurface =
-        static_cast<gfxWindowsSurface*>(mDestinationSurface.get())->
-          GetImageSurface();
-      break;
-#endif
-    default:
-      /** 
-       * XXX - This is very undesirable. Implement this for other platforms in
-       * a more efficient way as well!
-       */
-      {
-        imageSurface = new gfxImageSurface(gfxIntSize(mInvalidatedRect.width,
-                                                      mInvalidatedRect.height),
-                                           imageFormat);
-        nsRefPtr<gfxContext> tmpContext = new gfxContext(imageSurface);
-        tmpContext->SetSource(mDestinationSurface);
-        tmpContext->SetOperator(gfxContext::OPERATOR_SOURCE);
-        tmpContext->Paint();
-      }
-      break;
-  }
-
-  gl()->fBindTexture(LOCAL_GL_TEXTURE_2D, mTexture);
-  gl()->fTexSubImage2D(LOCAL_GL_TEXTURE_2D,
-                       0,
-                       mInvalidatedRect.x - mVisibleRect.x,
-                       mInvalidatedRect.y - mVisibleRect.y,
-                       mInvalidatedRect.width,
-                       mInvalidatedRect.height,
-                       LOCAL_GL_BGRA,
-                       LOCAL_GL_UNSIGNED_BYTE,
-                       imageSurface->Data());
-
-  mDestinationSurface = NULL;
-  mContext = NULL;
-}
-
 LayerOGL::LayerType
 ThebesLayerOGL::GetType()
 {
   return TYPE_THEBES;
 }
 
 const nsIntRect&
 ThebesLayerOGL::GetVisibleRect()
 {
   return mVisibleRect;
 }
 
 void
-ThebesLayerOGL::RenderLayer(int aPreviousFrameBuffer)
+ThebesLayerOGL::RenderLayer(int aPreviousFrameBuffer,
+                            DrawThebesLayerCallback aCallback,
+                            void* aCallbackData)
 {
   if (!mTexture) {
     return;
   }
 
+  if (!mInvalidatedRect.IsEmpty()) {
+    gfxASurface::gfxImageFormat imageFormat;
+
+    if (UseOpaqueSurface(this)) {
+      imageFormat = gfxASurface::ImageFormatRGB24;
+    } else {
+      imageFormat = gfxASurface::ImageFormatARGB32;
+    }
+
+    nsRefPtr<gfxASurface> surface =
+      gfxPlatform::GetPlatform()->
+        CreateOffscreenSurface(gfxIntSize(mInvalidatedRect.width,
+                                          mInvalidatedRect.height),
+                               imageFormat);
+
+    nsRefPtr<gfxContext> ctx = new gfxContext(surface);
+    ctx->Translate(gfxPoint(-mInvalidatedRect.x, -mInvalidatedRect.y));
+    aCallback(this, ctx, mInvalidatedRect, aCallbackData);
+
+    static_cast<LayerManagerOGL*>(mManager)->MakeCurrent();
+
+    nsRefPtr<gfxImageSurface> imageSurface;
+
+    switch (surface->GetType()) {
+      case gfxASurface::SurfaceTypeImage:
+        imageSurface = static_cast<gfxImageSurface*>(surface.get());
+        break;
+#ifdef XP_WIN
+      case gfxASurface::SurfaceTypeWin32:
+        imageSurface =
+          static_cast<gfxWindowsSurface*>(surface.get())->
+            GetImageSurface();
+        break;
+#endif
+      default:
+        /** 
+         * XXX - This is very undesirable. Implement this for other platforms in
+         * a more efficient way as well!
+         */
+        {
+          imageSurface = new gfxImageSurface(gfxIntSize(mInvalidatedRect.width,
+                                                        mInvalidatedRect.height),
+                                             imageFormat);
+          nsRefPtr<gfxContext> tmpContext = new gfxContext(imageSurface);
+          tmpContext->SetSource(surface);
+          tmpContext->SetOperator(gfxContext::OPERATOR_SOURCE);
+          tmpContext->Paint();
+        }
+        break;
+    }
+
+    gl()->fBindTexture(LOCAL_GL_TEXTURE_2D, mTexture);
+    gl()->fTexSubImage2D(LOCAL_GL_TEXTURE_2D,
+                         0,
+                         mInvalidatedRect.x - mVisibleRect.x,
+                         mInvalidatedRect.y - mVisibleRect.y,
+                         mInvalidatedRect.width,
+                         mInvalidatedRect.height,
+                         LOCAL_GL_BGRA,
+                         LOCAL_GL_UNSIGNED_BYTE,
+                         imageSurface->Data());
+  }
+
   float quadTransform[4][4];
   /*
    * Matrix to transform the <0.0,0.0>, <1.0,1.0> quad to the correct position
    * and size.
    */
   memset(&quadTransform, 0, sizeof(quadTransform));
   quadTransform[0][0] = (float)GetVisibleRect().width;
   quadTransform[1][1] = (float)GetVisibleRect().height;
--- a/gfx/layers/opengl/ThebesLayerOGL.h
+++ b/gfx/layers/opengl/ThebesLayerOGL.h
@@ -54,50 +54,38 @@ public:
   virtual ~ThebesLayerOGL();
 
   /** Layer implementation */
   void SetVisibleRegion(const nsIntRegion& aRegion);
 
   /** ThebesLayer implementation */
   void InvalidateRegion(const nsIntRegion& aRegion);
 
-  gfxContext *BeginDrawing(nsIntRegion* aRegionToDraw);
-
-  void EndDrawing();
-
   /** LayerOGL implementation */
   LayerType GetType();
   Layer* GetLayer();
   virtual PRBool IsEmpty();
-  virtual void RenderLayer(int aPreviousFrameBuffer);
+  virtual void RenderLayer(int aPreviousFrameBuffer,
+                           DrawThebesLayerCallback aCallback,
+                           void* aCallbackData);
 
   /** ThebesLayerOGL */
   const nsIntRect &GetVisibleRect();
   const nsIntRect &GetInvalidatedRect();
 
 private:
   /** 
    * Visible rectangle, this is used to know the size and position of the quad
    * when doing the rendering of this layer.
    */
   nsIntRect mVisibleRect;
   /**
    * Currently invalidated rectangular area.
    */
   nsIntRect mInvalidatedRect;
-  /**
-   * Destination surface used for this layer's drawing operation. This is 
-   * created on BeginDrawing() and should be removed on EndDrawing().
-   */
-  nsRefPtr<gfxASurface> mDestinationSurface;
-
-  /**
-   * We hold the reference to the context.
-   */
-  nsRefPtr<gfxContext> mContext;
 
   /**
    * OpenGL Texture
    */
   GLuint mTexture;
 
 };
 
new file mode 100644
--- /dev/null
+++ b/layout/base/FrameLayerBuilder.cpp
@@ -0,0 +1,539 @@
+/* -*- 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) 2010
+ * 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 "FrameLayerBuilder.h"
+
+#include "nsDisplayList.h"
+#include "nsPresContext.h"
+#include "nsLayoutUtils.h"
+
+using namespace mozilla::layers;
+
+namespace mozilla {
+
+namespace {
+
+/**
+ * This class iterates through a display list tree, descending only into
+ * nsDisplayClip items, and returns each display item encountered during
+ * such iteration. Along with each item we also return the clip rect
+ * accumulated for the item.
+ */
+class ClippedItemIterator {
+public:
+  ClippedItemIterator(const nsDisplayList* aList)
+  {
+    DescendIntoList(aList, nsnull, nsnull);
+    AdvanceToItem();
+  }
+  PRBool IsDone()
+  {
+    return mStack.IsEmpty();
+  }
+  void Next()
+  {
+    State* top = StackTop();
+    top->mItem = top->mItem->GetAbove();
+    AdvanceToItem();
+  }
+  // Returns null if there is no clipping affecting the item. The
+  // clip rect is in device pixels
+  const gfxRect* GetEffectiveClipRect()
+  {
+    State* top = StackTop();
+    return top->mHasClipRect ? &top->mEffectiveClipRect : nsnull;
+  }
+  nsDisplayItem* Item()
+  {
+    return StackTop()->mItem;
+  }
+
+private:
+  // We maintain a stack of state objects. Each State object represents
+  // where we're up to in the iteration of a list.
+  struct State {
+    // The current item we're at in the list
+    nsDisplayItem* mItem;
+    // The effective clip rect applying to all the items in this list
+    gfxRect mEffectiveClipRect;
+    PRPackedBool mHasClipRect;
+  };
+
+  State* StackTop()
+  {
+    return &mStack[mStack.Length() - 1];
+  }
+  void DescendIntoList(const nsDisplayList* aList,
+                       nsPresContext* aPresContext,
+                       const nsRect* aClipRect)
+  {
+    State* state = mStack.AppendElement();
+    if (!state)
+      return;
+    if (mStack.Length() >= 2) {
+      *state = mStack[mStack.Length() - 2];
+    } else {
+      state->mHasClipRect = PR_FALSE;
+    }
+    state->mItem = aList->GetBottom();
+    if (aClipRect) {
+      gfxRect r(aClipRect->x, aClipRect->y, aClipRect->width, aClipRect->height);
+      r.ScaleInverse(aPresContext->AppUnitsPerDevPixel());
+      if (state->mHasClipRect) {
+        state->mEffectiveClipRect = state->mEffectiveClipRect.Intersect(r);
+      } else {
+        state->mEffectiveClipRect = r;
+        state->mHasClipRect = PR_TRUE;
+      }
+    }
+  }
+  // Advances to an item that the iterator should return.
+  void AdvanceToItem()
+  {
+    while (!mStack.IsEmpty()) {
+      State* top = StackTop();
+      if (!top->mItem) {
+        mStack.SetLength(mStack.Length() - 1);
+        if (!mStack.IsEmpty()) {
+          top = StackTop();
+          top->mItem = top->mItem->GetAbove();
+        }
+        continue;
+      }
+      if (top->mItem->GetType() != nsDisplayItem::TYPE_CLIP)
+        return;
+      nsDisplayClip* clipItem = static_cast<nsDisplayClip*>(top->mItem);
+      nsRect clip = clipItem->GetClipRect();
+      DescendIntoList(clipItem->GetList(),
+                      clipItem->GetClippingFrame()->PresContext(),
+                      &clip);
+    }
+  }
+
+  nsAutoTArray<State,10> mStack;
+};
+
+/**
+ * This class represents a sublist of consecutive items in an nsDisplayList.
+ * The first item in the sublist is mStartItem and the last item
+ * is the item before mEndItem.
+ * 
+ * These sublists are themselves organized into a linked list of all
+ * the ItemGroups associated with a given layer, via mNextItemsForLayer.
+ * This list will have more than one element if the display items in a layer
+ * come from different nsDisplayLists, or if they come from the same
+ * nsDisplayList but they aren't consecutive in that list.
+ * 
+ * These objects are allocated from the nsDisplayListBuilder arena.
+ */
+struct ItemGroup {
+  // If null, then the item group is empty.
+  nsDisplayItem* mStartItem;
+  nsDisplayItem* mEndItem;
+  ItemGroup* mNextItemsForLayer;
+  // The clipping (if any) that needs to be applied to all these items.
+  gfxRect mClipRect;
+  PRPackedBool mHasClipRect;
+
+  ItemGroup() : mStartItem(nsnull), mEndItem(nsnull),
+    mNextItemsForLayer(nsnull), mHasClipRect(PR_FALSE) {}
+
+  void* operator new(size_t aSize,
+                     nsDisplayListBuilder* aBuilder) CPP_THROW_NEW {
+    return aBuilder->Allocate(aSize);
+  }
+};
+
+/**
+ * This class represents a layer and the display item(s) it
+ * will render. The items are stored in a linked list of ItemGroups.
+ */
+struct LayerItems {
+  nsRefPtr<Layer> mLayer;
+  // equal to mLayer, or null if mLayer is not a ThebesLayer
+  ThebesLayer* mThebesLayer;
+  ItemGroup* mItems;
+  // The bounds of the visible region for this layer, in device pixels
+  nsIntRect mVisibleRect;
+
+  LayerItems(ItemGroup* aItems) :
+    mThebesLayer(nsnull), mItems(aItems)
+  {
+  }
+
+  void* operator new(size_t aSize,
+                     nsDisplayListBuilder* aBuilder) CPP_THROW_NEW {
+    return aBuilder->Allocate(aSize);
+  }
+};
+
+/**
+ * Given a (possibly clipped) display item in aItem, try to append it to
+ * the items in aGroup. If aItem is the next item in the sublist in
+ * aGroup, and the clipping matches, we can just update aGroup in-place,
+ * otherwise we'll allocate a new ItemGroup, add it to the linked list,
+ * and put aItem in the new ItemGroup. We return the ItemGroup into which
+ * aItem was inserted.
+ */
+static ItemGroup*
+AddToItemGroup(nsDisplayListBuilder* aBuilder,
+               ItemGroup* aGroup, nsDisplayItem* aItem,
+               const gfxRect* aClipRect)
+{
+  NS_ASSERTION(!aGroup->mNextItemsForLayer,
+               "aGroup must be the last group in the chain");
+
+  if (!aGroup->mStartItem) {
+    aGroup->mStartItem = aItem;
+    aGroup->mEndItem = aItem->GetAbove();
+    aGroup->mHasClipRect = aClipRect != nsnull;
+    if (aClipRect) {
+      aGroup->mClipRect = *aClipRect;
+    }
+    return aGroup;
+  }
+
+  if (aGroup->mEndItem == aItem &&
+      (aGroup->mHasClipRect
+       ? (aClipRect && aGroup->mClipRect == *aClipRect)
+       : !aClipRect))  {
+    aGroup->mEndItem = aItem->GetAbove();
+    return aGroup;
+  }
+
+  ItemGroup* itemGroup = new (aBuilder) ItemGroup();
+  if (!itemGroup)
+    return aGroup;
+  aGroup->mNextItemsForLayer = itemGroup;
+  return AddToItemGroup(aBuilder, itemGroup, aItem, aClipRect);
+}
+
+/**
+ * Create an empty Thebes layer, with an empty ItemGroup associated with
+ * it, and append it to aLayers.
+ */
+static ItemGroup*
+CreateEmptyThebesLayer(nsDisplayListBuilder* aBuilder,
+                       LayerManager* aManager,
+                       nsTArray<LayerItems*>* aLayers)
+{
+  ItemGroup* itemGroup = new (aBuilder) ItemGroup();
+  if (!itemGroup)
+    return nsnull;
+  nsRefPtr<ThebesLayer> thebesLayer = aManager->CreateThebesLayer();
+  if (!thebesLayer)
+    return nsnull;
+  LayerItems* layerItems = new (aBuilder) LayerItems(itemGroup);
+  aLayers->AppendElement(layerItems);
+  thebesLayer->SetUserData(layerItems);
+  layerItems->mThebesLayer = thebesLayer;
+  layerItems->mLayer = thebesLayer.forget();
+  return itemGroup;
+}
+
+static PRBool
+IsAllUniform(nsDisplayListBuilder* aBuilder, ItemGroup* aGroup,
+             nscolor* aColor)
+{
+  nsRect visibleRect = aGroup->mStartItem->GetVisibleRect();
+  nscolor finalColor = NS_RGBA(0,0,0,0);
+  for (ItemGroup* group = aGroup; group;
+       group = group->mNextItemsForLayer) {
+    for (nsDisplayItem* item = group->mStartItem; item != group->mEndItem;
+         item = item->GetAbove()) {
+      nscolor color;
+      if (visibleRect != item->GetVisibleRect())
+        return PR_FALSE;
+      if (!item->IsUniform(aBuilder, &color))
+        return PR_FALSE;
+      finalColor = NS_ComposeColors(finalColor, color);
+    }
+  }
+  *aColor = finalColor;
+  return PR_TRUE;
+}
+
+/**
+ * This is the heart of layout's integration with layers. We
+ * use a ClippedItemIterator to iterate through descendant display
+ * items. Each item either has its own layer or is assigned to a
+ * ThebesLayer. We create ThebesLayers as necessary, although we try
+ * to put items in the bottom-most ThebesLayer because that is most
+ * likely to be able to render with an opaque background, which will often
+ * be required for subpixel text antialiasing to work.
+ */
+static void BuildLayers(nsDisplayListBuilder* aBuilder,
+                        const nsDisplayList& aList,
+                        LayerManager* aManager,
+                        nsTArray<LayerItems*>* aLayers)
+{
+  NS_ASSERTION(aLayers->IsEmpty(), "aLayers must be initially empty");
+
+  // Create "bottom" Thebes layer. We'll try to put as much content
+  // as possible in this layer because if the container is filled with
+  // opaque content, this bottommost layer can also be treated as opaque,
+  // which means content in this layer can have subpixel AA.
+  // firstThebesLayerItems always points to the last ItemGroup for the
+  // first Thebes layer.
+  ItemGroup* firstThebesLayerItems =
+    CreateEmptyThebesLayer(aBuilder, aManager, aLayers);
+  if (!firstThebesLayerItems)
+    return;
+  // lastThebesLayerItems always points to the last ItemGroup for the
+  // topmost layer, if it's a ThebesLayer. If the top layer is not a
+  // Thebes layer, this is null.
+  ItemGroup* lastThebesLayerItems = firstThebesLayerItems;
+  // This region contains the bounds of all the content that is above
+  // the first Thebes layer.
+  nsRegion areaAboveFirstThebesLayer;
+
+  for (ClippedItemIterator iter(&aList); !iter.IsDone(); iter.Next()) {
+    nsDisplayItem* item = iter.Item();
+    const gfxRect* clipRect = iter.GetEffectiveClipRect();
+    // Ask the item if it manages its own layer
+    nsRefPtr<Layer> layer = item->BuildLayer(aBuilder, aManager);
+    nsRect bounds = item->GetBounds(aBuilder);
+    // We set layerItems to point to the LayerItems object where the
+    // item ends up.
+    LayerItems* layerItems = nsnull;
+    if (layer) {
+      // item has a dedicated layer. Add it to the list, with an ItemGroup
+      // covering this item only.
+      ItemGroup* itemGroup = new (aBuilder) ItemGroup();
+      if (itemGroup) {
+        AddToItemGroup(aBuilder, itemGroup, item, clipRect);
+        layerItems = new (aBuilder) LayerItems(itemGroup);
+        aLayers->AppendElement(layerItems);
+        if (layerItems) {
+          if (itemGroup->mHasClipRect) {
+            gfxRect r = itemGroup->mClipRect;
+            r.Round();
+            nsIntRect intRect(r.X(), r.Y(), r.Width(), r.Height());
+            layer->IntersectClipRect(intRect);
+          }
+          layerItems->mLayer = layer.forget();
+        }
+      }
+      // This item is above the first Thebes layer.
+      areaAboveFirstThebesLayer.Or(areaAboveFirstThebesLayer, bounds);
+      lastThebesLayerItems = nsnull;
+    } else {
+      // No dedicated layer. Add it to a Thebes layer. First try to add
+      // it to the first Thebes layer, which we can do if there's no
+      // content between the first Thebes layer and our display item that
+      // overlaps our display item.
+      if (!areaAboveFirstThebesLayer.Intersects(bounds)) {
+        firstThebesLayerItems =
+          AddToItemGroup(aBuilder, firstThebesLayerItems, item, clipRect);
+        layerItems = aLayers->ElementAt(0);
+      } else if (lastThebesLayerItems) {
+        // Try to add to the last Thebes layer
+        lastThebesLayerItems =
+          AddToItemGroup(aBuilder, lastThebesLayerItems, item, clipRect);
+        // This item is above the first Thebes layer.
+        areaAboveFirstThebesLayer.Or(areaAboveFirstThebesLayer, bounds);
+        layerItems = aLayers->ElementAt(aLayers->Length() - 1);
+      } else {
+        // Create a new Thebes layer
+        ItemGroup* itemGroup =
+          CreateEmptyThebesLayer(aBuilder, aManager, aLayers);
+        if (itemGroup) {
+          lastThebesLayerItems =
+            AddToItemGroup(aBuilder, itemGroup, item, clipRect);
+          NS_ASSERTION(lastThebesLayerItems == itemGroup,
+                       "AddToItemGroup shouldn't create a new group if the "
+                       "initial group is empty");
+          // This item is above the first Thebes layer.
+          areaAboveFirstThebesLayer.Or(areaAboveFirstThebesLayer, bounds);
+        }
+      }
+    }
+
+    if (layerItems) {
+      // Update the visible region of the layer to account for the new
+      // item
+      nscoord appUnitsPerDevPixel =
+        item->GetUnderlyingFrame()->PresContext()->AppUnitsPerDevPixel();
+      layerItems->mVisibleRect.UnionRect(layerItems->mVisibleRect,
+        item->GetVisibleRect().ToNearestPixels(appUnitsPerDevPixel));
+    }
+  }
+
+  if (!firstThebesLayerItems->mStartItem) {
+    // The first Thebes layer has nothing in it. Delete the layer.
+    // Ensure layer is released.
+    aLayers->ElementAt(0)->mLayer = nsnull;
+    aLayers->RemoveElementAt(0);
+  }
+
+  for (PRUint32 i = 0; i < aLayers->Length(); ++i) {
+    LayerItems* layerItems = aLayers->ElementAt(i);
+
+    nscolor color;
+    // Only convert layers with identity transform to ColorLayers, for now.
+    // This simplifies the code to set the clip region.
+    if (layerItems->mThebesLayer &&
+        IsAllUniform(aBuilder, layerItems->mItems, &color) &&
+        layerItems->mLayer->GetTransform().IsIdentity()) {
+      nsRefPtr<ColorLayer> layer = aManager->CreateColorLayer();
+      layer->SetClipRect(layerItems->mThebesLayer->GetClipRect());
+      // Clip to mVisibleRect to ensure only the pixels we want are filled.
+      layer->IntersectClipRect(layerItems->mVisibleRect);
+      layer->SetColor(gfxRGBA(color));
+      layerItems->mLayer = layer.forget();
+      layerItems->mThebesLayer = nsnull;
+    }
+
+    gfxMatrix transform;
+    nsIntRect visibleRect = layerItems->mVisibleRect;
+    if (layerItems->mLayer->GetTransform().Is2D(&transform)) {
+      // if 'transform' is not invertible, then nothing will be displayed
+      // for the layer, so it doesn't really matter what we do here
+      transform.Invert();
+      gfxRect layerVisible = transform.TransformBounds(
+          gfxRect(visibleRect.x, visibleRect.y, visibleRect.width, visibleRect.height));
+      layerVisible.RoundOut();
+      if (NS_FAILED(nsLayoutUtils::GfxRectToIntRect(layerVisible, &visibleRect))) {
+        NS_ERROR("Visible rect transformed out of bounds");
+      }
+    } else {
+      NS_ERROR("Only 2D transformations currently supported");
+    }
+    layerItems->mLayer->SetVisibleRegion(nsIntRegion(visibleRect));
+  }
+}
+
+} // anonymous namespace
+
+already_AddRefed<Layer>
+FrameLayerBuilder::MakeContainerLayerFor(nsDisplayListBuilder* aBuilder,
+                                         LayerManager* aManager,
+                                         nsDisplayItem* aContainer,
+                                         const nsDisplayList& aChildren)
+{
+  // If there's only one layer, then in principle we can try to flatten
+  // things by returning that layer here. But that adds complexity to
+  // retained layer management so we don't do it. Layer backends can
+  // flatten internally.
+  nsRefPtr<ContainerLayer> container = aManager->CreateContainerLayer();
+  if (!container)
+    return nsnull;
+
+  nsAutoTArray<LayerItems*,10> layerItems;
+  BuildLayers(aBuilder, aChildren, aManager, &layerItems);
+
+  Layer* lastChild = nsnull;
+  for (PRUint32 i = 0; i < layerItems.Length(); ++i) {
+    Layer* child = layerItems[i]->mLayer;
+    container->InsertAfter(child, lastChild);
+    lastChild = child;
+    // release the layer now because the ItemGroup destructor doesn't run;
+    // the container is still holding a reference to it
+    layerItems[i]->mLayer = nsnull;
+  }
+  container->SetIsOpaqueContent(aChildren.IsOpaque());
+  nsRefPtr<Layer> layer = container.forget();
+  return layer.forget();
+}
+
+/* static */ void
+FrameLayerBuilder::DrawThebesLayer(ThebesLayer* aLayer,
+                                   gfxContext* aContext,
+                                   const nsIntRegion& aRegionToDraw,
+                                   void* aCallbackData)
+{
+  LayerItems* layerItems = static_cast<LayerItems*>(aLayer->GetUserData());
+  nsDisplayListBuilder* builder =
+    static_cast<nsDisplayListBuilder*>(aCallbackData);
+
+  // For now, we'll ignore toDraw and just draw the entire visible
+  // area, because the "visible area" is already confined to just the
+  // area that needs to be repainted. Later, when we start reusing layers
+  // from paint to paint, we'll need to pay attention to toDraw and
+  // actually try to avoid drawing stuff that's not in it.
+
+  // Our list may contain content with different prescontexts at
+  // different zoom levels. 'rc' contains the nsIRenderingContext
+  // used for the previous display item, and lastPresContext is the
+  // prescontext for that item. We also cache the clip state for that
+  // item.
+  nsRefPtr<nsIRenderingContext> rc;
+  nsPresContext* lastPresContext = nsnull;
+  gfxRect currentClip;
+  PRBool setClipRect = PR_FALSE;
+  NS_ASSERTION(layerItems->mItems, "No empty layers allowed");
+  for (ItemGroup* group = layerItems->mItems; group;
+       group = group->mNextItemsForLayer) {
+    // If the new desired clip state is different from the current state,
+    // update the clip.
+    if (setClipRect != group->mHasClipRect ||
+        (group->mHasClipRect && group->mClipRect != currentClip)) {
+      if (setClipRect) {
+        aContext->Restore();
+      }
+      setClipRect = group->mHasClipRect;
+      if (setClipRect) {
+        aContext->Save();
+        aContext->NewPath();
+        aContext->Rectangle(group->mClipRect, PR_TRUE);
+        aContext->Clip();
+        currentClip = group->mClipRect;
+      }
+    }
+    NS_ASSERTION(group->mStartItem, "No empty groups allowed");
+    for (nsDisplayItem* item = group->mStartItem; item != group->mEndItem;
+         item = item->GetAbove()) {
+      nsPresContext* presContext = item->GetUnderlyingFrame()->PresContext();
+      if (presContext != lastPresContext) {
+        // Create a new rendering context with the right
+        // appunits-per-dev-pixel.
+        nsresult rv =
+          presContext->DeviceContext()->CreateRenderingContextInstance(*getter_AddRefs(rc));
+        if (NS_FAILED(rv))
+          break;
+        rc->Init(presContext->DeviceContext(), aContext);
+        lastPresContext = presContext;
+      }
+      item->Paint(builder, rc);
+    }
+  }
+  if (setClipRect) {
+    aContext->Restore();
+  }
+}
+
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/layout/base/FrameLayerBuilder.h
@@ -0,0 +1,77 @@
+/* -*- 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) 2010
+ * 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 FRAMELAYERBUILDER_H_
+#define FRAMELAYERBUILDER_H_
+
+#include "Layers.h"
+
+class nsDisplayListBuilder;
+class nsDisplayList;
+class nsDisplayItem;
+
+namespace mozilla {
+
+class FrameLayerBuilder {
+public:
+  typedef mozilla::layers::Layer Layer;
+  typedef mozilla::layers::ThebesLayer ThebesLayer;
+  typedef mozilla::layers::LayerManager LayerManager;
+
+  /**
+   * Create a container layer for a display item that contains a child
+   * list, either reusing an existing one or creating a new one.
+   * aContainer may be null, in which case we construct a root layer.
+   */
+  already_AddRefed<Layer> MakeContainerLayerFor(nsDisplayListBuilder* aBuilder,
+                                                LayerManager* aManager,
+                                                nsDisplayItem* aContainer,
+                                                const nsDisplayList& aChildren);
+
+  /**
+   * This callback must be provided to EndTransaction. The callback data
+   * must be the nsDisplayListBuilder containing this FrameLayerBuilder.
+   */
+  static void DrawThebesLayer(ThebesLayer* aLayer,
+                              gfxContext* aContext,
+                              const nsIntRegion& aRegionToDraw,
+                              void* aCallbackData);
+};
+
+}
+
+#endif /* FRAMELAYERBUILDER_H_ */
--- a/layout/base/Makefile.in
+++ b/layout/base/Makefile.in
@@ -54,16 +54,17 @@ LIBXUL_LIBRARY	= 1
 
 
 
 XPIDLSRCS	= \
 		nsIStyleSheetService.idl	\
 		$(NULL)
 
 EXPORTS		= \
+		FrameLayerBuilder.h \
 		FramePropertyTable.h \
 		nsBidi.h \
 		nsBidiPresUtils.h \
 		nsCaret.h \
 		nsCSSFrameConstructor.h \
 		nsChangeHint.h \
 		nsCompatibility.h \
 		nsDisplayList.h \
@@ -83,16 +84,17 @@ EXPORTS		= \
 		nsPresContext.h \
 		nsPresState.h \
 		nsRefreshDriver.h \
 		nsStyleChangeList.h \
 		nsStyleConsts.h \
 		$(NULL)
 
 CPPSRCS		= \
+		FrameLayerBuilder.cpp \
 		FramePropertyTable.cpp \
 		nsCSSColorUtils.cpp \
 		nsCSSFrameConstructor.cpp \
 		nsCSSRendering.cpp \
 		nsCSSRenderingBorders.cpp \
 		nsCaret.cpp \
 		nsChildIterator.cpp \
 		nsCounterManager.cpp \
--- a/layout/base/nsDisplayList.cpp
+++ b/layout/base/nsDisplayList.cpp
@@ -57,16 +57,17 @@
 #include "nsSVGIntegrationUtils.h"
 #endif
 #include "nsLayoutUtils.h"
 
 #include "imgIContainer.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "BasicLayers.h"
 
+using namespace mozilla;
 using namespace mozilla::layers;
 
 nsDisplayListBuilder::nsDisplayListBuilder(nsIFrame* aReferenceFrame,
     PRBool aIsForEvents, PRBool aBuildCaret)
     : mReferenceFrame(aReferenceFrame),
       mMovingFrame(nsnull),
       mSaveVisibleRegionOfMovingContent(nsnull),
       mIgnoreScrollFrame(nsnull),
@@ -400,380 +401,16 @@ nsDisplayList::ComputeVisibility(nsDispl
     movingContentVisibleRegionAfterMove);
 
   mIsOpaque = aVisibleRegion->IsEmpty();
 #ifdef DEBUG
   mDidComputeVisibility = PR_TRUE;
 #endif
 }
 
-namespace {
-/**
- * This class iterates through a display list tree, descending only into
- * nsDisplayClip items, and returns each display item encountered during
- * such iteration. Along with each item we also return the clip rect
- * accumulated for the item.
- */
-class ClippedItemIterator {
-public:
-  ClippedItemIterator(const nsDisplayList* aList)
-  {
-    DescendIntoList(aList, nsnull, nsnull);
-    AdvanceToItem();
-  }
-  PRBool IsDone()
-  {
-    return mStack.IsEmpty();
-  }
-  void Next()
-  {
-    State* top = StackTop();
-    top->mItem = top->mItem->GetAbove();
-    AdvanceToItem();
-  }
-  // Returns null if there is no clipping affecting the item. The
-  // clip rect is in device pixels
-  const gfxRect* GetEffectiveClipRect()
-  {
-    State* top = StackTop();
-    return top->mHasClipRect ? &top->mEffectiveClipRect : nsnull;
-  }
-  nsDisplayItem* Item()
-  {
-    return StackTop()->mItem;
-  }
-
-private:
-  // We maintain a stack of state objects. Each State object represents
-  // where we're up to in the iteration of a list.
-  struct State {
-    // The current item we're at in the list
-    nsDisplayItem* mItem;
-    // The effective clip rect applying to all the items in this list
-    gfxRect mEffectiveClipRect;
-    PRPackedBool mHasClipRect;
-  };
-
-  State* StackTop()
-  {
-    return &mStack[mStack.Length() - 1];
-  }
-  void DescendIntoList(const nsDisplayList* aList,
-                       nsPresContext* aPresContext,
-                       const nsRect* aClipRect)
-  {
-    State* state = mStack.AppendElement();
-    if (!state)
-      return;
-    if (mStack.Length() >= 2) {
-      *state = mStack[mStack.Length() - 2];
-    } else {
-      state->mHasClipRect = PR_FALSE;
-    }
-    state->mItem = aList->GetBottom();
-    if (aClipRect) {
-      gfxRect r(aClipRect->x, aClipRect->y, aClipRect->width, aClipRect->height);
-      r.ScaleInverse(aPresContext->AppUnitsPerDevPixel());
-      if (state->mHasClipRect) {
-        state->mEffectiveClipRect = state->mEffectiveClipRect.Intersect(r);
-      } else {
-        state->mEffectiveClipRect = r;
-        state->mHasClipRect = PR_TRUE;
-      }
-    }
-  }
-  // Advances to an item that the iterator should return.
-  void AdvanceToItem()
-  {
-    while (!mStack.IsEmpty()) {
-      State* top = StackTop();
-      if (!top->mItem) {
-        mStack.SetLength(mStack.Length() - 1);
-        if (!mStack.IsEmpty()) {
-          top = StackTop();
-          top->mItem = top->mItem->GetAbove();
-        }
-        continue;
-      }
-      if (top->mItem->GetType() != nsDisplayItem::TYPE_CLIP)
-        return;
-      nsDisplayClip* clipItem = static_cast<nsDisplayClip*>(top->mItem);
-      nsRect clip = clipItem->GetClipRect();
-      DescendIntoList(clipItem->GetList(),
-                      clipItem->GetClippingFrame()->PresContext(),
-                      &clip);
-    }
-  }
-
-  nsAutoTArray<State,10> mStack;
-};
-}
-
-/**
- * Given a (possibly clipped) display item in aItem, try to append it to
- * the items in aGroup. If aItem is the next item in the sublist in
- * aGroup, and the clipping matches, we can just update aGroup in-place,
- * otherwise we'll allocate a new ItemGroup, add it to the linked list,
- * and put aItem in the new ItemGroup. We return the ItemGroup into which
- * aItem was inserted.
- */
-static nsDisplayList::ItemGroup*
-AddToItemGroup(nsDisplayListBuilder* aBuilder,
-               nsDisplayList::ItemGroup* aGroup, nsDisplayItem* aItem,
-               const gfxRect* aClipRect) {
-  NS_ASSERTION(!aGroup->mNextItemsForLayer,
-               "aGroup must be the last group in the chain");
-
-  if (!aGroup->mStartItem) {
-    aGroup->mStartItem = aItem;
-    aGroup->mEndItem = aItem->GetAbove();
-    aGroup->mHasClipRect = aClipRect != nsnull;
-    if (aClipRect) {
-      aGroup->mClipRect = *aClipRect;
-    }
-    return aGroup;
-  }
-
-  if (aGroup->mEndItem == aItem &&
-      (aGroup->mHasClipRect
-       ? (aClipRect && aGroup->mClipRect == *aClipRect)
-       : !aClipRect))  {
-    aGroup->mEndItem = aItem->GetAbove();
-    return aGroup;
-  }
-
-  nsDisplayList::ItemGroup* itemGroup =
-    new (aBuilder) nsDisplayList::ItemGroup();
-  if (!itemGroup)
-    return aGroup;
-  aGroup->mNextItemsForLayer = itemGroup;
-  return AddToItemGroup(aBuilder, itemGroup, aItem, aClipRect);
-}
-
-/**
- * Create an empty Thebes layer, with an empty ItemGroup associated with
- * it, and append it to aLayers.
- */
-static nsDisplayList::ItemGroup*
-CreateEmptyThebesLayer(nsDisplayListBuilder* aBuilder,
-                       LayerManager* aManager,
-                       nsTArray<nsDisplayList::LayerItems>* aLayers) {
-  nsDisplayList::ItemGroup* itemGroup =
-    new (aBuilder) nsDisplayList::ItemGroup();
-  if (!itemGroup)
-    return nsnull;
-  nsRefPtr<ThebesLayer> thebesLayer =
-    aManager->CreateThebesLayer();
-  if (!thebesLayer)
-    return nsnull;
-  nsDisplayList::LayerItems* layerItems =
-    aLayers->AppendElement(nsDisplayList::LayerItems(itemGroup));
-  layerItems->mThebesLayer = thebesLayer;
-  layerItems->mLayer = thebesLayer.forget();
-  return itemGroup;
-}
-
-static PRBool
-IsAllUniform(nsDisplayListBuilder* aBuilder, nsDisplayList::ItemGroup* aGroup,
-             nscolor* aColor)
-{
-  nsRect visibleRect = aGroup->mStartItem->GetVisibleRect();
-  nscolor finalColor = NS_RGBA(0,0,0,0);
-  for (nsDisplayList::ItemGroup* group = aGroup; group;
-       group = group->mNextItemsForLayer) {
-    for (nsDisplayItem* item = group->mStartItem; item != group->mEndItem;
-         item = item->GetAbove()) {
-      nscolor color;
-      if (visibleRect != item->GetVisibleRect())
-        return PR_FALSE;
-      if (!item->IsUniform(aBuilder, &color))
-        return PR_FALSE;
-      finalColor = NS_ComposeColors(finalColor, color);
-    }
-  }
-  *aColor = finalColor;
-  return PR_TRUE;
-}
-
-/**
- * This is the heart of layout's integration with layers. We
- * use a ClippedItemIterator to iterate through descendant display
- * items. Each item either has its own layer or is assigned to a
- * ThebesLayer. We create ThebesLayers as necessary, although we try
- * to put items in the bottom-most ThebesLayer because that is most
- * likely to be able to render with an opaque background, which will often
- * be required for subpixel text antialiasing to work.
- */
-void nsDisplayList::BuildLayers(nsDisplayListBuilder* aBuilder,
-                                LayerManager* aManager,
-                                nsTArray<LayerItems>* aLayers) const {
-  NS_ASSERTION(aLayers->IsEmpty(), "aLayers must be initially empty");
-
-  // Create "bottom" Thebes layer. We'll try to put as much content
-  // as possible in this layer because if the container is filled with
-  // opaque content, this bottommost layer can also be treated as opaque,
-  // which means content in this layer can have subpixel AA.
-  // firstThebesLayerItems always points to the last ItemGroup for the
-  // first Thebes layer.
-  ItemGroup* firstThebesLayerItems =
-    CreateEmptyThebesLayer(aBuilder, aManager, aLayers);
-  if (!firstThebesLayerItems)
-    return;
-  // lastThebesLayerItems always points to the last ItemGroup for the
-  // topmost layer, if it's a ThebesLayer. If the top layer is not a
-  // Thebes layer, this is null.
-  ItemGroup* lastThebesLayerItems = firstThebesLayerItems;
-  // This region contains the bounds of all the content that is above
-  // the first Thebes layer.
-  nsRegion areaAboveFirstThebesLayer;
-
-  for (ClippedItemIterator iter(this); !iter.IsDone(); iter.Next()) {
-    nsDisplayItem* item = iter.Item();
-    const gfxRect* clipRect = iter.GetEffectiveClipRect();
-    // Ask the item if it manages its own layer
-    nsRefPtr<Layer> layer = item->BuildLayer(aBuilder, aManager);
-    nsRect bounds = item->GetBounds(aBuilder);
-    // We set layerItems to point to the LayerItems object where the
-    // item ends up.
-    LayerItems* layerItems = nsnull;
-    if (layer) {
-      // item has a dedicated layer. Add it to the list, with an ItemGroup
-      // covering this item only.
-      ItemGroup* itemGroup = new (aBuilder) ItemGroup();
-      if (itemGroup) {
-        AddToItemGroup(aBuilder, itemGroup, item, clipRect);
-        layerItems = aLayers->AppendElement(LayerItems(itemGroup));
-        if (layerItems) {
-          if (itemGroup->mHasClipRect) {
-            gfxRect r = itemGroup->mClipRect;
-            r.Round();
-            nsIntRect intRect(r.X(), r.Y(), r.Width(), r.Height());
-            layer->IntersectClipRect(intRect);
-          }
-          layerItems->mLayer = layer.forget();
-        }
-      }
-      // This item is above the first Thebes layer.
-      areaAboveFirstThebesLayer.Or(areaAboveFirstThebesLayer, bounds);
-      lastThebesLayerItems = nsnull;
-    } else {
-      // No dedicated layer. Add it to a Thebes layer. First try to add
-      // it to the first Thebes layer, which we can do if there's no
-      // content between the first Thebes layer and our display item that
-      // overlaps our display item.
-      if (!areaAboveFirstThebesLayer.Intersects(bounds)) {
-        firstThebesLayerItems =
-          AddToItemGroup(aBuilder, firstThebesLayerItems, item, clipRect);
-        layerItems = &aLayers->ElementAt(0);
-      } else if (lastThebesLayerItems) {
-        // Try to add to the last Thebes layer
-        lastThebesLayerItems =
-          AddToItemGroup(aBuilder, lastThebesLayerItems, item, clipRect);
-        // This item is above the first Thebes layer.
-        areaAboveFirstThebesLayer.Or(areaAboveFirstThebesLayer, bounds);
-        layerItems = &aLayers->ElementAt(aLayers->Length() - 1);
-      } else {
-        // Create a new Thebes layer
-        ItemGroup* itemGroup =
-          CreateEmptyThebesLayer(aBuilder, aManager, aLayers);
-        if (itemGroup) {
-          lastThebesLayerItems =
-            AddToItemGroup(aBuilder, itemGroup, item, clipRect);
-          NS_ASSERTION(lastThebesLayerItems == itemGroup,
-                       "AddToItemGroup shouldn't create a new group if the "
-                       "initial group is empty");
-          // This item is above the first Thebes layer.
-          areaAboveFirstThebesLayer.Or(areaAboveFirstThebesLayer, bounds);
-          layerItems = &aLayers->ElementAt(aLayers->Length() - 1);
-        }
-      }
-    }
-
-    if (layerItems) {
-      // Update the visible region of the layer to account for the new
-      // item
-      nscoord appUnitsPerDevPixel =
-        item->GetUnderlyingFrame()->PresContext()->AppUnitsPerDevPixel();
-      layerItems->mVisibleRect.UnionRect(layerItems->mVisibleRect,
-        item->mVisibleRect.ToNearestPixels(appUnitsPerDevPixel));
-    }
-  }
-
-  if (!firstThebesLayerItems->mStartItem) {
-    // The first Thebes layer has nothing in it. Delete the layer.
-    aLayers->RemoveElementAt(0);
-  }
-
-  for (PRUint32 i = 0; i < aLayers->Length(); ++i) {
-    LayerItems* layerItems = &aLayers->ElementAt(i);
-
-    nscolor color;
-    // Only convert layers with identity transform to ColorLayers, for now.
-    // This simplifies the code to set the clip region.
-    if (layerItems->mThebesLayer &&
-        IsAllUniform(aBuilder, layerItems->mItems, &color) &&
-        layerItems->mLayer->GetTransform().IsIdentity()) {
-      nsRefPtr<ColorLayer> layer = aManager->CreateColorLayer();
-      layer->SetClipRect(layerItems->mThebesLayer->GetClipRect());
-      // Clip to mVisibleRect to ensure only the pixels we want are filled.
-      layer->IntersectClipRect(layerItems->mVisibleRect);
-      layer->SetColor(gfxRGBA(color));
-      layerItems->mLayer = layer.forget();
-      layerItems->mThebesLayer = nsnull;
-    }
-
-    gfxMatrix transform;
-    nsIntRect visibleRect = layerItems->mVisibleRect;
-    if (layerItems->mLayer->GetTransform().Is2D(&transform)) {
-      // if 'transform' is not invertible, then nothing will be displayed
-      // for the layer, so it doesn't really matter what we do here
-      transform.Invert();
-      gfxRect layerVisible = transform.TransformBounds(
-          gfxRect(visibleRect.x, visibleRect.y, visibleRect.width, visibleRect.height));
-      layerVisible.RoundOut();
-      if (NS_FAILED(nsLayoutUtils::GfxRectToIntRect(layerVisible, &visibleRect))) {
-        NS_ERROR("Visible rect transformed out of bounds");
-      }
-    } else {
-      NS_ERROR("Only 2D transformations currently supported");
-    }
-    layerItems->mLayer->SetVisibleRegion(nsIntRegion(visibleRect));
-  }
-}
-
-/**
- * We build a single layer by building a list of layers needed for
- * all the display items and building a container layer to hold them.
- */
-already_AddRefed<Layer>
-nsDisplayList::BuildLayer(nsDisplayListBuilder* aBuilder,
-                          LayerManager* aManager,
-                          nsTArray<LayerItems>* aLayers) const {
-  // If there's only one layer, then in principle we can try to flatten
-  // things by returning that layer here. But that adds complexity to
-  // retained layer management so we don't do it. Layer backends can
-  // flatten internally.
-  nsRefPtr<ContainerLayer> container =
-    aManager->CreateContainerLayer();
-  if (!container)
-    return nsnull;
-
-  BuildLayers(aBuilder, aManager, aLayers);
-
-  Layer* lastChild = nsnull;
-  for (PRUint32 i = 0; i < aLayers->Length(); ++i) {
-    Layer* child = aLayers->ElementAt(i).mLayer;
-    container->InsertAfter(child, lastChild);
-    lastChild = child;
-  }
-  container->SetIsOpaqueContent(mIsOpaque);
-  nsRefPtr<Layer> layer = container.forget();
-  return layer.forget();
-}
-
 void nsDisplayList::PaintRoot(nsDisplayListBuilder* aBuilder,
                               nsIRenderingContext* aCtx,
                               PRUint32 aFlags) const {
   PaintForFrame(aBuilder, aCtx, aBuilder->ReferenceFrame(), aFlags);
 }
 
 /**
  * We paint by executing a layer manager transaction, constructing a
@@ -808,109 +445,32 @@ void nsDisplayList::PaintForFrame(nsDisp
   }
 
   if (aCtx) {
     layerManager->BeginTransactionWithTarget(aCtx->ThebesContext());
   } else {
     layerManager->BeginTransaction();
   }
 
-  nsAutoTArray<LayerItems,10> layers;
-  nsRefPtr<Layer> root = BuildLayer(aBuilder, layerManager, &layers);
+  nsRefPtr<Layer> root = aBuilder->LayerBuilder()->
+    MakeContainerLayerFor(aBuilder, layerManager, nsnull, *this);
   if (!root)
     return;
 
   nsIntRect visible =
     mVisibleRect.ToNearestPixels(aForFrame->PresContext()->AppUnitsPerDevPixel());
   root->SetVisibleRegion(nsIntRegion(visible));
 
   layerManager->SetRoot(root);
-  layerManager->EndConstruction();
-  PaintThebesLayers(aBuilder, layers);
-  layerManager->EndTransaction();
+  layerManager->EndTransaction(FrameLayerBuilder::DrawThebesLayer,
+                               aBuilder);
 
   nsCSSRendering::DidPaint();
 }
 
-void
-nsDisplayList::PaintThebesLayers(nsDisplayListBuilder* aBuilder,
-                                 const nsTArray<LayerItems>& aLayers) const
-{
-  for (PRUint32 i = 0; i < aLayers.Length(); ++i) {
-    ThebesLayer* thebesLayer = aLayers[i].mThebesLayer;
-    if (!thebesLayer) {
-      // Just ask the display item to paint any Thebes layers that it
-      // used to construct its layer
-      aLayers[i].mItems->mStartItem->PaintThebesLayers(aBuilder);
-      continue;
-    }
-
-    // OK, we have a real Thebes layer. Start drawing into it.
-    nsIntRegion toDraw;
-    gfxContext* ctx = thebesLayer->BeginDrawing(&toDraw);
-    if (!ctx)
-      continue;
-    // For now, we'll ignore toDraw and just draw the entire visible
-    // area, because the "visible area" is already confined to just the
-    // area that needs to be repainted. Later, when we start reusing layers
-    // from paint to paint, we'll need to pay attention to toDraw and
-    // actually try to avoid drawing stuff that's not in it.
-
-    // Our list may contain content with different prescontexts at
-    // different zoom levels. 'rc' contains the nsIRenderingContext
-    // used for the previous display item, and lastPresContext is the
-    // prescontext for that item. We also cache the clip state for that
-    // item.
-    nsRefPtr<nsIRenderingContext> rc;
-    nsPresContext* lastPresContext = nsnull;
-    gfxRect currentClip;
-    PRBool setClipRect = PR_FALSE;
-    NS_ASSERTION(aLayers[i].mItems, "No empty layers allowed");
-    for (ItemGroup* group = aLayers[i].mItems; group;
-         group = group->mNextItemsForLayer) {
-      // If the new desired clip state is different from the current state,
-      // update the clip.
-      if (setClipRect != group->mHasClipRect ||
-          (group->mHasClipRect && group->mClipRect != currentClip)) {
-        if (setClipRect) {
-          ctx->Restore();
-        }
-        setClipRect = group->mHasClipRect;
-        if (setClipRect) {
-          ctx->Save();
-          ctx->NewPath();
-          ctx->Rectangle(group->mClipRect, PR_TRUE);
-          ctx->Clip();
-          currentClip = group->mClipRect;
-        }
-      }
-      NS_ASSERTION(group->mStartItem, "No empty groups allowed");
-      for (nsDisplayItem* item = group->mStartItem; item != group->mEndItem;
-           item = item->GetAbove()) {
-        nsPresContext* presContext = item->GetUnderlyingFrame()->PresContext();
-        if (presContext != lastPresContext) {
-          // Create a new rendering context with the right
-          // appunits-per-dev-pixel.
-          nsresult rv =
-            presContext->DeviceContext()->CreateRenderingContextInstance(*getter_AddRefs(rc));
-          if (NS_FAILED(rv))
-            break;
-          rc->Init(presContext->DeviceContext(), ctx);
-          lastPresContext = presContext;
-        }
-        item->Paint(aBuilder, rc);
-      }
-    }
-    if (setClipRect) {
-      ctx->Restore();
-    }
-    thebesLayer->EndDrawing();
-  }
-}
-
 PRUint32 nsDisplayList::Count() const {
   PRUint32 count = 0;
   for (nsDisplayItem* i = GetBottom(); i; i = i->GetAbove()) {
     ++count;
   }
   return count;
 }
 
@@ -1579,30 +1139,25 @@ PRBool nsDisplayOpacity::IsOpaque(nsDisp
   // been created.
   return PR_FALSE;
 }
 
 // nsDisplayOpacity uses layers for rendering
 already_AddRefed<Layer>
 nsDisplayOpacity::BuildLayer(nsDisplayListBuilder* aBuilder,
                              LayerManager* aManager) {
-  nsRefPtr<Layer> layer =
-    mList.BuildLayer(aBuilder, aManager, &mChildLayers);
+  nsRefPtr<Layer> layer = aBuilder->LayerBuilder()->
+    MakeContainerLayerFor(aBuilder, aManager, this, mList);
   if (!layer)
     return nsnull;
 
   layer->SetOpacity(mFrame->GetStyleDisplay()->mOpacity);
   return layer.forget();
 }
 
-void
-nsDisplayOpacity::PaintThebesLayers(nsDisplayListBuilder* aBuilder) {
-  mList.PaintThebesLayers(aBuilder, mChildLayers);
-}
-
 PRBool nsDisplayOpacity::ComputeVisibility(nsDisplayListBuilder* aBuilder,
                                            nsRegion* aVisibleRegion,
                                            nsRegion* aVisibleRegionBeforeMove) {
   NS_ASSERTION((aVisibleRegionBeforeMove != nsnull) == aBuilder->HasMovingFrames(),
                "Should have aVisibleRegionBeforeMove when there are moving frames");
 
   // 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
--- a/layout/base/nsDisplayList.h
+++ b/layout/base/nsDisplayList.h
@@ -48,22 +48,23 @@
 #include "nsCOMPtr.h"
 #include "nsIFrame.h"
 #include "nsPoint.h"
 #include "nsRect.h"
 #include "nsISelection.h"
 #include "nsCaret.h"
 #include "plarena.h"
 #include "Layers.h"
+#include "nsRegion.h"
+#include "FrameLayerBuilder.h"
 
 #include <stdlib.h>
 
 class nsIPresShell;
 class nsIContent;
-class nsRegion;
 class nsIRenderingContext;
 class nsIDeviceContext;
 class nsDisplayTableItem;
 class nsDisplayItem;
 
 /*
  * An nsIFrame can have many different visual parts. For example an image frame
  * can have a background, border, and outline, the image itself, and a
@@ -115,16 +116,17 @@ class nsDisplayItem;
  * the display list memory using a PLArena. It also establishes the reference
  * coordinate system for all display list items. Some of the parameters are
  * available from the prescontext/presshell, but we copy them into the builder
  * for faster/more convenient access.
  */
 class NS_STACK_CLASS nsDisplayListBuilder {
 public:
   typedef mozilla::FramePropertyDescriptor FramePropertyDescriptor;
+  typedef mozilla::FrameLayerBuilder FrameLayerBuilder;
 
   /**
    * @param aReferenceFrame the frame at the root of the subtree; its origin
    * is the origin of the reference coordinate system for this display list
    * @param aIsForEvents PR_TRUE if we're creating this list in order to
    * determine which frame is under the mouse position
    * @param aBuildCaret whether or not we should include the caret in any
    * display lists that we make.
@@ -334,16 +336,21 @@ public:
    * that might not be displayed, we mark the placeholders and their ancestors
    * to ensure that display list construction descends into them
    * anyway. nsDisplayListBuilder will take care of unmarking them when it is
    * destroyed.
    */
   void MarkFramesForDisplayList(nsIFrame* aDirtyFrame,
                                 const nsFrameList& aFrames,
                                 const nsRect& aDirtyRect);
+
+  /**
+   * Return the FrameLayerBuilder.
+   */
+  FrameLayerBuilder* LayerBuilder() { return &mLayerBuilder; }
   
   /**
    * Allocate memory in our arena. It will only be freed when this display list
    * builder is destroyed. This memory holds nsDisplayItems. nsDisplayItem
    * destructors are called as soon as the item is no longer used.
    */
   void* Allocate(size_t aSize);
   
@@ -402,17 +409,18 @@ private:
     nsIFrame*     mCaretFrame;
     PRUint32      mFirstFrameMarkedForDisplay;
   };
   PresShellState* CurrentPresShellState() {
     NS_ASSERTION(mPresShellStates.Length() > 0,
                  "Someone forgot to enter a presshell");
     return &mPresShellStates[mPresShellStates.Length() - 1];
   }
-  
+
+  FrameLayerBuilder              mLayerBuilder;
   nsIFrame*                      mReferenceFrame;
   nsIFrame*                      mMovingFrame;
   nsRegion*                      mSaveVisibleRegionOfMovingContent;
   nsIFrame*                      mIgnoreScrollFrame;
   nsPoint                        mMoveDelta; // only valid when mMovingFrame is non-null
   PLArenaPool                    mPool;
   nsCOMPtr<nsISelection>         mBoundingSelection;
   nsAutoTArray<PresShellState,8> mPresShellStates;
@@ -573,23 +581,16 @@ public:
    * rendering) instead of cairo.
    * 
    * The caller (nsDisplayList) is responsible for setting the visible
    * region of the layer.
    */
   virtual already_AddRefed<Layer> BuildLayer(nsDisplayListBuilder* aBuilder,
                                              LayerManager* aManager)
   { return nsnull; }
-  /**
-   * If BuildLayer returned non-null, then this method is called to
-   * paint any ThebesLayers which are descendants of the returned layer.
-   */
-  virtual void PaintThebesLayers(nsDisplayListBuilder* aBuilder)
-  {
-  }
 
   /**
    * On entry, aVisibleRegion contains the region (relative to ReferenceFrame())
    * which may be visible. If the display item opaquely covers an area, it
    * can remove that area from aVisibleRegion before returning.
    * If we're doing scroll analysis with moving frames, then
    * aVisibleRegionBeforeMove will be non-null and contains the region that
    * would have been visible before the move. aVisibleRegion contains the
@@ -625,16 +626,20 @@ public:
   }
   
   /**
    * If this is a leaf item we return null, otherwise we return the wrapped
    * list.
    */
   virtual nsDisplayList* GetList() { return nsnull; }
 
+  /**
+   * Returns the visible rect. Should only be called after ComputeVisibility
+   * has happened.
+   */
   const nsRect& GetVisibleRect() { return mVisibleRect; }
   
 #ifdef NS_DEBUG
   /**
    * For debugging and stuff
    */
   virtual const char* Name() = 0;
 #endif
@@ -887,91 +892,16 @@ public:
   /**
    * Find the topmost display item that returns a non-null frame, and return
    * the frame.
    */
   void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
                nsDisplayItem::HitTestState* aState,
                nsTArray<nsIFrame*> *aOutFrames) const;
 
-  /**
-   * This class represents a sublist of consecutive items in an nsDisplayList.
-   * The first item in the sublist is mStartItem and the last item
-   * is the item before mEndItem.
-   * 
-   * These sublists are themselves organized into a linked list of all
-   * the ItemGroups associated with a given layer, via mNextItemsForLayer.
-   * This list will have more than one element if the display items in a layer
-   * come from different nsDisplayLists, or if they come from the same
-   * nsDisplayList but they aren't consecutive in that list.
-   * 
-   * These objects are allocated from the nsDisplayListBuilder arena.
-   */
-  struct ItemGroup {
-    // If null, then the item group is empty.
-    nsDisplayItem* mStartItem;
-    nsDisplayItem* mEndItem;
-    ItemGroup* mNextItemsForLayer;
-    // The clipping (if any) that needs to be applied to all these items.
-    gfxRect mClipRect;
-    PRPackedBool mHasClipRect;
-
-    ItemGroup() : mStartItem(nsnull), mEndItem(nsnull),
-      mNextItemsForLayer(nsnull), mHasClipRect(PR_FALSE) {}
-
-    void* operator new(size_t aSize,
-                       nsDisplayListBuilder* aBuilder) CPP_THROW_NEW {
-      return aBuilder->Allocate(aSize);
-    }
-  };
-  /**
-   * This class represents a layer and the display item(s) it
-   * will render. The items are stored in a linked list of ItemGroups.
-   */
-  struct LayerItems {
-    nsRefPtr<Layer> mLayer;
-    // equal to mLayer, or null if mLayer is not a ThebesLayer
-    ThebesLayer* mThebesLayer;
-    ItemGroup* mItems;
-    // The bounds of the visible region for this layer, in device pixels
-    nsIntRect mVisibleRect;
-
-    LayerItems(ItemGroup* aItems) :
-      mThebesLayer(nsnull), mItems(aItems)
-    {
-    }
-  };
-  /**
-   * Compute a list of layers needed to render this display list. The layers
-   * are added to aLayers, which must be empty on entry. This
-   * must be called while aManager is in the construction phase, because
-   * we construct layers belonging to aManager. The layers used to
-   * construct the layer tree (along with the display items associated
-   * with each layer) are returned in aLayers.
-   */
-  void BuildLayers(nsDisplayListBuilder* aBuilder,
-                   LayerManager* aManager,
-                   nsTArray<LayerItems>* aLayers) const;
-  /**
-   * Return a single layer which renders this display list. This
-   * must be called while aManager is in the construction phase, because
-   * we construct layers belonging to aManager. The layers used to
-   * construct the layer tree (along with the display items associated
-   * with each layer) are returned in aLayers.
-   * The caller is responsible for setting the visible region on the layer.
-   */
-  already_AddRefed<Layer> BuildLayer(nsDisplayListBuilder* aBuilder,
-                                     LayerManager* aManager,
-                                     nsTArray<LayerItems>* aLayers) const;
-  /**
-   * Paint the ThebesLayers in the list of layers.
-   */
-  void PaintThebesLayers(nsDisplayListBuilder* aBuilder,
-                         const nsTArray<LayerItems>& aLayers) const;
-
 private:
   // This class is only used on stack, so we don't have to worry about leaking
   // it.  Don't let us be heap-allocated!
   void* operator new(size_t sz) CPP_THROW_NEW;
   
   // Utility function used to massage the list during ComputeVisibility.
   void FlattenTo(nsTArray<nsDisplayItem*>* aElements);
   // Utility function used to massage the list during sorting, to rewrite
@@ -1543,25 +1473,21 @@ public:
 #ifdef NS_BUILD_REFCNT_LOGGING
   virtual ~nsDisplayOpacity();
 #endif
   
   virtual Type GetType() { return TYPE_OPACITY; }
   virtual PRBool IsOpaque(nsDisplayListBuilder* aBuilder);
   virtual already_AddRefed<Layer> BuildLayer(nsDisplayListBuilder* aBuilder,
                                              LayerManager* aManager);
-  virtual void PaintThebesLayers(nsDisplayListBuilder* aBuilder);
   virtual PRBool ComputeVisibility(nsDisplayListBuilder* aBuilder,
                                    nsRegion* aVisibleRegion,
                                    nsRegion* aVisibleRegionBeforeMove);  
   virtual PRBool TryMerge(nsDisplayListBuilder* aBuilder, nsDisplayItem* aItem);
   NS_DISPLAY_DECL_NAME("Opacity")
-
-private:
-  nsTArray<nsDisplayList::LayerItems> mChildLayers;
 };
 
 /**
  * nsDisplayClip can clip a list of items, but we take a single item
  * initially and then later merge other items into it when we merge
  * adjacent matching nsDisplayClips
  */
 class nsDisplayClip : public nsDisplayWrapList {
--- a/layout/base/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -5673,16 +5673,57 @@ nscolor PresShell::ComputeBackstopColor(
     return NS_RGBA(0,0,0,0);
   }
   // Within an opaque widget (or no widget at all), so the backstop
   // color must be totally opaque. The user's default background
   // as reported by the prescontext is guaranteed to be opaque.
   return GetPresContext()->DefaultBackgroundColor();
 }
 
+struct PaintParams {
+  nsIFrame* mFrame;
+  nsPoint mOffsetToRoot;
+  nsPoint mOffsetToWidget;
+  const nsRegion* mDirtyRegion;
+  nscolor mBackgroundColor;
+};
+
+static void DrawThebesLayer(ThebesLayer* aLayer,
+                            gfxContext* aContext,
+                            const nsIntRegion& aRegionToDraw,
+                            void* aCallbackData)
+{
+  PaintParams* params = static_cast<PaintParams*>(aCallbackData);
+  nsIFrame* frame = params->mFrame;
+  if (frame) {
+    // We're drawing into a child window. Don't pass
+    // nsLayoutUtils::PAINT_WIDGET_LAYERS, since that will draw into
+    // the widget for the display root.
+    nsIDeviceContext* devCtx = frame->PresContext()->DeviceContext();
+    nsCOMPtr<nsIRenderingContext> rc;
+    nsresult rv = devCtx->CreateRenderingContextInstance(*getter_AddRefs(rc));
+    if (NS_SUCCEEDED(rv)) {
+      rc->Init(devCtx, aContext);
+      nsRegion dirtyRegion = *params->mDirtyRegion;
+      dirtyRegion.MoveBy(params->mOffsetToRoot);
+      nsIRenderingContext::AutoPushTranslation
+        push(rc, -params->mOffsetToWidget.x, -params->mOffsetToWidget.y);
+      nsLayoutUtils::PaintFrame(rc, frame, dirtyRegion,
+                                params->mBackgroundColor);
+    }
+  } else {
+    aContext->NewPath();
+    aContext->SetColor(gfxRGBA(params->mBackgroundColor));
+    nsIntRect dirtyRect = aRegionToDraw.GetBounds();
+    aContext->Rectangle(
+      gfxRect(dirtyRect.x, dirtyRect.y, dirtyRect.width, dirtyRect.height));
+    aContext->Fill();
+  }
+}
+
 NS_IMETHODIMP
 PresShell::Paint(nsIView*        aDisplayRoot,
                  nsIView*        aViewToPaint,
                  nsIWidget*      aWidgetToPaint,
                  const nsRegion& aDirtyRegion,
                  PRBool          aPaintDefaultBackground)
 {
 #ifdef NS_FUNCTION_TIMER
@@ -5725,52 +5766,27 @@ PresShell::Paint(nsIView*        aDispla
   layerManager->BeginTransaction();
   nsRefPtr<ThebesLayer> root = layerManager->CreateThebesLayer();
   nsIntRect dirtyRect = aDirtyRegion.GetBounds().
     ToOutsidePixels(presContext->AppUnitsPerDevPixel());
   if (root) {
     root->SetVisibleRegion(dirtyRect);
     layerManager->SetRoot(root);
   }
-  layerManager->EndConstruction();
-  if (root) {
-    nsIntRegion toDraw;
-    gfxContext* ctx = root->BeginDrawing(&toDraw);
-    if (ctx) {
-      if (frame) {
-        // We're drawing into a child window. Don't pass
-        // nsLayoutUtils::PAINT_WIDGET_LAYERS, since that will draw into
-        // the widget for the display root.
-        nsIDeviceContext* devCtx = GetPresContext()->DeviceContext();
-        nsCOMPtr<nsIRenderingContext> rc;
-        nsresult rv = devCtx->CreateRenderingContextInstance(*getter_AddRefs(rc));
-        if (NS_SUCCEEDED(rv)) {
-          rc->Init(devCtx, ctx);
-          // Offset to add to aView coordinates to get aWidget coordinates
-          nsPoint offsetToRoot = aViewToPaint->GetOffsetTo(aDisplayRoot);
-          nsRegion dirtyRegion = aDirtyRegion;
-          dirtyRegion.MoveBy(offsetToRoot);
-
-          nsPoint translate = -offsetToRoot + aViewToPaint->ViewToWidgetOffset();
-          nsIRenderingContext::AutoPushTranslation
-            push(rc, translate.x, translate.y);
-
-          nsLayoutUtils::PaintFrame(rc, frame, dirtyRegion, bgcolor);
-        }
-      } else {
-        bgcolor = NS_ComposeColors(bgcolor, mCanvasBackgroundColor);
-        ctx->NewPath();
-        ctx->SetColor(gfxRGBA(bgcolor));
-        ctx->Rectangle(gfxRect(dirtyRect.x, dirtyRect.y, dirtyRect.width, dirtyRect.height));
-        ctx->Fill();
-      }
-    }
-    root->EndDrawing();
-  }
-  layerManager->EndTransaction();
+  if (!frame) {
+    bgcolor = NS_ComposeColors(bgcolor, mCanvasBackgroundColor);
+  }
+  nsPoint offsetToRoot = aViewToPaint->GetOffsetTo(aDisplayRoot);
+  PaintParams params =
+    { frame,
+      offsetToRoot,
+      offsetToRoot - aViewToPaint->ViewToWidgetOffset(),
+      &aDirtyRegion,
+      bgcolor };
+  layerManager->EndTransaction(DrawThebesLayer, &params);
 
   return NS_OK;
 }
 
 // static
 void
 nsIPresShell::SetCapturingContent(nsIContent* aContent, PRUint8 aFlags)
 {