Bug 1143575. Route ImageCompositeNotifications to ImageContainers. r=nical
authorRobert O'Callahan <robert@ocallahan.org>
Mon, 06 Jul 2015 15:02:26 +1200
changeset 285010 8ee029e39ce4ccb4344e915e785dde99a4608a2b
parent 285009 3b2ed2e93f491318359bd4ebaa1d804bf4546b6e
child 285011 07ee1bf0f982923bedd2bf5d475ba2c534faca3b
push id934
push userraliiev@mozilla.com
push dateMon, 26 Oct 2015 12:58:05 +0000
treeherdermozilla-release@05704e35c1d0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnical
bugs1143575
milestone42.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1143575. Route ImageCompositeNotifications to ImageContainers. r=nical For frame statistics to work properly, we have to notify an ImageContainer when it has been composited. This requires a few changes, which have been lumped together in this patch: -- Create PImageContainer and ImageContainerParent/ImageContainerChild. -- Add mFrameID and mProducerID everywhere we're passing around images. -- Route composition notifications from the compositor back to ImageContainerChild.
dom/ipc/TabChild.h
gfx/layers/ImageContainer.cpp
gfx/layers/ImageContainer.h
gfx/layers/client/ClientLayerManager.cpp
gfx/layers/client/ClientLayerManager.h
gfx/layers/client/CompositableClient.cpp
gfx/layers/client/CompositableClient.h
gfx/layers/client/ImageClient.cpp
gfx/layers/client/ImageClient.h
gfx/layers/composite/CanvasLayerComposite.cpp
gfx/layers/composite/CompositableHost.cpp
gfx/layers/composite/CompositableHost.h
gfx/layers/composite/ContentHost.cpp
gfx/layers/composite/ContentHost.h
gfx/layers/composite/ImageHost.cpp
gfx/layers/composite/ImageHost.h
gfx/layers/composite/ImageLayerComposite.cpp
gfx/layers/composite/LayerManagerComposite.h
gfx/layers/composite/PaintedLayerComposite.cpp
gfx/layers/composite/TiledContentHost.cpp
gfx/layers/composite/TiledContentHost.h
gfx/layers/ipc/CompositableForwarder.h
gfx/layers/ipc/CompositableTransactionParent.cpp
gfx/layers/ipc/CompositorParent.cpp
gfx/layers/ipc/ImageBridgeChild.cpp
gfx/layers/ipc/ImageBridgeChild.h
gfx/layers/ipc/ImageBridgeParent.cpp
gfx/layers/ipc/ImageBridgeParent.h
gfx/layers/ipc/ImageContainerParent.cpp
gfx/layers/ipc/ImageContainerParent.h
gfx/layers/ipc/LayerTransactionChild.cpp
gfx/layers/ipc/LayerTransactionParent.cpp
gfx/layers/ipc/LayersMessages.ipdlh
gfx/layers/ipc/PCompositor.ipdl
gfx/layers/ipc/PImageBridge.ipdl
gfx/layers/ipc/PImageContainer.ipdl
gfx/layers/ipc/ShadowLayerParent.cpp
gfx/layers/ipc/ShadowLayerParent.h
gfx/layers/ipc/ShadowLayers.cpp
gfx/layers/ipc/ShadowLayers.h
gfx/layers/moz.build
layout/ipc/RenderFrameParent.h
--- a/dom/ipc/TabChild.h
+++ b/dom/ipc/TabChild.h
@@ -42,16 +42,17 @@ class nsIDOMWindowUtils;
 
 namespace mozilla {
 namespace layout {
 class RenderFrameChild;
 }
 
 namespace layers {
 class APZEventState;
+class ImageCompositeNotification;
 struct SetTargetAPZCCallback;
 struct SetAllowedTouchBehaviorCallback;
 }
 
 namespace widget {
 struct AutoCacheNativeKeyCommands;
 }
 
--- a/gfx/layers/ImageContainer.cpp
+++ b/gfx/layers/ImageContainer.cpp
@@ -9,16 +9,17 @@
 #include "GLImages.h"                   // for SurfaceTextureImage
 #include "gfx2DGlue.h"
 #include "gfxPlatform.h"                // for gfxPlatform
 #include "gfxUtils.h"                   // for gfxUtils
 #include "mozilla/RefPtr.h"             // for already_AddRefed
 #include "mozilla/ipc/CrossProcessMutex.h"  // for CrossProcessMutex, etc
 #include "mozilla/layers/CompositorTypes.h"
 #include "mozilla/layers/ImageBridgeChild.h"  // for ImageBridgeChild
+#include "mozilla/layers/PImageContainerChild.h"
 #include "mozilla/layers/ImageClient.h"  // for ImageClient
 #include "nsISupportsUtils.h"           // for NS_IF_ADDREF
 #include "YCbCrUtils.h"                 // for YCbCr conversions
 #ifdef MOZ_WIDGET_GONK
 #include "GrallocImages.h"
 #endif
 #if defined(MOZ_WIDGET_GONK) && defined(MOZ_B2G_CAMERA) && defined(MOZ_WEBRTC)
 #include "GonkCameraImage.h"
@@ -134,65 +135,95 @@ BufferRecycleBin::GetBuffer(uint32_t aSi
     return new uint8_t[aSize];
 
   uint32_t last = mRecycledBuffers.Length() - 1;
   uint8_t* result = mRecycledBuffers[last].forget();
   mRecycledBuffers.RemoveElementAt(last);
   return result;
 }
 
-ImageContainer::ImageContainer(ImageContainer::Mode flag)
+/**
+ * The child side of PImageContainer. It's best to avoid ImageContainer filling
+ * this role since IPDL objects should be associated with a single thread and
+ * ImageContainer definitely isn't. This object belongs to (and is always
+ * destroyed on) the ImageBridge thread, except when we need to destroy it
+ * during shutdown.
+ * An ImageContainer owns one of these; we have a weak reference to our
+ * ImageContainer.
+ */
+class ImageContainerChild : public PImageContainerChild {
+public:
+  explicit ImageContainerChild(ImageContainer* aImageContainer)
+    : mLock("ImageContainerChild"), mImageContainer(aImageContainer) {}
+  void ForgetImageContainer()
+  {
+    MutexAutoLock lock(mLock);
+    mImageContainer = nullptr;
+  }
+
+  // This protects mImageContainer. This is always taken before the
+  // mImageContainer's monitor (when both need to be held).
+  Mutex mLock;
+  ImageContainer* mImageContainer;
+};
+
+ImageContainer::ImageContainer(Mode flag)
 : mReentrantMonitor("ImageContainer.mReentrantMonitor"),
   mGenerationCounter(++sGenerationCounter),
   mPaintCount(0),
   mPreviousImagePainted(false),
   mImageFactory(new ImageFactory()),
   mRecycleBin(new BufferRecycleBin()),
-  mImageClient(nullptr)
+  mImageClient(nullptr),
+  mIPDLChild(nullptr)
 {
   if (ImageBridgeChild::IsCreated()) {
     // the refcount of this ImageClient is 1. we don't use a RefPtr here because the refcount
     // of this class must be done on the ImageBridge thread.
-    switch(flag) {
+    switch (flag) {
       case SYNCHRONOUS:
         break;
       case ASYNCHRONOUS:
-        mImageClient = ImageBridgeChild::GetSingleton()->CreateImageClient(CompositableType::IMAGE).take();
+        mIPDLChild = new ImageContainerChild(this);
+        mImageClient = ImageBridgeChild::GetSingleton()->CreateImageClient(CompositableType::IMAGE, this).take();
         MOZ_ASSERT(mImageClient);
         break;
       case ASYNCHRONOUS_OVERLAY:
-        mImageClient = ImageBridgeChild::GetSingleton()->CreateImageClient(CompositableType::IMAGE_OVERLAY).take();
+        mIPDLChild = new ImageContainerChild(this);
+        mImageClient = ImageBridgeChild::GetSingleton()->CreateImageClient(CompositableType::IMAGE_OVERLAY, this).take();
         MOZ_ASSERT(mImageClient);
         break;
       default:
         MOZ_ASSERT(false, "This flag is invalid.");
         break;
     }
   }
 }
 
 ImageContainer::~ImageContainer()
 {
   if (IsAsync()) {
-    ImageBridgeChild::DispatchReleaseImageClient(mImageClient);
+    mIPDLChild->ForgetImageContainer();
+    ImageBridgeChild::DispatchReleaseImageClient(mImageClient, mIPDLChild);
   }
 }
 
 already_AddRefed<Image>
 ImageContainer::CreateImage(ImageFormat aFormat)
 {
   ReentrantMonitorAutoEnter mon(mReentrantMonitor);
 
 #ifdef MOZ_WIDGET_GONK
   if (aFormat == ImageFormat::OVERLAY_IMAGE) {
     if (mImageClient && mImageClient->GetTextureInfo().mCompositableType != CompositableType::IMAGE_OVERLAY) {
       // If this ImageContainer is async but the image type mismatch, fix it here
       if (ImageBridgeChild::IsCreated()) {
         ImageBridgeChild::DispatchReleaseImageClient(mImageClient);
-        mImageClient = ImageBridgeChild::GetSingleton()->CreateImageClient(CompositableType::IMAGE_OVERLAY).take();
+        mImageClient = ImageBridgeChild::GetSingleton()->CreateImageClient(
+            CompositableType::IMAGE_OVERLAY, this).take();
       }
     }
   }
 #endif
   if (mImageClient) {
     nsRefPtr<Image> img = mImageClient->CreateImage(aFormat);
     if (img) {
       return img.forget();
@@ -290,17 +321,20 @@ ImageContainer::HasCurrentImage()
 
 void
 ImageContainer::GetCurrentImages(nsTArray<OwningImage>* aImages,
                                  uint32_t* aGenerationCounter)
 {
   ReentrantMonitorAutoEnter mon(mReentrantMonitor);
 
   if (mActiveImage) {
-    aImages->AppendElement()->mImage = mActiveImage;
+    OwningImage* img = aImages->AppendElement();
+    img->mImage = mActiveImage;
+    img->mFrameID = mGenerationCounter;
+    img->mProducerID = 0;
   }
   if (aGenerationCounter) {
     *aGenerationCounter = mGenerationCounter;
   }
 }
 
 gfx::IntSize
 ImageContainer::GetCurrentSize()
@@ -547,10 +581,29 @@ CairoImage::GetTextureClient(Compositabl
     }
     dt->CopySurface(surface, IntRect(IntPoint(), surface->GetSize()), IntPoint());
   }
 
   mTextureClients.Put(forwarder->GetSerial(), textureClient);
   return textureClient;
 }
 
+PImageContainerChild*
+ImageContainer::GetPImageContainerChild()
+{
+  return mIPDLChild;
+}
+
+/* static */ void
+ImageContainer::NotifyComposite(const ImageCompositeNotification& aNotification)
+{
+  ImageContainerChild* child =
+      static_cast<ImageContainerChild*>(aNotification.imageContainerChild());
+  if (child) {
+    MutexAutoLock lock(child->mLock);
+    if (child->mImageContainer) {
+      child->mImageContainer->NotifyCompositeInternal(aNotification);
+    }
+  }
+}
+
 } // namespace
 } // namespace
--- a/gfx/layers/ImageContainer.h
+++ b/gfx/layers/ImageContainer.h
@@ -93,16 +93,19 @@ struct ID3D10ShaderResourceView;
 typedef void* HANDLE;
 
 namespace mozilla {
 
 
 namespace layers {
 
 class ImageClient;
+class ImageCompositeNotification;
+class ImageContainerChild;
+class PImageContainerChild;
 class SharedPlanarYCbCrImage;
 class TextureClient;
 class CompositableClient;
 class GrallocImage;
 
 struct ImageBackendData
 {
   virtual ~ImageBackendData() {}
@@ -280,16 +283,20 @@ class ImageContainer final : public Supp
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ImageContainer)
 public:
   MOZ_DECLARE_WEAKREFERENCE_TYPENAME(ImageContainer)
 
   enum Mode { SYNCHRONOUS = 0x0, ASYNCHRONOUS = 0x01, ASYNCHRONOUS_OVERLAY = 0x02 };
 
   explicit ImageContainer(ImageContainer::Mode flag = SYNCHRONOUS);
 
+  typedef int32_t FrameID;
+  typedef int32_t ProducerID;
+
+
   /**
    * Create an Image in one of the given formats.
    * Picks the "best" format from the list and creates an Image of that
    * format.
    * Returns null if this backend does not support any of the formats.
    * Can be called on any thread. This method takes mReentrantMonitor
    * when accessing thread-shared state.
    */
@@ -372,16 +379,18 @@ public:
    * Can be called on any thread. This method takes mReentrantMonitor
    * when accessing thread-shared state.
    */
   bool HasCurrentImage();
 
   struct OwningImage {
     nsRefPtr<Image> mImage;
     TimeStamp mTimeStamp;
+    FrameID mFrameID;
+    ProducerID mProducerID;
   };
   /**
    * Copy the current Image list to aImages.
    * This has to add references since otherwise there are race conditions
    * where the current image is destroyed before the caller can add
    * a reference.
    * Can be called on any thread.
    * May return an empty list to indicate there is no current image.
@@ -457,16 +466,20 @@ public:
       // While we were painting this image, the current image changed. We
       // still must count it as painted, but can't set mPaintTime, since we're
       // no longer the current image.
       mPaintCount++;
       mPreviousImagePainted = true;
     }
   }
 
+  PImageContainerChild* GetPImageContainerChild();
+
+  static void NotifyComposite(const ImageCompositeNotification& aNotification);
+
 private:
   typedef mozilla::ReentrantMonitor ReentrantMonitor;
 
   // Private destructor, to discourage deletion outside of Release():
   B2G_ACL_EXPORT ~ImageContainer();
 
   void SetCurrentImageInternal(Image* aImage);
 
@@ -484,16 +497,18 @@ private:
   // are accurate. Must be called by SetCurrentImage() implementations with
   // mReentrantMonitor held.
   void CurrentImageChanged() {
     mReentrantMonitor.AssertCurrentThreadIn();
     mPreviousImagePainted = !mPaintTime.IsNull();
     mPaintTime = TimeStamp();
   }
 
+  void NotifyCompositeInternal(const ImageCompositeNotification& aNotification) {}
+
   nsRefPtr<Image> mActiveImage;
   // Updates every time mActiveImage changes
   uint32_t mGenerationCounter;
 
   // Number of contained images that have been painted at least once.  It's up
   // to the ImageContainer implementation to ensure accesses to this are
   // threadsafe.
   uint32_t mPaintCount;
@@ -518,16 +533,20 @@ private:
   // sucessfully created with ENABLE_ASYNC, or points to null otherwise.
   // 'unsuccessful' in this case only means that the ImageClient could not
   // be created, most likely because off-main-thread compositing is not enabled.
   // In this case the ImageContainer is perfectly usable, but it will forward
   // frames to the compositor through transactions in the main thread rather than
   // asynchronusly using the ImageBridge IPDL protocol.
   ImageClient* mImageClient;
 
+  // Object must be released on the ImageBridge thread. Field is immutable
+  // after creation of the ImageContainer.
+  ImageContainerChild* mIPDLChild;
+
   static mozilla::Atomic<uint32_t> sGenerationCounter;
 };
 
 class AutoLockImage
 {
 public:
   explicit AutoLockImage(ImageContainer *aContainer)
   {
--- a/gfx/layers/client/ClientLayerManager.cpp
+++ b/gfx/layers/client/ClientLayerManager.cpp
@@ -15,16 +15,17 @@
 #include "mozilla/layers/CompositorChild.h" // for CompositorChild
 #include "mozilla/layers/ContentClient.h"
 #include "mozilla/layers/FrameUniformityData.h"
 #include "mozilla/layers/ISurfaceAllocator.h"
 #include "mozilla/layers/LayersMessages.h"  // for EditReply, etc
 #include "mozilla/layers/LayersSurfaces.h"  // for SurfaceDescriptor
 #include "mozilla/layers/PLayerChild.h"  // for PLayerChild
 #include "mozilla/layers/LayerTransactionChild.h"
+#include "mozilla/layers/ShadowLayerChild.h"
 #include "mozilla/layers/TextureClientPool.h" // for TextureClientPool
 #include "mozilla/layers/PersistentBufferProvider.h"
 #include "ClientReadbackLayer.h"        // for ClientReadbackLayer
 #include "nsAString.h"
 #include "nsIWidgetListener.h"
 #include "nsTArray.h"                   // for AutoInfallibleTArray
 #include "nsXULAppAPI.h"                // for XRE_GetProcessType, etc
 #include "TiledLayerBuffer.h"
@@ -596,17 +597,16 @@ ClientLayerManager::ForwardTransaction(b
         ContentClientRemote* contentClient =
           static_cast<ContentClientRemote*>(compositable);
         MOZ_ASSERT(contentClient);
 
         contentClient->SwapBuffers(obs.frontUpdatedRegion());
 
         break;
       }
-
       default:
         NS_RUNTIMEABORT("not reached");
       }
     }
 
     if (sent) {
       mNeedsComposite = false;
     }
--- a/gfx/layers/client/ClientLayerManager.h
+++ b/gfx/layers/client/ClientLayerManager.h
@@ -200,17 +200,17 @@ public:
   }
   bool NeedsComposite() const { return mNeedsComposite; }
 
   virtual void Composite() override;
   virtual void GetFrameUniformity(FrameUniformityData* aFrameUniformityData) override;
   virtual bool RequestOverfill(mozilla::dom::OverfillCallback* aCallback) override;
   virtual void RunOverfillCallback(const uint32_t aOverfill) override;
 
-  virtual void DidComposite(uint64_t aTransactionId);
+  void DidComposite(uint64_t aTransactionId);
 
   virtual bool SupportsMixBlendModes(EnumSet<gfx::CompositionOp>& aMixBlendModes) override
   {
    return (GetTextureFactoryIdentifier().mSupportedBlendModes & aMixBlendModes) == aMixBlendModes;
   }
 
   virtual bool AreComponentAlphaLayersEnabled() override;
 
--- a/gfx/layers/client/CompositableClient.cpp
+++ b/gfx/layers/client/CompositableClient.cpp
@@ -153,22 +153,22 @@ CompositableClient::SetIPDLActor(Composi
 
 PCompositableChild*
 CompositableClient::GetIPDLActor() const
 {
   return mCompositableChild;
 }
 
 bool
-CompositableClient::Connect()
+CompositableClient::Connect(ImageContainer* aImageContainer)
 {
   if (!GetForwarder() || GetIPDLActor()) {
     return false;
   }
-  GetForwarder()->Connect(this);
+  GetForwarder()->Connect(this, aImageContainer);
   return true;
 }
 
 void
 CompositableClient::Destroy()
 {
   mDestroyed = true;
 
--- a/gfx/layers/client/CompositableClient.h
+++ b/gfx/layers/client/CompositableClient.h
@@ -20,16 +20,17 @@
 #include "nsISupportsImpl.h"            // for MOZ_COUNT_CTOR, etc
 
 namespace mozilla {
 namespace layers {
 
 class CompositableClient;
 class BufferTextureClient;
 class ImageBridgeChild;
+class ImageContainer;
 class CompositableForwarder;
 class CompositableChild;
 class PCompositableChild;
 
 /**
  * Handle RemoveTextureFromCompositableAsync() transaction.
  */
 class RemoveTextureFromCompositableTracker : public AsyncTransactionTracker {
@@ -141,17 +142,17 @@ public:
                                 gfx::IntSize aSize,
                                 gfx::BackendType aMoz2DBackend,
                                 TextureFlags aTextureFlags,
                                 TextureAllocationFlags aAllocFlags = ALLOC_DEFAULT);
 
   /**
    * Establishes the connection with compositor side through IPDL
    */
-  virtual bool Connect();
+  virtual bool Connect(ImageContainer* aImageContainer = nullptr);
 
   void Destroy();
 
   bool IsDestroyed() { return mDestroyed; }
 
   PCompositableChild* GetIPDLActor() const;
 
   // should only be called by a CompositableForwarder
--- a/gfx/layers/client/ImageClient.cpp
+++ b/gfx/layers/client/ImageClient.cpp
@@ -244,16 +244,18 @@ ImageClientSingle::UpdateImage(ImageCont
       return false;
     }
 
 
     CompositableForwarder::TimedTextureClient* t = textures.AppendElement();
     t->mTextureClient = texture;
     t->mTimeStamp = img.mTimeStamp;
     t->mPictureRect = image->GetPictureRect();
+    t->mFrameID = img.mFrameID;
+    t->mProducerID = img.mProducerID;
 
     Buffer* newBuf = newBuffers.AppendElement();
     newBuf->mImageSerial = image->GetSerial();
     newBuf->mTextureClient = texture;
 
     aContainer->NotifyPaintedImage(image);
     texture->SyncWithObject(GetForwarder()->GetSyncObject());
   }
--- a/gfx/layers/client/ImageClient.h
+++ b/gfx/layers/client/ImageClient.h
@@ -120,17 +120,17 @@ protected:
  */
 class ImageClientBridge : public ImageClient
 {
 public:
   ImageClientBridge(CompositableForwarder* aFwd,
                     TextureFlags aFlags);
 
   virtual bool UpdateImage(ImageContainer* aContainer, uint32_t aContentFlags) override;
-  virtual bool Connect() override { return false; }
+  virtual bool Connect(ImageContainer* aImageContainer) override { return false; }
 
   virtual TextureInfo GetTextureInfo() const override
   {
     return TextureInfo(mType);
   }
 
   virtual void SetIPDLActor(CompositableChild* aChild) override
   {
--- a/gfx/layers/composite/CanvasLayerComposite.cpp
+++ b/gfx/layers/composite/CanvasLayerComposite.cpp
@@ -92,17 +92,17 @@ CanvasLayerComposite::RenderLayer(const 
   if (gfxUtils::sDumpPainting) {
     RefPtr<gfx::DataSourceSurface> surf = mCompositableHost->GetAsSurface();
     WriteSnapshotToDumpFile(this, surf);
   }
 #endif
 
   RenderWithAllMasks(this, mCompositor, aClipRect,
                      [&](EffectChain& effectChain, const Rect& clipRect) {
-    mCompositableHost->Composite(effectChain,
+    mCompositableHost->Composite(this, effectChain,
                           GetEffectiveOpacity(),
                           GetEffectiveTransform(),
                           GetEffectFilter(),
                           clipRect);
   });
 
   mCompositableHost->BumpFlashCounter();
 }
--- a/gfx/layers/composite/CompositableHost.cpp
+++ b/gfx/layers/composite/CompositableHost.cpp
@@ -6,16 +6,17 @@
 #include "CompositableHost.h"
 #include <map>                          // for _Rb_tree_iterator, map, etc
 #include <utility>                      // for pair
 #include "ContentHost.h"                // for ContentHostDoubleBuffered, etc
 #include "Effects.h"                    // for EffectMask, Effect, etc
 #include "gfxUtils.h"
 #include "ImageHost.h"                  // for ImageHostBuffered, etc
 #include "TiledContentHost.h"           // for TiledContentHost
+#include "mozilla/layers/ImageContainerParent.h"
 #include "mozilla/layers/LayersSurfaces.h"  // for SurfaceDescriptor
 #include "mozilla/layers/TextureHost.h"  // for TextureHost, etc
 #include "nsRefPtr.h"                   // for nsRefPtr
 #include "nsDebug.h"                    // for NS_WARNING
 #include "nsISupportsImpl.h"            // for MOZ_COUNT_CTOR, etc
 #include "gfxPlatform.h"                // for gfxPlatform
 #include "mozilla/layers/PCompositableParent.h"
 
@@ -28,31 +29,36 @@ namespace layers {
 class Compositor;
 
 /**
  * IPDL actor used by CompositableHost to match with its corresponding
  * CompositableClient on the content side.
  *
  * CompositableParent is owned by the IPDL system. It's deletion is triggered
  * by either the CompositableChild's deletion, or by the IPDL communication
- * goind down.
+ * going down.
  */
 class CompositableParent : public PCompositableParent
 {
 public:
   CompositableParent(CompositableParentManager* aMgr,
                      const TextureInfo& aTextureInfo,
-                     uint64_t aID = 0)
+                     uint64_t aID = 0,
+                     PImageContainerParent* aImageContainer = nullptr)
   {
     MOZ_COUNT_CTOR(CompositableParent);
     mHost = CompositableHost::Create(aTextureInfo);
     mHost->SetAsyncID(aID);
     if (aID) {
       CompositableMap::Set(aID, this);
     }
+    if (aImageContainer) {
+      mHost->SetImageContainer(
+          static_cast<ImageContainerParent*>(aImageContainer));
+    }
   }
 
   ~CompositableParent()
   {
     MOZ_COUNT_DTOR(CompositableParent);
     CompositableMap::Erase(mHost->GetAsyncID());
   }
 
@@ -82,19 +88,20 @@ CompositableHost::CompositableHost(const
 CompositableHost::~CompositableHost()
 {
   MOZ_COUNT_DTOR(CompositableHost);
 }
 
 PCompositableParent*
 CompositableHost::CreateIPDLActor(CompositableParentManager* aMgr,
                                   const TextureInfo& aTextureInfo,
-                                  uint64_t aID)
+                                  uint64_t aID,
+                                  PImageContainerParent* aImageContainer)
 {
-  return new CompositableParent(aMgr, aTextureInfo, aID);
+  return new CompositableParent(aMgr, aTextureInfo, aID, aImageContainer);
 }
 
 bool
 CompositableHost::DestroyIPDLActor(PCompositableParent* aActor)
 {
   delete aActor;
   return true;
 }
--- a/gfx/layers/composite/CompositableHost.h
+++ b/gfx/layers/composite/CompositableHost.h
@@ -32,17 +32,19 @@ namespace mozilla {
 namespace gfx {
 class Matrix4x4;
 class DataSourceSurface;
 }
 
 namespace layers {
 
 class Layer;
+class LayerComposite;
 class Compositor;
+class ImageContainerParent;
 class ThebesBufferData;
 class TiledContentHost;
 class CompositableParentManager;
 class PCompositableParent;
 struct EffectChain;
 
 /**
  * The compositor-side counterpart to CompositableClient. Responsible for
@@ -70,17 +72,18 @@ public:
   static already_AddRefed<CompositableHost> Create(const TextureInfo& aTextureInfo);
 
   virtual CompositableType GetType() = 0;
 
   // If base class overrides, it should still call the parent implementation
   virtual void SetCompositor(Compositor* aCompositor);
 
   // composite the contents of this buffer host to the compositor's surface
-  virtual void Composite(EffectChain& aEffectChain,
+  virtual void Composite(LayerComposite* aLayer,
+                         EffectChain& aEffectChain,
                          float aOpacity,
                          const gfx::Matrix4x4& aTransform,
                          const gfx::Filter& aFilter,
                          const gfx::Rect& aClipRect,
                          const nsIntRegion* aVisibleRegion = nullptr) = 0;
 
   /**
    * Update the content host.
@@ -126,16 +129,18 @@ public:
   Compositor* GetCompositor() const
   {
     return mCompositor;
   }
 
   Layer* GetLayer() const { return mLayer; }
   void SetLayer(Layer* aLayer) { mLayer = aLayer; }
 
+  virtual void SetImageContainer(ImageContainerParent* aImageContainer) {}
+
   virtual TiledContentHost* AsTiledContentHost() { return nullptr; }
 
   typedef uint32_t AttachFlags;
   static const AttachFlags NO_FLAGS = 0;
   static const AttachFlags ALLOW_REATTACH = 1;
   static const AttachFlags KEEP_ATTACHED = 2;
   static const AttachFlags FORCE_DETACH = 2;
 
@@ -179,16 +184,18 @@ public:
   virtual already_AddRefed<gfx::DataSourceSurface> GetAsSurface() { return nullptr; }
 
   virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix) = 0;
 
   struct TimedTexture {
     RefPtr<TextureHost> mTexture;
     TimeStamp mTimeStamp;
     gfx::IntRect mPictureRect;
+    int32_t mFrameID;
+    int32_t mProducerID;
   };
   virtual void UseTextureHost(const nsTArray<TimedTexture>& aTextures);
   virtual void UseComponentAlphaTextures(TextureHost* aTextureOnBlack,
                                          TextureHost* aTextureOnWhite);
   virtual void UseOverlaySource(OverlaySource aOverlay,
                                 const gfx::IntRect& aPictureRect) { }
 
   virtual void RemoveTextureHost(TextureHost* aTexture);
@@ -197,17 +204,18 @@ public:
   void BumpFlashCounter() {
     mFlashCounter = mFlashCounter >= DIAGNOSTIC_FLASH_COUNTER_MAX
                   ? DIAGNOSTIC_FLASH_COUNTER_MAX : mFlashCounter + 1;
   }
 
   static PCompositableParent*
   CreateIPDLActor(CompositableParentManager* mgr,
                   const TextureInfo& textureInfo,
-                  uint64_t asyncID);
+                  uint64_t asyncID,
+                  PImageContainerParent* aImageContainer = nullptr);
 
   static bool DestroyIPDLActor(PCompositableParent* actor);
 
   static CompositableHost* FromIPDLActor(PCompositableParent* actor);
 
   uint64_t GetCompositorID() const { return mCompositorID; }
 
   uint64_t GetAsyncID() const { return mAsyncID; }
@@ -265,17 +273,17 @@ private:
  * different top level protocols. In this case they don't share the same
  * communication channel and we can't send an OpAttachCompositable (PCompositable,
  * PLayer) message.
  *
  * In order to attach a layer and the right compositable if the the compositable
  * is async, we store references to the async compositables in a CompositableMap
  * that is accessed only on the compositor thread. During a layer transaction we
  * send the message OpAttachAsyncCompositable(ID, PLayer), and on the compositor
- * side we lookup the ID in the map and attach the correspondig compositable to
+ * side we lookup the ID in the map and attach the corresponding compositable to
  * the layer.
  *
  * CompositableMap must be global because the image bridge doesn't have any
  * reference to whatever we have created with PLayerTransaction. So, the only way to
  * actually connect these two worlds is to have something global that they can
  * both query (in the same  thread). The map is not allocated the map on the 
  * stack to avoid the badness of static initialization.
  *
--- a/gfx/layers/composite/ContentHost.cpp
+++ b/gfx/layers/composite/ContentHost.cpp
@@ -30,17 +30,18 @@ ContentHostBase::ContentHostBase(const T
   , mInitialised(false)
 {}
 
 ContentHostBase::~ContentHostBase()
 {
 }
 
 void
-ContentHostTexture::Composite(EffectChain& aEffectChain,
+ContentHostTexture::Composite(LayerComposite* aLayer,
+                              EffectChain& aEffectChain,
                               float aOpacity,
                               const gfx::Matrix4x4& aTransform,
                               const Filter& aFilter,
                               const Rect& aClipRect,
                               const nsIntRegion* aVisibleRegion)
 {
   NS_ASSERTION(aVisibleRegion, "Requires a visible region");
 
--- a/gfx/layers/composite/ContentHost.h
+++ b/gfx/layers/composite/ContentHost.h
@@ -109,17 +109,18 @@ protected:
 class ContentHostTexture : public ContentHostBase
 {
 public:
   explicit ContentHostTexture(const TextureInfo& aTextureInfo)
     : ContentHostBase(aTextureInfo)
     , mLocked(false)
   { }
 
-  virtual void Composite(EffectChain& aEffectChain,
+  virtual void Composite(LayerComposite* aLayer,
+                         EffectChain& aEffectChain,
                          float aOpacity,
                          const gfx::Matrix4x4& aTransform,
                          const gfx::Filter& aFilter,
                          const gfx::Rect& aClipRect,
                          const nsIntRegion* aVisibleRegion = nullptr) override;
 
   virtual void SetCompositor(Compositor* aCompositor) override;
 
--- a/gfx/layers/composite/ImageHost.cpp
+++ b/gfx/layers/composite/ImageHost.cpp
@@ -1,19 +1,22 @@
 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "ImageHost.h"
+
 #include "LayersLogging.h"              // for AppendToString
 #include "composite/CompositableHost.h"  // for CompositableHost, etc
 #include "ipc/IPCMessageUtils.h"        // for null_t
 #include "mozilla/layers/Compositor.h"  // for Compositor
 #include "mozilla/layers/Effects.h"     // for TexturedEffect, Effect, etc
+#include "mozilla/layers/ImageContainerParent.h"
+#include "mozilla/layers/LayerManagerComposite.h"     // for TexturedEffect, Effect, etc
 #include "nsAString.h"
 #include "nsDebug.h"                    // for NS_WARNING, NS_ASSERTION
 #include "nsPrintfCString.h"            // for nsPrintfCString
 #include "nsString.h"                   // for nsAutoCString
 
 class nsIntRegion;
 
 namespace mozilla {
@@ -24,21 +27,26 @@ class Matrix4x4;
 using namespace gfx;
 
 namespace layers {
 
 class ISurfaceAllocator;
 
 ImageHost::ImageHost(const TextureInfo& aTextureInfo)
   : CompositableHost(aTextureInfo)
+  , mImageContainer(nullptr)
+  , mLastFrameID(-1)
+  , mLastProducerID(-1)
   , mLocked(false)
 {}
 
 ImageHost::~ImageHost()
-{}
+{
+  SetImageContainer(nullptr);
+}
 
 void
 ImageHost::UseTextureHost(const nsTArray<TimedTexture>& aTextures)
 {
   MOZ_ASSERT(!mLocked);
 
   CompositableHost::UseTextureHost(aTextures);
   MOZ_ASSERT(aTextures.Length() >= 1);
@@ -49,29 +57,40 @@ ImageHost::UseTextureHost(const nsTArray
   for (int32_t i = mImages.Length() - 1; i >= 0; --i) {
     if (!mImages[i].mTextureSource) {
       mImages.RemoveElementAt(i);
     }
   }
 
   // Create new TimedImage entries and recycle any mTextureSources that match
   // our mFrontBuffers.
-  for (auto& t : aTextures) {
+  for (uint32_t i = 0; i < aTextures.Length(); ++i) {
+    const TimedTexture& t = aTextures[i];
+    MOZ_ASSERT(t.mTexture);
+    if (i + 1 < aTextures.Length() &&
+        t.mProducerID == mLastProducerID && t.mFrameID < mLastFrameID) {
+      // Ignore frames before a frame that we already composited. We don't
+      // ever want to display these frames. This could be important if
+      // the frame producer adjusts timestamps (e.g. to track the audio clock)
+      // and the new frame times are earlier.
+      continue;
+    }
     TimedImage& img = *newImages.AppendElement();
-    MOZ_ASSERT(t.mTexture);
     img.mFrontBuffer = t.mTexture;
     for (uint32_t i = 0; i < mImages.Length(); ++i) {
       if (mImages[i].mFrontBuffer == img.mFrontBuffer) {
         img.mTextureSource = mImages[i].mTextureSource;
         mImages.RemoveElementAt(i);
         break;
       }
     }
     img.mTimeStamp = t.mTimeStamp;
     img.mPictureRect = t.mPictureRect;
+    img.mFrameID = t.mFrameID;
+    img.mProducerID = t.mProducerID;
   }
   // Recycle any leftover mTextureSources and call PrepareTextureSource on all
   // images.
   for (auto& img : newImages) {
     if (!img.mTextureSource && !mImages.IsEmpty()) {
       img.mTextureSource = mImages.LastElement().mTextureSource;
       mImages.RemoveElementAt(mImages.Length() - 1);
     }
@@ -102,18 +121,20 @@ int ImageHost::ChooseImageIndex() const
     return -1;
   }
   TimeStamp now = GetCompositor()->GetCompositionTime();
 
   if (now.IsNull()) {
     // Not in a composition, so just return the last image we composited
     // (if it's one of the current images).
     for (uint32_t i = 0; i < mImages.Length(); ++i) {
-      // For now there's only one image so we'll cheat until we can do better.
-      return i;
+      if (mImages[i].mFrameID == mLastFrameID &&
+          mImages[i].mProducerID == mLastProducerID) {
+        return i;
+      }
     }
     return -1;
   }
 
   uint32_t result = 0;
   while (result + 1 < mImages.Length() &&
       mImages[result + 1].mTimeStamp <= now) {
     ++result;
@@ -153,17 +174,18 @@ void ImageHost::Attach(Layer* aLayer,
       img.mFrontBuffer->SetCompositor(GetCompositor());
     }
     img.mFrontBuffer->Updated();
     img.mFrontBuffer->PrepareTextureSource(img.mTextureSource);
   }
 }
 
 void
-ImageHost::Composite(EffectChain& aEffectChain,
+ImageHost::Composite(LayerComposite* aLayer,
+                     EffectChain& aEffectChain,
                      float aOpacity,
                      const gfx::Matrix4x4& aTransform,
                      const gfx::Filter& aFilter,
                      const gfx::Rect& aClipRect,
                      const nsIntRegion* aVisibleRegion)
 {
   if (!GetCompositor()) {
     // should only happen when a tab is dragged to another window and
@@ -205,16 +227,27 @@ ImageHost::Composite(EffectChain& aEffec
   RefPtr<TexturedEffect> effect =
       CreateTexturedEffect(img->mFrontBuffer->GetFormat(),
           img->mTextureSource.get(), aFilter, isAlphaPremultiplied,
           GetRenderState());
   if (!effect) {
     return;
   }
 
+  if (mLastFrameID != img->mFrameID || mLastProducerID != img->mProducerID) {
+    if (mImageContainer) {
+      aLayer->GetLayerManager()->
+          AppendImageCompositeNotification(ImageCompositeNotification(
+              mImageContainer, nullptr,
+              img->mTimeStamp, GetCompositor()->GetCompositionTime(),
+              img->mFrameID, img->mProducerID));
+    }
+    mLastFrameID = img->mFrameID;
+    mLastProducerID = img->mProducerID;
+  }
   aEffectChain.mPrimaryEffect = effect;
   gfx::Rect pictureRect(0, 0, img->mPictureRect.width, img->mPictureRect.height);
   BigImageIterator* it = img->mTextureSource->AsBigImageIterator();
   if (it) {
 
     // This iteration does not work if we have multiple texture sources here
     // (e.g. 3 YCbCr textures). There's nothing preventing the different
     // planes from having different resolutions or tile sizes. For example, a
@@ -386,28 +419,41 @@ ImageHost::GenEffect(const gfx::Filter& 
 
   return CreateTexturedEffect(img->mFrontBuffer->GetFormat(),
                               img->mTextureSource,
                               aFilter,
                               isAlphaPremultiplied,
                               GetRenderState());
 }
 
+void
+ImageHost::SetImageContainer(ImageContainerParent* aImageContainer)
+{
+  if (mImageContainer) {
+    mImageContainer->mImageHosts.RemoveElement(this);
+  }
+  mImageContainer = aImageContainer;
+  if (mImageContainer) {
+    mImageContainer->mImageHosts.AppendElement(this);
+  }
+}
+
 #ifdef MOZ_WIDGET_GONK
 ImageHostOverlay::ImageHostOverlay(const TextureInfo& aTextureInfo)
   : CompositableHost(aTextureInfo)
 {
 }
 
 ImageHostOverlay::~ImageHostOverlay()
 {
 }
 
 void
-ImageHostOverlay::Composite(EffectChain& aEffectChain,
+ImageHostOverlay::Composite(LayerComposite* aLayer,
+                            EffectChain& aEffectChain,
                             float aOpacity,
                             const gfx::Matrix4x4& aTransform,
                             const gfx::Filter& aFilter,
                             const gfx::Rect& aClipRect,
                             const nsIntRegion* aVisibleRegion)
 {
   if (!GetCompositor()) {
     return;
--- a/gfx/layers/composite/ImageHost.h
+++ b/gfx/layers/composite/ImageHost.h
@@ -27,29 +27,31 @@ class nsIntRegion;
 namespace mozilla {
 namespace gfx {
 class Matrix4x4;
 }
 namespace layers {
 
 class Compositor;
 struct EffectChain;
+class ImageContainerParent;
 
 /**
  * ImageHost. Works with ImageClientSingle and ImageClientBuffered
  */
 class ImageHost : public CompositableHost
 {
 public:
   explicit ImageHost(const TextureInfo& aTextureInfo);
   ~ImageHost();
 
   virtual CompositableType GetType() override { return mTextureInfo.mCompositableType; }
 
-  virtual void Composite(EffectChain& aEffectChain,
+  virtual void Composite(LayerComposite* aLayer,
+                         EffectChain& aEffectChain,
                          float aOpacity,
                          const gfx::Matrix4x4& aTransform,
                          const gfx::Filter& aFilter,
                          const gfx::Rect& aClipRect,
                          const nsIntRegion* aVisibleRegion = nullptr) override;
 
   virtual void UseTextureHost(const nsTArray<TimedTexture>& aTextures) override;
 
@@ -58,16 +60,18 @@ public:
   virtual TextureHost* GetAsTextureHost(gfx::IntRect* aPictureRect = nullptr) override;
 
   virtual void Attach(Layer* aLayer,
                       Compositor* aCompositor,
                       AttachFlags aFlags = NO_FLAGS) override;
 
   virtual void SetCompositor(Compositor* aCompositor) override;
 
+  virtual void SetImageContainer(ImageContainerParent* aImageContainer) override;
+
   gfx::IntSize GetImageSize() const override;
 
   virtual LayerRenderState GetRenderState() override;
 
   virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix) override;
 
   virtual void Dump(std::stringstream& aStream,
                     const char* aPrefix = "",
@@ -82,45 +86,52 @@ public:
   virtual already_AddRefed<TexturedEffect> GenEffect(const gfx::Filter& aFilter) override;
 
 protected:
   struct TimedImage {
     CompositableTextureHostRef mFrontBuffer;
     CompositableTextureSourceRef mTextureSource;
     TimeStamp mTimeStamp;
     gfx::IntRect mPictureRect;
+    int32_t mFrameID;
+    int32_t mProducerID;
   };
 
   /**
    * ChooseImage is guaranteed to return the same TimedImage every time it's
    * called during the same composition --- it depends only on mImages and
    * mCompositor->GetCompositionTime().
    */
   const TimedImage* ChooseImage() const;
   TimedImage* ChooseImage();
   int ChooseImageIndex() const;
 
   nsTArray<TimedImage> mImages;
+  // Weak reference, will be null if mImageContainer has been destroyed.
+  ImageContainerParent* mImageContainer;
+  int32_t mLastFrameID;
+  int32_t mLastProducerID;
 
   bool mLocked;
 };
 
 #ifdef MOZ_WIDGET_GONK
 
 /**
  * ImageHostOverlay works with ImageClientOverlay
  */
 class ImageHostOverlay : public CompositableHost {
 public:
   ImageHostOverlay(const TextureInfo& aTextureInfo);
   ~ImageHostOverlay();
 
   virtual CompositableType GetType() { return mTextureInfo.mCompositableType; }
 
-  virtual void Composite(EffectChain& aEffectChain,
+  virtual void Composite(LayerComposite* aLayer,
+                         EffectChain& aEffectChain,
                          float aOpacity,
                          const gfx::Matrix4x4& aTransform,
                          const gfx::Filter& aFilter,
                          const gfx::Rect& aClipRect,
                          const nsIntRegion* aVisibleRegion = nullptr) override;
   virtual LayerRenderState GetRenderState() override;
   virtual void UseOverlaySource(OverlaySource aOverlay,
                                 const gfx::IntRect& aPictureRect) override;
--- a/gfx/layers/composite/ImageLayerComposite.cpp
+++ b/gfx/layers/composite/ImageLayerComposite.cpp
@@ -93,17 +93,17 @@ ImageLayerComposite::RenderLayer(const I
   }
 #endif
 
   mCompositor->MakeCurrent();
 
   RenderWithAllMasks(this, mCompositor, aClipRect,
                      [&](EffectChain& effectChain, const Rect& clipRect) {
     mImageHost->SetCompositor(mCompositor);
-    mImageHost->Composite(effectChain,
+    mImageHost->Composite(this, effectChain,
                           GetEffectiveOpacity(),
                           GetEffectiveTransformForBuffer(),
                           GetEffectFilter(),
                           clipRect);
   });
   mImageHost->BumpFlashCounter();
 }
 
--- a/gfx/layers/composite/LayerManagerComposite.h
+++ b/gfx/layers/composite/LayerManagerComposite.h
@@ -14,16 +14,17 @@
 #include "mozilla/Attributes.h"         // for override
 #include "mozilla/RefPtr.h"             // for RefPtr, already_AddRefed
 #include "mozilla/gfx/2D.h"
 #include "mozilla/gfx/Point.h"          // for IntSize
 #include "mozilla/gfx/Rect.h"           // for Rect
 #include "mozilla/gfx/Types.h"          // for SurfaceFormat
 #include "mozilla/layers/CompositorTypes.h"
 #include "mozilla/layers/Effects.h"     // for EffectChain
+#include "mozilla/layers/LayersMessages.h"
 #include "mozilla/layers/LayersTypes.h"  // for LayersBackend, etc
 #include "mozilla/Maybe.h"              // for Maybe
 #include "mozilla/RefPtr.h"
 #include "mozilla/UniquePtr.h"
 #include "nsAString.h"
 #include "nsRefPtr.h"                   // for nsRefPtr
 #include "nsCOMPtr.h"                   // for already_AddRefed
 #include "nsDebug.h"                    // for NS_ASSERTION
@@ -256,16 +257,25 @@ public:
   void UnusedApzTransformWarning() {
     mUnusedApzTransformWarning = true;
   }
 
   bool LastFrameMissedHWC() { return mLastFrameMissedHWC; }
 
   bool AsyncPanZoomEnabled() const override;
 
+  void AppendImageCompositeNotification(const ImageCompositeNotification& aNotification)
+  {
+    mImageCompositeNotifications.AppendElement(aNotification);
+  }
+  void ExtractImageCompositeNotifications(nsTArray<ImageCompositeNotification>* aNotifications)
+  {
+    aNotifications->MoveElementsFrom(mImageCompositeNotifications);
+  }
+
 private:
   /** Region we're clipping our current drawing to. */
   nsIntRegion mClippingRegion;
   gfx::IntRect mRenderBounds;
 
   /** Current root layer. */
   LayerComposite* RootLayer() const;
 
@@ -302,16 +312,18 @@ private:
                                float aContrastEffect);
 
   float mWarningLevel;
   mozilla::TimeStamp mWarnTime;
   bool mUnusedApzTransformWarning;
   RefPtr<Compositor> mCompositor;
   UniquePtr<LayerProperties> mClonedLayerTreeProperties;
 
+  nsTArray<ImageCompositeNotification> mImageCompositeNotifications;
+
   /**
    * Context target, nullptr when drawing directly to our swap chain.
    */
   RefPtr<gfx::DrawTarget> mTarget;
   gfx::IntRect mTargetBounds;
 
   nsIntRegion mInvalidRegion;
   UniquePtr<FPSState> mFPS;
@@ -363,16 +375,18 @@ public:
    * concrete class destructor
    */
   virtual void Destroy();
 
   virtual Layer* GetLayer() = 0;
 
   virtual void SetLayerManager(LayerManagerComposite* aManager);
 
+  LayerManagerComposite* GetLayerManager() const { return mCompositeManager; }
+
   /**
    * Perform a first pass over the layer tree to render all of the intermediate
    * surfaces that we can. This allows us to avoid framebuffer switches in the
    * middle of our render which is inefficient especially on mobile GPUs. This
    * must be called before RenderLayer.
    */
   virtual void Prepare(const RenderTargetIntRect& aClipRect) {}
 
--- a/gfx/layers/composite/PaintedLayerComposite.cpp
+++ b/gfx/layers/composite/PaintedLayerComposite.cpp
@@ -124,17 +124,17 @@ PaintedLayerComposite::RenderLayer(const
   }
 #endif
 
 
   RenderWithAllMasks(this, compositor, aClipRect,
                      [&](EffectChain& effectChain, const Rect& clipRect) {
     mBuffer->SetPaintWillResample(MayResample());
 
-    mBuffer->Composite(effectChain,
+    mBuffer->Composite(this, effectChain,
                        GetEffectiveOpacity(),
                        GetEffectiveTransform(),
                        GetEffectFilter(),
                        clipRect,
                        &visibleRegion);
   });
 
   mBuffer->BumpFlashCounter();
--- a/gfx/layers/composite/TiledContentHost.cpp
+++ b/gfx/layers/composite/TiledContentHost.cpp
@@ -357,17 +357,18 @@ TiledLayerBufferComposite::Clear()
   mTiles.mFirst = TileIntPoint();
   mTiles.mSize = TileIntSize();
   mValidRegion = nsIntRegion();
   mPaintedRegion = nsIntRegion();
   mResolution = 1.0;
 }
 
 void
-TiledContentHost::Composite(EffectChain& aEffectChain,
+TiledContentHost::Composite(LayerComposite* aLayer,
+                            EffectChain& aEffectChain,
                             float aOpacity,
                             const gfx::Matrix4x4& aTransform,
                             const gfx::Filter& aFilter,
                             const gfx::Rect& aClipRect,
                             const nsIntRegion* aVisibleRegion /* = nullptr */)
 {
   MOZ_ASSERT(mCompositor);
   // Reduce the opacity of the low-precision buffer to make it a
--- a/gfx/layers/composite/TiledContentHost.h
+++ b/gfx/layers/composite/TiledContentHost.h
@@ -230,22 +230,23 @@ public:
     CompositableHost::SetCompositor(aCompositor);
     mTiledBuffer.SetCompositor(aCompositor);
     mLowPrecisionTiledBuffer.SetCompositor(aCompositor);
   }
 
   bool UseTiledLayerBuffer(ISurfaceAllocator* aAllocator,
                            const SurfaceDescriptorTiles& aTiledDescriptor);
 
-  void Composite(EffectChain& aEffectChain,
-                 float aOpacity,
-                 const gfx::Matrix4x4& aTransform,
-                 const gfx::Filter& aFilter,
-                 const gfx::Rect& aClipRect,
-                 const nsIntRegion* aVisibleRegion = nullptr) override;
+  virtual void Composite(LayerComposite* aLayer,
+                         EffectChain& aEffectChain,
+                         float aOpacity,
+                         const gfx::Matrix4x4& aTransform,
+                         const gfx::Filter& aFilter,
+                         const gfx::Rect& aClipRect,
+                         const nsIntRegion* aVisibleRegion = nullptr) override;
 
   virtual CompositableType GetType() override { return CompositableType::CONTENT_TILED; }
 
   virtual TiledContentHost* AsTiledContentHost() override { return this; }
 
   virtual void Attach(Layer* aLayer,
                       Compositor* aCompositor,
                       AttachFlags aFlags = NO_FLAGS) override;
--- a/gfx/layers/ipc/CompositableForwarder.h
+++ b/gfx/layers/ipc/CompositableForwarder.h
@@ -17,16 +17,17 @@
 #include "nsRegion.h"                   // for nsIntRegion
 #include "mozilla/gfx/Rect.h"
 
 namespace mozilla {
 namespace layers {
 
 class CompositableClient;
 class AsyncTransactionTracker;
+class ImageContainer;
 struct TextureFactoryIdentifier;
 class SurfaceDescriptor;
 class SurfaceDescriptorTiles;
 class ThebesBufferData;
 class PTextureChild;
 
 /**
  * A transaction is a set of changes that happenned on the content side, that
@@ -45,17 +46,18 @@ public:
   CompositableForwarder()
     : mSerial(++sSerialCounter)
   {}
 
   /**
    * Setup the IPDL actor for aCompositable to be part of layers
    * transactions.
    */
-  virtual void Connect(CompositableClient* aCompositable) = 0;
+  virtual void Connect(CompositableClient* aCompositable,
+                       ImageContainer* aImageContainer = nullptr) = 0;
 
   /**
    * Tell the CompositableHost on the compositor side what TiledLayerBuffer to
    * use for the next composition.
    */
   virtual void UseTiledLayerBuffer(CompositableClient* aCompositable,
                                    const SurfaceDescriptorTiles& aTiledDescriptor) = 0;
 
@@ -124,19 +126,24 @@ public:
    * This function needs to be called after a tansaction with Compositor.
    */
   virtual void RemoveTexturesIfNecessary()
   {
     mTexturesToRemove.Clear();
   }
 
   struct TimedTextureClient {
+    TimedTextureClient()
+        : mTextureClient(nullptr), mFrameID(0), mProducerID(0) {}
+
     TextureClient* mTextureClient;
     TimeStamp mTimeStamp;
     nsIntRect mPictureRect;
+    int32_t mFrameID;
+    int32_t mProducerID;
   };
   /**
    * Tell the CompositableHost on the compositor side what textures to use for
    * the next composition.
    */
   virtual void UseTextures(CompositableClient* aCompositable,
                            const nsTArray<TimedTextureClient>& aTextures) = 0;
   virtual void UseComponentAlphaTextures(CompositableClient* aCompositable,
--- a/gfx/layers/ipc/CompositableTransactionParent.cpp
+++ b/gfx/layers/ipc/CompositableTransactionParent.cpp
@@ -175,16 +175,18 @@ CompositableParentManager::ReceiveCompos
       nsAutoTArray<CompositableHost::TimedTexture,4> textures;
       for (auto& timedTexture : op.textures()) {
         CompositableHost::TimedTexture* t = textures.AppendElement();
         t->mTexture =
             TextureHost::AsTextureHost(timedTexture.textureParent());
         MOZ_ASSERT(t->mTexture);
         t->mTimeStamp = timedTexture.timeStamp();
         t->mPictureRect = timedTexture.picture();
+        t->mFrameID = timedTexture.frameID();
+        t->mProducerID = timedTexture.producerID();
         MOZ_ASSERT(ValidatePictureRect(t->mTexture->GetSize(), t->mPictureRect));
 
         MaybeFence maybeFence = timedTexture.fence();
         if (maybeFence.type() == MaybeFence::TFenceHandle) {
           FenceHandle fence = maybeFence.get_FenceHandle();
           if (fence.IsValid()) {
             t->mTexture->SetAcquireFenceHandle(fence);
           }
--- a/gfx/layers/ipc/CompositorParent.cpp
+++ b/gfx/layers/ipc/CompositorParent.cpp
@@ -33,16 +33,17 @@
 #include "mozilla/layers/APZThreadUtils.h"  // for APZCTreeManager
 #include "mozilla/layers/AsyncCompositionManager.h"
 #include "mozilla/layers/BasicCompositor.h"  // for BasicCompositor
 #include "mozilla/layers/Compositor.h"  // for Compositor
 #include "mozilla/layers/CompositorLRU.h"  // for CompositorLRU
 #include "mozilla/layers/CompositorOGL.h"  // for CompositorOGL
 #include "mozilla/layers/CompositorTypes.h"
 #include "mozilla/layers/FrameUniformityData.h"
+#include "mozilla/layers/ImageBridgeParent.h"
 #include "mozilla/layers/LayerManagerComposite.h"
 #include "mozilla/layers/LayersTypes.h"
 #include "mozilla/layers/PLayerTransactionParent.h"
 #include "mozilla/layers/ShadowLayersManager.h" // for ShadowLayersManager
 #include "mozilla/media/MediaSystemResourceService.h" // for MediaSystemResourceService
 #include "mozilla/mozalloc.h"           // for operator new, etc
 #include "mozilla/Telemetry.h"
 #ifdef MOZ_WIDGET_GTK
@@ -1837,16 +1838,23 @@ private:
 
 void
 CompositorParent::DidComposite()
 {
   if (mPendingTransaction) {
     unused << SendDidComposite(0, mPendingTransaction);
     mPendingTransaction = 0;
   }
+  if (mLayerManager) {
+    nsTArray<ImageCompositeNotification> notifications;
+    mLayerManager->ExtractImageCompositeNotifications(&notifications);
+    if (!notifications.IsEmpty()) {
+      unused << ImageBridgeParent::NotifyImageComposites(notifications);
+    }
+  }
 
   MonitorAutoLock lock(*sIndirectLayerTreesLock);
   for (LayerTreeMap::iterator it = sIndirectLayerTrees.begin();
        it != sIndirectLayerTrees.end(); it++) {
     LayerTreeState* lts = &it->second;
     if (lts->mParent == this && lts->mCrossProcessParent) {
       static_cast<CrossProcessCompositorParent*>(lts->mCrossProcessParent)->DidComposite(it->first);
     }
--- a/gfx/layers/ipc/ImageBridgeChild.cpp
+++ b/gfx/layers/ipc/ImageBridgeChild.cpp
@@ -24,16 +24,17 @@
 #include "mozilla/media/MediaSystemResourceManager.h" // for MediaSystemResourceManager
 #include "mozilla/media/MediaSystemResourceManagerChild.h" // for MediaSystemResourceManagerChild
 #include "mozilla/layers/CompositableClient.h"  // for CompositableChild, etc
 #include "mozilla/layers/CompositorParent.h" // for CompositorParent
 #include "mozilla/layers/ISurfaceAllocator.h"  // for ISurfaceAllocator
 #include "mozilla/layers/ImageClient.h"  // for ImageClient
 #include "mozilla/layers/LayersMessages.h"  // for CompositableOperation
 #include "mozilla/layers/PCompositableChild.h"  // for PCompositableChild
+#include "mozilla/layers/PImageContainerChild.h"
 #include "mozilla/layers/TextureClient.h"  // for TextureClient
 #include "mozilla/mozalloc.h"           // for operator new, etc
 #include "nsAutoPtr.h"                  // for nsRefPtr
 #include "nsISupportsImpl.h"            // for ImageContainer::AddRef, etc
 #include "nsTArray.h"                   // for nsAutoTArray, nsTArray, etc
 #include "nsTArrayForwardDeclare.h"     // for AutoInfallibleTArray
 #include "nsThreadUtils.h"              // for NS_IsMainThread
 #include "nsXULAppAPI.h"                // for XRE_GetIOMessageLoop
@@ -116,17 +117,18 @@ ImageBridgeChild::UseTextures(Compositab
   nsAutoTArray<TimedTexture,4> textures;
 
   for (auto& t : aTextures) {
     MOZ_ASSERT(t.mTextureClient);
     MOZ_ASSERT(t.mTextureClient->GetIPDLActor());
     FenceHandle fence = t.mTextureClient->GetAcquireFenceHandle();
     textures.AppendElement(TimedTexture(nullptr, t.mTextureClient->GetIPDLActor(),
                                         fence.IsValid() ? MaybeFence(fence) : MaybeFence(null_t()),
-                                        t.mTimeStamp, t.mPictureRect));
+                                        t.mTimeStamp, t.mPictureRect,
+                                        t.mFrameID, t.mProducerID));
   }
   mTxn->AddNoSwapEdit(OpUseTexture(nullptr, aCompositable->GetIPDLActor(),
                                    textures));
 }
 
 void
 ImageBridgeChild::UseComponentAlphaTextures(CompositableClient* aCompositable,
                                             TextureClient* aTextureOnBlack,
@@ -211,20 +213,22 @@ static void ImageBridgeShutdownStep2(Ree
   *aDone = true;
   aBarrier->NotifyAll();
 }
 
 // dispatched function
 static void CreateImageClientSync(RefPtr<ImageClient>* result,
                                   ReentrantMonitor* barrier,
                                   CompositableType aType,
+                                  ImageContainer* aImageContainer,
                                   bool *aDone)
 {
   ReentrantMonitorAutoEnter autoMon(*barrier);
-  *result = sImageBridgeChildSingleton->CreateImageClientNow(aType);
+  *result = sImageBridgeChildSingleton->CreateImageClientNow(
+      aType, aImageContainer);
   *aDone = true;
   barrier->NotifyAll();
 }
 
 static void ConnectImageBridge(ImageBridgeChild * child, ImageBridgeParent * parent)
 {
   MessageLoop *parentMsgLoop = parent->GetMessageLoop();
   ipc::MessageChannel *parentChannel = parent->GetIPCChannel();
@@ -251,29 +255,32 @@ ImageBridgeChild::~ImageBridgeChild()
 void
 ImageBridgeChild::MarkShutDown()
 {
   MOZ_ASSERT(!mShuttingDown);
   mShuttingDown = true;
 }
 
 void
-ImageBridgeChild::Connect(CompositableClient* aCompositable)
+ImageBridgeChild::Connect(CompositableClient* aCompositable,
+                          ImageContainer* aImageContainer)
 {
   MOZ_ASSERT(aCompositable);
   MOZ_ASSERT(!mShuttingDown);
   uint64_t id = 0;
   PCompositableChild* child =
-    SendPCompositableConstructor(aCompositable->GetTextureInfo(), &id);
+    SendPCompositableConstructor(aCompositable->GetTextureInfo(),
+                                 aImageContainer->GetPImageContainerChild(), &id);
   MOZ_ASSERT(child);
   aCompositable->InitIPDLActor(child, id);
 }
 
 PCompositableChild*
-ImageBridgeChild::AllocPCompositableChild(const TextureInfo& aInfo, uint64_t* aID)
+ImageBridgeChild::AllocPCompositableChild(const TextureInfo& aInfo,
+                                          PImageContainerChild* aChild, uint64_t* aID)
 {
   MOZ_ASSERT(!mShuttingDown);
   return CompositableClient::CreateIPDLActor();
 }
 
 bool
 ImageBridgeChild::DeallocPCompositableChild(PCompositableChild* aActor)
 {
@@ -320,43 +327,53 @@ ConnectImageBridgeInChildProcess(Transpo
       ->message_loop()->PostTask(FROM_HERE,
                                  NewRunnableFunction(NuwaMarkCurrentThread,
                                                      (void (*)(void *))nullptr,
                                                      (void *)nullptr));
   }
 #endif
 }
 
-static void ReleaseImageClientNow(ImageClient* aClient)
+static void ReleaseImageClientNow(ImageClient* aClient,
+                                  PImageContainerChild* aChild)
 {
   MOZ_ASSERT(InImageBridgeChildThread());
-  aClient->Release();
+  if (aClient) {
+    aClient->Release();
+  }
+  if (aChild) {
+    aChild->SendAsyncDelete();
+  }
 }
 
 // static
-void ImageBridgeChild::DispatchReleaseImageClient(ImageClient* aClient)
+void ImageBridgeChild::DispatchReleaseImageClient(ImageClient* aClient,
+                                                  PImageContainerChild* aChild)
 {
-  if (!aClient) {
+  if (!aClient && !aChild) {
     return;
   }
 
   if (!IsCreated()) {
-    // CompositableClient::Release should normally happen in the ImageBridgeChild
-    // thread because it usually generate some IPDL messages.
-    // However, if we take this branch it means that the ImageBridgeChild
-    // has already shut down, along with the CompositableChild, which means no
-    // message will be sent and it is safe to run this code from any thread.
-    MOZ_ASSERT(aClient->GetIPDLActor() == nullptr);
-    aClient->Release();
+    if (aClient) {
+      // CompositableClient::Release should normally happen in the ImageBridgeChild
+      // thread because it usually generate some IPDL messages.
+      // However, if we take this branch it means that the ImageBridgeChild
+      // has already shut down, along with the CompositableChild, which means no
+      // message will be sent and it is safe to run this code from any thread.
+      MOZ_ASSERT(aClient->GetIPDLActor() == nullptr);
+      aClient->Release();
+    }
+    delete aChild;
     return;
   }
 
   sImageBridgeChildSingleton->GetMessageLoop()->PostTask(
     FROM_HERE,
-    NewRunnableFunction(&ReleaseImageClientNow, aClient));
+    NewRunnableFunction(&ReleaseImageClientNow, aClient, aChild));
 }
 
 static void ReleaseTextureClientNow(TextureClient* aClient)
 {
   MOZ_ASSERT(InImageBridgeChildThread());
   RELEASE_MANUALLY(aClient);
 }
 
@@ -384,17 +401,16 @@ void ImageBridgeChild::DispatchReleaseTe
 }
 
 static void UpdateImageClientNow(ImageClient* aClient, ImageContainer* aContainer)
 {
   MOZ_ASSERT(aClient);
   MOZ_ASSERT(aContainer);
   sImageBridgeChildSingleton->BeginTransaction();
   aClient->UpdateImage(aContainer, Layer::CONTENT_OPAQUE);
-  aClient->OnTransaction();
   sImageBridgeChildSingleton->EndTransaction();
 }
 
 //static
 void ImageBridgeChild::DispatchImageClientUpdate(ImageClient* aClient,
                                                  ImageContainer* aContainer)
 {
   if (!aClient || !aContainer || !IsCreated()) {
@@ -417,17 +433,16 @@ static void FlushAllImagesSync(ImageClie
                                bool aExceptFront, AsyncTransactionWaiter* aWaiter)
 {
   MOZ_ASSERT(aClient);
   sImageBridgeChildSingleton->BeginTransaction();
   if (aContainer && !aExceptFront) {
     aContainer->ClearCurrentImage();
   }
   aClient->FlushAllImages(aExceptFront, aWaiter);
-  aClient->OnTransaction();
   sImageBridgeChildSingleton->EndTransaction();
   // This decrement is balanced by the increment in FlushAllImages.
   // If any AsyncTransactionTrackers were created by FlushAllImages and attached
   // to aWaiter, aWaiter will not complete until those trackers all complete.
   // Otherwise, aWaiter will be ready to complete now.
   aWaiter->DecrementWaitCount();
 }
 
@@ -643,45 +658,51 @@ void
 ImageBridgeChild::IdentifyCompositorTextureHost(const TextureFactoryIdentifier& aIdentifier)
 {
   if (sImageBridgeChildSingleton) {
     sImageBridgeChildSingleton->IdentifyTextureHost(aIdentifier);
   }
 }
 
 already_AddRefed<ImageClient>
-ImageBridgeChild::CreateImageClient(CompositableType aType)
+ImageBridgeChild::CreateImageClient(CompositableType aType,
+                                    ImageContainer* aImageContainer)
 {
   if (InImageBridgeChildThread()) {
-    return CreateImageClientNow(aType);
+    return CreateImageClientNow(aType, aImageContainer);
   }
   ReentrantMonitor barrier("CreateImageClient Lock");
   ReentrantMonitorAutoEnter autoMon(barrier);
   bool done = false;
 
   RefPtr<ImageClient> result = nullptr;
-  GetMessageLoop()->PostTask(FROM_HERE, NewRunnableFunction(&CreateImageClientSync,
-                                                            &result, &barrier, aType, &done));
+  GetMessageLoop()->PostTask(FROM_HERE,
+      NewRunnableFunction(&CreateImageClientSync, &result, &barrier, aType,
+                          aImageContainer, &done));
   // should stop the thread until the ImageClient has been created on
   // the other thread
   while (!done) {
     barrier.Wait();
   }
   return result.forget();
 }
 
 already_AddRefed<ImageClient>
-ImageBridgeChild::CreateImageClientNow(CompositableType aType)
+ImageBridgeChild::CreateImageClientNow(CompositableType aType,
+                                       ImageContainer* aImageContainer)
 {
   MOZ_ASSERT(!sImageBridgeChildSingleton->mShuttingDown);
+  if (aImageContainer) {
+    SendPImageContainerConstructor(aImageContainer->GetPImageContainerChild());
+  }
   RefPtr<ImageClient> client
     = ImageClient::CreateImageClient(aType, this, TextureFlags::NO_FLAGS);
   MOZ_ASSERT(client, "failed to create ImageClient");
   if (client) {
-    client->Connect();
+    client->Connect(aImageContainer);
   }
   return client.forget();
 }
 
 bool
 ImageBridgeChild::AllocUnsafeShmem(size_t aSize,
                                    ipc::SharedMemory::SharedMemoryType aType,
                                    ipc::Shmem* aShmem)
@@ -828,16 +849,31 @@ ImageBridgeChild::AllocPMediaSystemResou
 bool
 ImageBridgeChild::DeallocPMediaSystemResourceManagerChild(PMediaSystemResourceManagerChild* aActor)
 {
   MOZ_ASSERT(aActor);
   delete static_cast<mozilla::media::MediaSystemResourceManagerChild*>(aActor);
   return true;
 }
 
+PImageContainerChild*
+ImageBridgeChild::AllocPImageContainerChild()
+{
+  // we always use the "power-user" ctor
+  NS_RUNTIMEABORT("not reached");
+  return nullptr;
+}
+
+bool
+ImageBridgeChild::DeallocPImageContainerChild(PImageContainerChild* actor)
+{
+  delete actor;
+  return true;
+}
+
 bool
 ImageBridgeChild::RecvParentAsyncMessages(InfallibleTArray<AsyncParentMessageData>&& aMessages)
 {
   for (AsyncParentMessageArray::index_type i = 0; i < aMessages.Length(); ++i) {
     const AsyncParentMessageData& message = aMessages[i];
 
     switch (message.type()) {
       case AsyncParentMessageData::TOpDeliverFence: {
@@ -870,16 +906,25 @@ ImageBridgeChild::RecvParentAsyncMessage
       default:
         NS_ERROR("unknown AsyncParentMessageData type");
         return false;
     }
   }
   return true;
 }
 
+bool
+ImageBridgeChild::RecvDidComposite(InfallibleTArray<ImageCompositeNotification>&& aNotifications)
+{
+  for (auto& n : aNotifications) {
+    ImageContainer::NotifyComposite(n);
+  }
+  return true;
+}
+
 PTextureChild*
 ImageBridgeChild::CreateTexture(const SurfaceDescriptor& aSharedData,
                                 TextureFlags aFlags)
 {
   MOZ_ASSERT(!mShuttingDown);
   return SendPTextureConstructor(aSharedData, aFlags);
 }
 
--- a/gfx/layers/ipc/ImageBridgeChild.h
+++ b/gfx/layers/ipc/ImageBridgeChild.h
@@ -169,55 +169,67 @@ public:
 
   /**
    * Returns the ImageBridgeChild's message loop.
    *
    * Can be called from any thread.
    */
   virtual MessageLoop * GetMessageLoop() const override;
 
-  PCompositableChild* AllocPCompositableChild(const TextureInfo& aInfo, uint64_t* aID) override;
+  PCompositableChild* AllocPCompositableChild(const TextureInfo& aInfo,
+                                              PImageContainerChild* aChild, uint64_t* aID) override;
   bool DeallocPCompositableChild(PCompositableChild* aActor) override;
 
   /**
    * This must be called by the static function DeleteImageBridgeSync defined
    * in ImageBridgeChild.cpp ONLY.
    */
   ~ImageBridgeChild();
 
   virtual PTextureChild*
   AllocPTextureChild(const SurfaceDescriptor& aSharedData, const TextureFlags& aFlags) override;
 
   virtual bool
   DeallocPTextureChild(PTextureChild* actor) override;
 
   PMediaSystemResourceManagerChild*
   AllocPMediaSystemResourceManagerChild() override;
-
   bool
   DeallocPMediaSystemResourceManagerChild(PMediaSystemResourceManagerChild* aActor) override;
 
+  virtual PImageContainerChild*
+  AllocPImageContainerChild() override;
+  virtual bool
+  DeallocPImageContainerChild(PImageContainerChild* actor) override;
+
   virtual bool
   RecvParentAsyncMessages(InfallibleTArray<AsyncParentMessageData>&& aMessages) override;
 
-  already_AddRefed<ImageClient> CreateImageClient(CompositableType aType);
-  already_AddRefed<ImageClient> CreateImageClientNow(CompositableType aType);
+  virtual bool
+  RecvDidComposite(InfallibleTArray<ImageCompositeNotification>&& aNotifications) override;
 
-  static void DispatchReleaseImageClient(ImageClient* aClient);
+  already_AddRefed<ImageClient> CreateImageClient(CompositableType aType,
+                                                  ImageContainer* aImageContainer);
+  already_AddRefed<ImageClient> CreateImageClientNow(CompositableType aType,
+                                                     ImageContainer* aImageContainer);
+
+  static void DispatchReleaseImageClient(ImageClient* aClient,
+                                         PImageContainerChild* aChild = nullptr);
   static void DispatchReleaseTextureClient(TextureClient* aClient);
   static void DispatchImageClientUpdate(ImageClient* aClient, ImageContainer* aContainer);
 
   /**
    * Flush all Images sent to CompositableHost.
    */
   static void FlushAllImages(ImageClient* aClient, ImageContainer* aContainer, bool aExceptFront);
 
   // CompositableForwarder
 
-  virtual void Connect(CompositableClient* aCompositable) override;
+  virtual void Connect(CompositableClient* aCompositable,
+                       ImageContainer* aImageContainer) override;
 
   virtual bool IsImageBridgeChild() const override { return true; }
 
   /**
    * See CompositableForwarder::UseTextures
    */
   virtual void UseTextures(CompositableClient* aCompositable,
                            const nsTArray<TimedTextureClient>& aTextures) override;
--- a/gfx/layers/ipc/ImageBridgeParent.cpp
+++ b/gfx/layers/ipc/ImageBridgeParent.cpp
@@ -78,16 +78,22 @@ ImageBridgeParent::~ImageBridgeParent()
   MOZ_ASSERT(NS_IsMainThread());
 
   if (mTransport) {
     MOZ_ASSERT(XRE_GetIOMessageLoop());
     XRE_GetIOMessageLoop()->PostTask(FROM_HERE,
                                      new DeleteTask<Transport>(mTransport));
   }
 
+  nsTArray<PImageContainerParent*> parents;
+  ManagedPImageContainerParent(parents);
+  for (PImageContainerParent* p : parents) {
+    delete p;
+  }
+
   sImageBridges.erase(OtherPid());
 }
 
 LayersBackend
 ImageBridgeParent::GetCompositorBackendType() const
 {
   return Compositor::GetBackend();
 }
@@ -231,21 +237,22 @@ static  uint64_t GenImageContainerID() {
   static uint64_t sNextImageID = 1;
 
   ++sNextImageID;
   return sNextImageID;
 }
 
 PCompositableParent*
 ImageBridgeParent::AllocPCompositableParent(const TextureInfo& aInfo,
+                                            PImageContainerParent* aImageContainer,
                                             uint64_t* aID)
 {
   uint64_t id = GenImageContainerID();
   *aID = id;
-  return CompositableHost::CreateIPDLActor(this, aInfo, id);
+  return CompositableHost::CreateIPDLActor(this, aInfo, id, aImageContainer);
 }
 
 bool ImageBridgeParent::DeallocPCompositableParent(PCompositableParent* aActor)
 {
   return CompositableHost::DestroyIPDLActor(aActor);
 }
 
 PTextureParent*
@@ -270,28 +277,82 @@ ImageBridgeParent::AllocPMediaSystemReso
 bool
 ImageBridgeParent::DeallocPMediaSystemResourceManagerParent(PMediaSystemResourceManagerParent* aActor)
 {
   MOZ_ASSERT(aActor);
   delete static_cast<mozilla::media::MediaSystemResourceManagerParent*>(aActor);
   return true;
 }
 
+PImageContainerParent*
+ImageBridgeParent::AllocPImageContainerParent()
+{
+  return new ImageContainerParent();
+}
+
+bool
+ImageBridgeParent::DeallocPImageContainerParent(PImageContainerParent* actor)
+{
+  delete actor;
+  return true;
+}
+
 void
 ImageBridgeParent::SendAsyncMessage(const InfallibleTArray<AsyncParentMessageData>& aMessage)
 {
   mozilla::unused << SendParentAsyncMessages(aMessage);
 }
 
 bool
 ImageBridgeParent::RecvChildAsyncMessages(InfallibleTArray<AsyncChildMessageData>&& aMessages)
 {
   return true;
 }
 
+class ProcessIdComparator
+{
+public:
+  bool Equals(const ImageCompositeNotification& aA,
+              const ImageCompositeNotification& aB) const
+  {
+    return aA.imageContainerParent()->OtherPid() == aB.imageContainerParent()->OtherPid();
+  }
+  bool LessThan(const ImageCompositeNotification& aA,
+                const ImageCompositeNotification& aB) const
+  {
+    return aA.imageContainerParent()->OtherPid() < aB.imageContainerParent()->OtherPid();
+  }
+};
+
+/* static */ bool
+ImageBridgeParent::NotifyImageComposites(nsTArray<ImageCompositeNotification>& aNotifications)
+{
+  // Group the notifications by destination process ID and then send the
+  // notifications in one message per group.
+  aNotifications.Sort(ProcessIdComparator());
+  uint32_t i = 0;
+  bool ok = true;
+  while (i < aNotifications.Length()) {
+    nsAutoTArray<ImageCompositeNotification,1> notifications;
+    notifications.AppendElement(aNotifications[i]);
+    uint32_t end = i + 1;
+    ProcessId pid = aNotifications[i].imageContainerParent()->OtherPid();
+    while (end < aNotifications.Length() &&
+           aNotifications[end].imageContainerParent()->OtherPid() == pid) {
+      notifications.AppendElement(aNotifications[end]);
+      ++end;
+    }
+    if (!GetInstance(pid)->SendDidComposite(notifications)) {
+      ok = false;
+    }
+    i = end;
+  }
+  return ok;
+}
+
 MessageLoop * ImageBridgeParent::GetMessageLoop() const {
   return mMessageLoop;
 }
 
 void
 ImageBridgeParent::DeferredDestroy()
 {
   mCompositorThreadHolder = nullptr;
--- a/gfx/layers/ipc/ImageBridgeParent.h
+++ b/gfx/layers/ipc/ImageBridgeParent.h
@@ -4,16 +4,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef gfx_layers_ipc_ImageBridgeParent_h_
 #define gfx_layers_ipc_ImageBridgeParent_h_
 
 #include <stddef.h>                     // for size_t
 #include <stdint.h>                     // for uint32_t, uint64_t
 #include "CompositableTransactionParent.h"
+#include "ImageContainerParent.h"
 #include "mozilla/Assertions.h"         // for MOZ_ASSERT_HELPER2
 #include "mozilla/Attributes.h"         // for override
 #include "mozilla/ipc/ProtocolUtils.h"
 #include "mozilla/ipc/SharedMemory.h"   // for SharedMemory, etc
 #include "mozilla/layers/CompositorParent.h"
 #include "mozilla/layers/PImageBridgeParent.h"
 #include "nsAutoPtr.h"                  // for nsRefPtr
 #include "nsISupportsImpl.h"
@@ -69,25 +70,28 @@ public:
   // PImageBridge
   virtual bool RecvImageBridgeThreadId(const PlatformThreadId& aThreadId) override;
   virtual bool RecvUpdate(EditArray&& aEdits, EditReplyArray* aReply) override;
   virtual bool RecvUpdateNoSwap(EditArray&& aEdits) override;
 
   virtual bool IsAsync() const override { return true; }
 
   PCompositableParent* AllocPCompositableParent(const TextureInfo& aInfo,
+                                                PImageContainerParent* aImageContainer,
                                                 uint64_t*) override;
   bool DeallocPCompositableParent(PCompositableParent* aActor) override;
 
   virtual PTextureParent* AllocPTextureParent(const SurfaceDescriptor& aSharedData,
                                               const TextureFlags& aFlags) override;
   virtual bool DeallocPTextureParent(PTextureParent* actor) override;
 
   PMediaSystemResourceManagerParent* AllocPMediaSystemResourceManagerParent() override;
   bool DeallocPMediaSystemResourceManagerParent(PMediaSystemResourceManagerParent* aActor) override;
+  virtual PImageContainerParent* AllocPImageContainerParent() override;
+  virtual bool DeallocPImageContainerParent(PImageContainerParent* actor) override;
 
   virtual bool
   RecvChildAsyncMessages(InfallibleTArray<AsyncChildMessageData>&& aMessages) override;
 
   // Shutdown step 1
   virtual bool RecvWillStop() override;
   // Shutdown step 2
   virtual bool RecvStop() override;
@@ -134,16 +138,18 @@ public:
                                         PTextureParent* aTexture,
                                         CompositableHost* aCompositableHost);
 
   using CompositableParentManager::SendPendingAsyncMessages;
   static void SendPendingAsyncMessages(base::ProcessId aChildProcessId);
 
   static ImageBridgeParent* GetInstance(ProcessId aId);
 
+  static bool NotifyImageComposites(nsTArray<ImageCompositeNotification>& aNotifications);
+
   // Overriden from IToplevelProtocol
   IToplevelProtocol*
   CloneToplevel(const InfallibleTArray<ProtocolFdMapping>& aFds,
                 base::ProcessHandle aPeerProcess,
                 mozilla::ipc::ProtocolCloneContext* aCtx) override;
 
 private:
   void DeferredDestroy();
new file mode 100644
--- /dev/null
+++ b/gfx/layers/ipc/ImageContainerParent.cpp
@@ -0,0 +1,37 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ImageContainerParent.h"
+
+#include "nsThreadUtils.h"
+#include "mozilla/layers/ImageHost.h"
+
+namespace mozilla {
+namespace layers {
+
+ImageContainerParent::~ImageContainerParent()
+{
+  while (!mImageHosts.IsEmpty()) {
+    mImageHosts[mImageHosts.Length() - 1]->SetImageContainer(nullptr);
+  }
+}
+
+static void SendDeleteAndIgnoreResult(ImageContainerParent* self)
+{
+  unused << PImageContainerParent::Send__delete__(self);
+}
+
+bool ImageContainerParent::RecvAsyncDelete()
+{
+  MessageLoop::current()->PostTask(
+    FROM_HERE, NewRunnableFunction(&SendDeleteAndIgnoreResult, this));
+
+  return true;
+}
+
+}
+}
new file mode 100644
--- /dev/null
+++ b/gfx/layers/ipc/ImageContainerParent.h
@@ -0,0 +1,38 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_ImageContainerParent_h
+#define mozilla_layers_ImageContainerParent_h
+
+#include "mozilla/Attributes.h"         // for override
+#include "mozilla/ipc/ProtocolUtils.h"
+#include "mozilla/layers/PImageContainerParent.h"
+#include "nsAutoPtr.h"                  // for nsRefPtr
+
+namespace mozilla {
+namespace layers {
+
+class ImageHost;
+
+class ImageContainerParent : public PImageContainerParent
+{
+public:
+  ImageContainerParent() {}
+  ~ImageContainerParent();
+
+  virtual bool RecvAsyncDelete() override;
+
+  nsAutoTArray<ImageHost*,1> mImageHosts;
+
+private:
+  virtual void ActorDestroy(ActorDestroyReason why) override {}
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // ifndef mozilla_layers_ImageContainerParent_h
--- a/gfx/layers/ipc/LayerTransactionChild.cpp
+++ b/gfx/layers/ipc/LayerTransactionChild.cpp
@@ -4,16 +4,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "LayerTransactionChild.h"
 #include "mozilla/layers/CompositableClient.h"  // for CompositableChild
 #include "mozilla/layers/PCompositableChild.h"  // for PCompositableChild
 #include "mozilla/layers/PLayerChild.h"  // for PLayerChild
+#include "mozilla/layers/PImageContainerChild.h"
 #include "mozilla/layers/ShadowLayers.h"  // for ShadowLayerForwarder
 #include "mozilla/mozalloc.h"           // for operator delete, etc
 #include "nsDebug.h"                    // for NS_RUNTIMEABORT, etc
 #include "nsTArray.h"                   // for nsTArray
 #include "mozilla/layers/TextureClient.h"
 
 namespace mozilla {
 namespace layers {
--- a/gfx/layers/ipc/LayerTransactionParent.cpp
+++ b/gfx/layers/ipc/LayerTransactionParent.cpp
@@ -576,17 +576,16 @@ LayerTransactionParent::RecvUpdate(Infal
       if (!compositableParent) {
         NS_ERROR("CompositableParent not found in the map");
         return false;
       }
       CompositableHost* host = CompositableHost::FromIPDLActor(compositableParent);
       if (!Attach(cast(op.layerParent()), host, true)) {
         return false;
       }
-
       host->SetCompositorID(mLayerManager->GetCompositor()->GetCompositorID());
       break;
     }
     default:
       NS_RUNTIMEABORT("not reached");
     }
   }
 
--- a/gfx/layers/ipc/LayersMessages.ipdlh
+++ b/gfx/layers/ipc/LayersMessages.ipdlh
@@ -4,16 +4,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 include LayersSurfaces;
 include protocol PCompositable;
 include protocol PCompositor;
 include protocol PLayer;
+include protocol PImageContainer;
 include protocol PRenderFrame;
 include protocol PTexture;
 
 include "gfxipc/ShadowLayerUtils.h";
 include "mozilla/GfxMessageUtils.h";
 include "ImageLayers.h";
 
 using mozilla::GraphicsFilterType from "mozilla/GfxMessageUtils.h";
@@ -49,17 +50,17 @@ namespace layers {
 struct TargetConfig {
   IntRect naturalBounds;
   ScreenRotation rotation;
   ScreenOrientation orientation;
   nsIntRegion clearRegion;
 };
 
 // Create a shadow layer for |layer|
-struct OpCreatePaintedLayer     { PLayer layer; };
+struct OpCreatePaintedLayer    { PLayer layer; };
 struct OpCreateContainerLayer  { PLayer layer; };
 struct OpCreateImageLayer      { PLayer layer; };
 struct OpCreateColorLayer      { PLayer layer; };
 struct OpCreateCanvasLayer     { PLayer layer; };
 struct OpCreateRefLayer        { PLayer layer; };
 
 struct OpAttachCompositable {
   PLayer layer;
@@ -383,16 +384,18 @@ union MaybeFence {
   null_t;
 };
 
 struct TimedTexture {
   PTexture texture;
   MaybeFence fence;
   TimeStamp timeStamp;
   IntRect picture;
+  int32_t frameID;
+  int32_t producerID;
 };
 
 /**
  * Tells the compositor-side which textures to use (for example, as front buffer
  * if there are several textures for double buffering).
  * This provides a list of textures with timestamps, ordered by timestamp.
  * The newest texture whose timestamp is <= the current time is rendered
  * (where null is considered less than every other timestamp). If there is no
@@ -468,16 +471,28 @@ union Edit {
 
 // Replies to operations
 
 struct OpContentBufferSwap {
   PCompositable compositable;
   nsIntRegion frontUpdatedRegion;
 };
 
+/**
+ * An ImageCompositeNotification is sent the first time a particular
+ * image is composited by an ImageHost.
+ */
+struct ImageCompositeNotification {
+  PImageContainer imageContainer;
+  TimeStamp imageTimeStamp;
+  TimeStamp firstCompositeTimeStamp;
+  int32_t frameID;
+  int32_t producerID;
+};
+
 // Unit of a "changeset reply".  This is a weird abstraction, probably
 // only to be used for buffer swapping.
 union EditReply {
   OpContentBufferSwap;
 };
 
 union AsyncParentMessageData {
   OpDeliverFence;
--- a/gfx/layers/ipc/PCompositor.ipdl
+++ b/gfx/layers/ipc/PCompositor.ipdl
@@ -2,16 +2,17 @@
  * vim: sw=2 ts=8 et :
  */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 include LayersSurfaces;
 include LayersMessages;
+include protocol PLayer;
 include protocol PLayerTransaction;
 include "mozilla/GfxMessageUtils.h";
 include "nsRegion.h";
 
 using struct mozilla::null_t from "ipc/IPCMessageUtils.h";
 using struct mozilla::layers::TextureFactoryIdentifier from "mozilla/layers/CompositorTypes.h";
 using struct mozilla::layers::FrameMetrics from "FrameMetrics.h";
 using mozilla::layers::FrameMetrics::ViewID from "FrameMetrics.h";
@@ -37,16 +38,18 @@ sync protocol PCompositor
 
 child:
   // The child should invalidate everything so that the whole window is redrawn.
   async InvalidateAll();
 
   // The compositor completed a layers transaction. id is the layers id
   // of the child layer tree that was composited (or 0 when notifying
   // the root layer tree).
+  // transactionId is the id of the transaction before this composite, or 0
+  // if there was no transaction since the last composite.
   async DidComposite(uint64_t id, uint64_t transactionId);
 
   // The parent sends the child the requested fill ratio numbers.
   async Overfill(uint32_t aOverfill);
 
   /**
    * Parent informs the child that the graphics objects are ready for
    * compositing.  This usually means that the graphics objects (textures
--- a/gfx/layers/ipc/PImageBridge.ipdl
+++ b/gfx/layers/ipc/PImageBridge.ipdl
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 include LayersSurfaces;
 include LayersMessages;
 include protocol PCompositable;
+include protocol PImageContainer;
 include protocol PLayer;
 include protocol PTexture;
 include ProtocolTypes;
 include protocol PMediaSystemResourceManager;
 
 include "mozilla/GfxMessageUtils.h";
 
 using struct mozilla::layers::TextureInfo from "mozilla/layers/CompositorTypes.h";
@@ -26,22 +27,24 @@ namespace layers {
  * frames directly to the compositor thread/process without relying on the main thread
  * which might be too busy dealing with content script.
  */
 sync protocol PImageBridge
 {
   manages PCompositable;
   manages PTexture;
   manages PMediaSystemResourceManager;
+  manages PImageContainer;
 
 child:
   async ParentAsyncMessages(AsyncParentMessageData[] aMessages);
 
+  async DidComposite(ImageCompositeNotification[] aNotifications);
+
 parent:
-
   async ImageBridgeThreadId(PlatformThreadId aTreahdId);
 
   sync Update(CompositableOperation[] ops) returns (EditReply[] reply);
   async UpdateNoSwap(CompositableOperation[] ops);
 
   // First step of the destruction sequence. This puts ImageBridge
   // in a state in which it can't send asynchronous messages
   // so as to not race with the upcomming Stop message and destruction.
@@ -49,19 +52,21 @@ parent:
   // it is scheduled in the ImageBridgeChild's message queue in order to ensure
   // that all of the messages from the parent side have been received and processed
   // before sending Stop, and that after Stop returns, there is no message in
   // flight on any side and we can safely destroy the channel and threads.
   sync WillStop();
   // Second step
   sync Stop();
 
-  sync PCompositable(TextureInfo aInfo) returns (uint64_t id);
+  sync PCompositable(TextureInfo aInfo,
+                     PImageContainer aImageContainer) returns (uint64_t id);
   async PTexture(SurfaceDescriptor aSharedData, TextureFlags aTextureFlags);
   async PMediaSystemResourceManager();
+  async PImageContainer();
 
   async ChildAsyncMessages(AsyncChildMessageData[] aMessages);
 };
 
 
 } // namespace
 } // namespace
 
new file mode 100644
--- /dev/null
+++ b/gfx/layers/ipc/PImageContainer.ipdl
@@ -0,0 +1,33 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+include protocol PImageBridge;
+
+namespace mozilla {
+namespace layers {
+
+/**
+ * PImageContainer represents an ImageContainer.
+ */
+
+async protocol PImageContainer {
+    manager PImageBridge;
+parent:
+    /**
+     * The child effectively owns the parent. When the child should be
+     * destroyed, it sends an AsyncDelete to the parent but does not die
+     * because we could still have messages in flight from the compositor
+     * mentioning the child. The parent handles AsyncDelete by destroying
+     * itself and sending __delete__ to the child to clean it up.
+     */
+    async AsyncDelete();
+child:
+    async __delete__();
+};
+
+} // layers
+} // mozilla
--- a/gfx/layers/ipc/ShadowLayerParent.cpp
+++ b/gfx/layers/ipc/ShadowLayerParent.cpp
@@ -20,34 +20,45 @@ namespace mozilla {
 namespace layers {
 
 ShadowLayerParent::ShadowLayerParent() : mLayer(nullptr)
 {
 }
 
 ShadowLayerParent::~ShadowLayerParent()
 {
+  Disconnect();
+}
+
+void
+ShadowLayerParent::Disconnect()
+{
+  if (mLayer) {
+    mLayer->Disconnect();
+    mLayer = nullptr;
+  }
 }
 
 void
 ShadowLayerParent::Bind(Layer* layer)
 {
-  mLayer = layer;
+  if (mLayer != layer) {
+    Disconnect();
+    mLayer = layer;
+  }
 }
 
 void
 ShadowLayerParent::Destroy()
 {
   // It's possible for Destroy() to come in just after this has been
   // created, but just before the transaction in which Bind() would
   // have been called.  In that case, we'll ignore shadow-layers
   // transactions from there on and never get a layer here.
-  if (mLayer) {
-    mLayer->Disconnect();
-  }
+  Disconnect();
 }
 
 ContainerLayerComposite*
 ShadowLayerParent::AsContainerLayerComposite() const
 {
   return mLayer && mLayer->GetType() == Layer::TYPE_CONTAINER
          ? static_cast<ContainerLayerComposite*>(mLayer.get())
          : nullptr;
@@ -98,25 +109,21 @@ ShadowLayerParent::ActorDestroy(ActorDes
 {
   switch (why) {
   case AncestorDeletion:
     NS_RUNTIMEABORT("shadow layer deleted out of order!");
     return;                     // unreached
 
   case Deletion:
     // See comment near Destroy() above.
-    if (mLayer) {
-      mLayer->Disconnect();
-    }
+    Disconnect();
     break;
 
   case AbnormalShutdown:
-    if (mLayer) {
-      mLayer->Disconnect();
-    }
+    Disconnect();
     break;
 
   case NormalShutdown:
     // let IPDL-generated code automatically clean up Shmems and so
     // forth; our channel is disconnected anyway
     break;
 
   case FailedConstructor:
--- a/gfx/layers/ipc/ShadowLayerParent.h
+++ b/gfx/layers/ipc/ShadowLayerParent.h
@@ -43,15 +43,17 @@ public:
   ColorLayerComposite* AsColorLayerComposite() const;
   ImageLayerComposite* AsImageLayerComposite() const;
   RefLayerComposite* AsRefLayerComposite() const;
   PaintedLayerComposite* AsPaintedLayerComposite() const;
 
 private:
   virtual void ActorDestroy(ActorDestroyReason why) override;
 
+  void Disconnect();
+
   nsRefPtr<Layer> mLayer;
 };
 
 } // namespace layers
 } // namespace mozilla
 
 #endif // ifndef mozilla_layers_ShadowLayerParent_h
--- a/gfx/layers/ipc/ShadowLayers.cpp
+++ b/gfx/layers/ipc/ShadowLayers.cpp
@@ -359,17 +359,18 @@ ShadowLayerForwarder::UseTextures(Compos
   nsAutoTArray<TimedTexture,4> textures;
 
   for (auto& t : aTextures) {
     MOZ_ASSERT(t.mTextureClient);
     MOZ_ASSERT(t.mTextureClient->GetIPDLActor());
     FenceHandle fence = t.mTextureClient->GetAcquireFenceHandle();
     textures.AppendElement(TimedTexture(nullptr, t.mTextureClient->GetIPDLActor(),
                                         fence.IsValid() ? MaybeFence(fence) : MaybeFence(null_t()),
-                                        t.mTimeStamp, t.mPictureRect));
+                                        t.mTimeStamp, t.mPictureRect,
+                                        t.mFrameID, t.mProducerID));
     if ((t.mTextureClient->GetFlags() & TextureFlags::IMMEDIATE_UPLOAD)
         && t.mTextureClient->HasInternalBuffer()) {
 
       // We use IMMEDIATE_UPLOAD when we want to be sure that the upload cannot
       // race with updates on the main thread. In this case we want the transaction
       // to be synchronous.
       mTxn->MarkSyncTransaction();
     }
@@ -762,17 +763,18 @@ ShadowLayerForwarder::ConstructShadowFor
 /*static*/ void
 ShadowLayerForwarder::PlatformSyncBeforeUpdate()
 {
 }
 
 #endif  // !defined(MOZ_HAVE_PLATFORM_SPECIFIC_LAYER_BUFFERS)
 
 void
-ShadowLayerForwarder::Connect(CompositableClient* aCompositable)
+ShadowLayerForwarder::Connect(CompositableClient* aCompositable,
+                              ImageContainer* aImageContainer)
 {
 #ifdef GFX_COMPOSITOR_LOGGING
   printf("ShadowLayerForwarder::Connect(Compositable)\n");
 #endif
   MOZ_ASSERT(aCompositable);
   MOZ_ASSERT(mShadowManager);
   if (!HasShadowManager() ||
       !mShadowManager->IPCOpen()) {
--- a/gfx/layers/ipc/ShadowLayers.h
+++ b/gfx/layers/ipc/ShadowLayers.h
@@ -22,16 +22,17 @@
 #include "nsRegion.h"                   // for nsIntRegion
 #include "nsTArrayForwardDeclare.h"     // for InfallibleTArray
 #include "nsIWidget.h"
 
 namespace mozilla {
 namespace layers {
 
 class EditReply;
+class ImageContainer;
 class Layer;
 class PLayerChild;
 class PLayerTransactionChild;
 class LayerTransactionChild;
 class ShadowableLayer;
 class SurfaceDescriptor;
 class TextureClient;
 class ThebesBuffer;
@@ -118,17 +119,18 @@ class ShadowLayerForwarder final : publi
 
 public:
   virtual ~ShadowLayerForwarder();
 
   /**
    * Setup the IPDL actor for aCompositable to be part of layers
    * transactions.
    */
-  void Connect(CompositableClient* aCompositable) override;
+  virtual void Connect(CompositableClient* aCompositable,
+                       ImageContainer* aImageContainer) override;
 
   virtual PTextureChild* CreateTexture(const SurfaceDescriptor& aSharedData,
                                        TextureFlags aFlags) override;
 
   /**
    * Adds an edit in the layers transaction in order to attach
    * the corresponding compositable and layer on the compositor side.
    * Connect must have been called on aCompositable beforehand.
--- a/gfx/layers/moz.build
+++ b/gfx/layers/moz.build
@@ -142,19 +142,21 @@ EXPORTS.mozilla.layers += [
     'ipc/CompositableForwarder.h',
     'ipc/CompositableTransactionParent.h',
     'ipc/CompositorChild.h',
     'ipc/CompositorLRU.h',
     'ipc/CompositorParent.h',
     'ipc/FenceUtils.h',
     'ipc/ImageBridgeChild.h',
     'ipc/ImageBridgeParent.h',
+    'ipc/ImageContainerParent.h',
     'ipc/ISurfaceAllocator.h',
     'ipc/LayerTransactionChild.h',
     'ipc/LayerTransactionParent.h',
+    'ipc/ShadowLayerChild.h',
     'ipc/ShadowLayers.h',
     'ipc/ShadowLayersManager.h',
     'ipc/SharedBufferManagerChild.h',
     'ipc/SharedBufferManagerParent.h',
     'ipc/SharedPlanarYCbCrImage.h',
     'ipc/SharedRGBImage.h',
     'LayerMetricsWrapper.h',
     'LayersTypes.h',
@@ -298,16 +300,17 @@ UNIFIED_SOURCES += [
     'ipc/CompositableTransactionParent.cpp',
     'ipc/CompositorBench.cpp',
     'ipc/CompositorChild.cpp',
     'ipc/CompositorLRU.cpp',
     'ipc/CompositorParent.cpp',
     'ipc/FenceUtils.cpp',
     'ipc/ImageBridgeChild.cpp',
     'ipc/ImageBridgeParent.cpp',
+    'ipc/ImageContainerParent.cpp',
     'ipc/ISurfaceAllocator.cpp',
     'ipc/LayerTransactionChild.cpp',
     'ipc/LayerTransactionParent.cpp',
     'ipc/ShadowLayerChild.cpp',
     'ipc/ShadowLayerParent.cpp',
     'ipc/ShadowLayers.cpp',
     'ipc/SharedBufferManagerChild.cpp',
     'ipc/SharedBufferManagerParent.cpp',
@@ -359,16 +362,17 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'coco
     ]
 
 IPDL_SOURCES = [
     'ipc/LayersMessages.ipdlh',
     'ipc/LayersSurfaces.ipdlh',
     'ipc/PCompositable.ipdl',
     'ipc/PCompositor.ipdl',
     'ipc/PImageBridge.ipdl',
+    'ipc/PImageContainer.ipdl',
     'ipc/PLayer.ipdl',
     'ipc/PLayerTransaction.ipdl',
     'ipc/PSharedBufferManager.ipdl',
     'ipc/PTexture.ipdl',
 ]
 
 FAIL_ON_WARNINGS = True
 
--- a/layout/ipc/RenderFrameParent.h
+++ b/layout/ipc/RenderFrameParent.h
@@ -21,33 +21,31 @@ class nsSubDocumentFrame;
 
 namespace mozilla {
 
 class InputEvent;
 
 namespace layers {
 class APZCTreeManager;
 class TargetConfig;
-class LayerTransactionParent;
 struct TextureFactoryIdentifier;
 struct ScrollableLayerGuid;
 }
 
 namespace layout {
 
 class RemoteContentController;
 
 class RenderFrameParent : public PRenderFrameParent
 {
   typedef mozilla::layers::FrameMetrics FrameMetrics;
   typedef mozilla::layers::ContainerLayer ContainerLayer;
   typedef mozilla::layers::Layer Layer;
   typedef mozilla::layers::LayerManager LayerManager;
   typedef mozilla::layers::TargetConfig TargetConfig;
-  typedef mozilla::layers::LayerTransactionParent LayerTransactionParent;
   typedef mozilla::ContainerLayerParameters ContainerLayerParameters;
   typedef mozilla::layers::TextureFactoryIdentifier TextureFactoryIdentifier;
   typedef mozilla::layers::ScrollableLayerGuid ScrollableLayerGuid;
   typedef mozilla::layers::TouchBehaviorFlags TouchBehaviorFlags;
   typedef mozilla::layers::ZoomConstraints ZoomConstraints;
   typedef FrameMetrics::ViewID ViewID;
 
 public: