Backed out 13 changesets (bug 1711061) for causing browser-chrome failures in test/performance/browser_startup_images. CLOSED TREE
authorSandor Molnar <smolnar@mozilla.com>
Tue, 26 Oct 2021 18:04:34 +0300
changeset 596960 a129e5ede22ceb7420f1ffec4c5e415b6569f8d0
parent 596959 88ca2c5e4030a237fe1b7daee8a6fc7574e8d308
child 596961 5a4d9d296d42cac0bbe774144f5c7b285b72d3fa
push id38915
push usermlaza@mozilla.com
push dateTue, 26 Oct 2021 21:44:17 +0000
treeherdermozilla-central@7708adfc84d3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1711061
milestone95.0a1
backs out5988220f56b87a6e9cbf8c3a440d2492de9c06e4
cb7721d7ce496a7aae2cac26b20caacddaa9dea0
343746ffae73a44556e24bad27628a3fe79f5bfd
000940244dcf4ed6dd3ecf6d5ca07f367a59b56b
27565d5ae08de12e6af08077232229d85b26752e
f3b2379d971be27ff01455f5a8fe66a5846c4e6e
d4b3d0fa17716d2df56a8643f9408b8bc8c5d08c
9af5b3f2225fabaed91cb32e67df099fe0756af2
da992effeff1453df88c8466b95084e16fd7c19a
6b2bc6c16b9ff5a7f62a1dd18646a21a72af5347
ac2792d42be2c97b057ba21e7b1877b1e628ae2b
cb00177e655ef3fb97c4c685d88c06989ef282a9
8d5261184226fa4244dcd59fb5ff0ec5c387081e
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
Backed out 13 changesets (bug 1711061) for causing browser-chrome failures in test/performance/browser_startup_images. CLOSED TREE Backed out changeset 5988220f56b8 (bug 1711061) Backed out changeset cb7721d7ce49 (bug 1711061) Backed out changeset 343746ffae73 (bug 1711061) Backed out changeset 000940244dcf (bug 1711061) Backed out changeset 27565d5ae08d (bug 1711061) Backed out changeset f3b2379d971b (bug 1711061) Backed out changeset d4b3d0fa1771 (bug 1711061) Backed out changeset 9af5b3f2225f (bug 1711061) Backed out changeset da992effeff1 (bug 1711061) Backed out changeset 6b2bc6c16b9f (bug 1711061) Backed out changeset ac2792d42be2 (bug 1711061) Backed out changeset cb00177e655e (bug 1711061) Backed out changeset 8d5261184226 (bug 1711061)
gfx/layers/ImageContainer.cpp
gfx/layers/ImageContainer.h
gfx/layers/ipc/SharedSurfacesChild.cpp
gfx/layers/ipc/SharedSurfacesChild.h
gfx/layers/wr/WebRenderCommandBuilder.cpp
gfx/layers/wr/WebRenderCommandBuilder.h
gfx/layers/wr/WebRenderUserData.cpp
gfx/layers/wr/WebRenderUserData.h
image/AnimationFrameBuffer.cpp
image/AnimationFrameBuffer.h
image/AnimationSurfaceProvider.cpp
image/AnimationSurfaceProvider.h
image/BlobSurfaceProvider.cpp
image/BlobSurfaceProvider.h
image/ClippedImage.cpp
image/ClippedImage.h
image/DecodedSurfaceProvider.cpp
image/DecodedSurfaceProvider.h
image/DynamicImage.cpp
image/FrameAnimator.cpp
image/FrameAnimator.h
image/FrozenImage.cpp
image/FrozenImage.h
image/ISurfaceProvider.h
image/Image.cpp
image/Image.h
image/ImageRegion.h
image/ImageWrapper.cpp
image/OrientedImage.cpp
image/OrientedImage.h
image/RasterImage.cpp
image/RasterImage.h
image/SourceSurfaceBlobImage.cpp
image/SourceSurfaceBlobImage.h
image/SurfaceCache.cpp
image/SurfaceCache.h
image/SurfaceFlags.h
image/VectorImage.cpp
image/VectorImage.h
image/WebRenderImageProvider.h
image/imgFrame.cpp
image/imgFrame.h
image/imgIContainer.idl
image/imgIRequest.idl
image/imgLoader.cpp
image/imgRequestProxy.cpp
image/moz.build
image/test/gtest/TestContainers.cpp
image/test/gtest/moz.build
layout/base/nsLayoutUtils.cpp
layout/generic/nsImageFrame.cpp
layout/painting/nsCSSRenderingBorders.cpp
layout/painting/nsImageRenderer.cpp
layout/style/ImageLoader.cpp
layout/svg/SVGImageFrame.cpp
layout/xul/nsImageBoxFrame.cpp
--- a/gfx/layers/ImageContainer.cpp
+++ b/gfx/layers/ImageContainer.cpp
@@ -23,16 +23,17 @@
 #include "mozilla/ipc/CrossProcessMutex.h"  // for CrossProcessMutex, etc
 #include "mozilla/layers/CompositorTypes.h"
 #include "mozilla/layers/ImageBridgeChild.h"     // for ImageBridgeChild
 #include "mozilla/layers/ImageClient.h"          // for ImageClient
 #include "mozilla/layers/ImageDataSerializer.h"  // for SurfaceDescriptorBuffer
 #include "mozilla/layers/LayersMessages.h"
 #include "mozilla/layers/SharedPlanarYCbCrImage.h"
 #include "mozilla/layers/SharedRGBImage.h"
+#include "mozilla/layers/SharedSurfacesChild.h"  // for SharedSurfacesAnimation
 #include "mozilla/layers/TextureClientRecycleAllocator.h"
 #include "nsProxyRelease.h"
 #include "nsISupportsUtils.h"  // for NS_IF_ADDREF
 
 #ifdef XP_MACOSX
 #  include "MacIOSurfaceImage.h"
 #  include "mozilla/gfx/QuartzSupport.h"
 #endif
@@ -169,16 +170,23 @@ void ImageContainer::EnsureImageClient()
     } else {
       // It's okay to drop the async container handle since the ImageBridgeChild
       // is going to die anyway.
       mAsyncContainerHandle = CompositableHandle();
     }
   }
 }
 
+SharedSurfacesAnimation* ImageContainer::EnsureSharedSurfacesAnimation() {
+  if (!mSharedAnimation) {
+    mSharedAnimation = new SharedSurfacesAnimation();
+  }
+  return mSharedAnimation;
+}
+
 ImageContainer::ImageContainer(Mode flag)
     : mRecursiveMutex("ImageContainer.mRecursiveMutex"),
       mGenerationCounter(++sGenerationCounter),
       mPaintCount(0),
       mDroppedImageCount(0),
       mImageFactory(new ImageFactory()),
       mRecycleBin(new BufferRecycleBin()),
       mIsAsync(flag == ASYNCHRONOUS),
@@ -207,16 +215,19 @@ ImageContainer::~ImageContainer() {
     mNotifyCompositeListener->ClearImageContainer();
   }
   if (mAsyncContainerHandle) {
     if (RefPtr<ImageBridgeChild> imageBridge =
             ImageBridgeChild::GetSingleton()) {
       imageBridge->ForgetImageContainer(mAsyncContainerHandle);
     }
   }
+  if (mSharedAnimation) {
+    mSharedAnimation->Destroy();
+  }
 }
 
 Maybe<SurfaceDescriptor> Image::GetDesc() { return {}; }
 
 Maybe<SurfaceDescriptor> Image::GetDescFromTexClient(
     TextureClient* const forTc) {
   RefPtr<TextureClient> tc = forTc;
   if (!forTc) {
--- a/gfx/layers/ImageContainer.h
+++ b/gfx/layers/ImageContainer.h
@@ -43,16 +43,17 @@ namespace mozilla {
 
 namespace layers {
 
 class ImageClient;
 class ImageCompositeNotification;
 class ImageContainer;
 class ImageContainerChild;
 class SharedPlanarYCbCrImage;
+class SharedSurfacesAnimation;
 class SurfaceDescriptor;
 class PlanarYCbCrImage;
 class TextureClient;
 class TextureClientRecycleAllocator;
 class KnowsCompositor;
 class NVImage;
 class MemoryOrShmem;
 #ifdef XP_WIN
@@ -523,16 +524,22 @@ class ImageContainer final : public Supp
 
   /**
    * Main thread only.
    */
   static ProducerID AllocateProducerID();
 
   void DropImageClient();
 
+  SharedSurfacesAnimation* GetSharedSurfacesAnimation() const {
+    return mSharedAnimation;
+  }
+
+  SharedSurfacesAnimation* EnsureSharedSurfacesAnimation();
+
  private:
   typedef mozilla::RecursiveMutex RecursiveMutex;
 
   void SetCurrentImageInternal(const nsTArray<NonOwningImage>& aImages);
 
   // This is called to ensure we have an active image, this may not be true
   // when we're storing image information in a RemoteImageData structure.
   // NOTE: If we have remote data mRemoteDataMutex should be locked when
@@ -587,16 +594,18 @@ class ImageContainer final : public Supp
   // 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.
   RefPtr<ImageClient> mImageClient;
 
+  RefPtr<SharedSurfacesAnimation> mSharedAnimation;
+
   bool mIsAsync;
   CompositableHandle mAsyncContainerHandle;
 
   // ProducerID for last current image(s)
   ProducerID mCurrentProducerID;
 
   RefPtr<ImageContainerListener> mNotifyCompositeListener;
 
--- a/gfx/layers/ipc/SharedSurfacesChild.cpp
+++ b/gfx/layers/ipc/SharedSurfacesChild.cpp
@@ -3,16 +3,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 "SharedSurfacesChild.h"
 #include "SharedSurfacesParent.h"
 #include "CompositorManagerChild.h"
 #include "mozilla/gfx/gfxVars.h"
+#include "mozilla/image/SourceSurfaceBlobImage.h"
 #include "mozilla/layers/IpcResourceUpdateQueue.h"
 #include "mozilla/layers/SourceSurfaceSharedData.h"
 #include "mozilla/layers/WebRenderBridgeChild.h"
 #include "mozilla/layers/RenderRootStateManager.h"
 #include "mozilla/SchedulerGroup.h"
 #include "mozilla/StaticPrefs_image.h"
 
 namespace mozilla {
@@ -319,16 +320,100 @@ nsresult SharedSurfacesChild::Share(Sour
   if (!sharedSurface) {
     return NS_ERROR_NOT_IMPLEMENTED;
   }
 
   return Share(sharedSurface, aManager, aResources, aKey);
 }
 
 /* static */
+nsresult SharedSurfacesChild::Share(ImageContainer* aContainer,
+                                    RenderRootStateManager* aManager,
+                                    wr::IpcResourceUpdateQueue& aResources,
+                                    wr::ImageKey& aKey,
+                                    ContainerProducerID aProducerId) {
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(aContainer);
+  MOZ_ASSERT(aManager);
+
+  if (aContainer->IsAsync()) {
+    return NS_ERROR_NOT_IMPLEMENTED;
+  }
+
+  AutoTArray<ImageContainer::OwningImage, 4> images;
+  aContainer->GetCurrentImages(&images);
+  if (images.IsEmpty()) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
+  if (aProducerId != kContainerProducerID_Invalid &&
+      images[0].mProducerID != aProducerId) {
+    // If the producer ID of the surface in the container does not match the
+    // expected producer ID, then we do not want to proceed with sharing. This
+    // is useful for when callers are unsure if given container is for the same
+    // producer / underlying image request.
+    return NS_ERROR_FAILURE;
+  }
+
+  RefPtr<gfx::SourceSurface> surface = images[0].mImage->GetAsSourceSurface();
+  if (!surface) {
+    return NS_ERROR_NOT_IMPLEMENTED;
+  }
+
+  auto sharedSurface = AsSourceSurfaceSharedData(surface);
+  if (!sharedSurface) {
+    return NS_ERROR_NOT_IMPLEMENTED;
+  }
+
+  SharedSurfacesAnimation* anim = aContainer->GetSharedSurfacesAnimation();
+  if (anim) {
+    return anim->UpdateKey(sharedSurface, aManager, aResources, aKey);
+  }
+
+  return Share(sharedSurface, aManager, aResources, aKey);
+}
+
+/* static */
+nsresult SharedSurfacesChild::ShareBlob(ImageContainer* aContainer,
+                                        RenderRootStateManager* aManager,
+                                        wr::IpcResourceUpdateQueue& aResources,
+                                        wr::BlobImageKey& aKey) {
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(aContainer);
+  MOZ_ASSERT(aManager);
+
+  if (aContainer->IsAsync()) {
+    return NS_ERROR_NOT_IMPLEMENTED;
+  }
+
+  AutoTArray<ImageContainer::OwningImage, 4> images;
+  aContainer->GetCurrentImages(&images);
+  if (images.IsEmpty()) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
+  RefPtr<gfx::SourceSurface> surface = images[0].mImage->GetAsSourceSurface();
+  if (!surface || surface->GetType() != SurfaceType::BLOB_IMAGE) {
+    return NS_ERROR_NOT_IMPLEMENTED;
+  }
+
+  auto* blobSurface =
+      static_cast<image::SourceSurfaceBlobImage*>(surface.get());
+
+  Maybe<wr::BlobImageKey> key =
+      blobSurface->UpdateKey(aManager->LayerManager(), aResources);
+  if (!key) {
+    return NS_ERROR_FAILURE;
+  }
+
+  aKey = key.value();
+  return NS_OK;
+}
+
+/* static */
 nsresult SharedSurfacesChild::Share(SourceSurface* aSurface,
                                     wr::ExternalImageId& aId) {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aSurface);
 
   auto sharedSurface = AsSourceSurfaceSharedData(aSurface);
   if (!sharedSurface) {
     return NS_ERROR_NOT_IMPLEMENTED;
@@ -387,16 +472,38 @@ void SharedSurfacesChild::Unshare(const 
       static_cast<SharedUserData*>(aSurface->GetUserData(&sSharedKey));
   if (!data || !data->IsShared()) {
     return Nothing();
   }
 
   return Some(data->Id());
 }
 
+/* static */
+nsresult SharedSurfacesChild::UpdateAnimation(ImageContainer* aContainer,
+                                              SourceSurface* aSurface,
+                                              const IntRect& aDirtyRect) {
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(aContainer);
+  MOZ_ASSERT(!aContainer->IsAsync());
+  MOZ_ASSERT(aSurface);
+
+  // If we aren't using shared surfaces, then is nothing to do.
+  auto sharedSurface = SharedSurfacesChild::AsSourceSurfaceSharedData(aSurface);
+  if (!sharedSurface) {
+    MOZ_ASSERT(!aContainer->GetSharedSurfacesAnimation());
+    return NS_ERROR_NOT_IMPLEMENTED;
+  }
+
+  SharedSurfacesAnimation* anim = aContainer->EnsureSharedSurfacesAnimation();
+  MOZ_ASSERT(anim);
+
+  return anim->SetCurrentFrame(sharedSurface, aDirtyRect);
+}
+
 AnimationImageKeyData::AnimationImageKeyData(RenderRootStateManager* aManager,
                                              const wr::ImageKey& aImageKey)
     : SharedSurfacesChild::ImageKeyData(aManager, aImageKey) {}
 
 AnimationImageKeyData::AnimationImageKeyData(AnimationImageKeyData&& aOther)
     : SharedSurfacesChild::ImageKeyData(std::move(aOther)),
       mPendingRelease(std::move(aOther.mPendingRelease)) {}
 
--- a/gfx/layers/ipc/SharedSurfacesChild.h
+++ b/gfx/layers/ipc/SharedSurfacesChild.h
@@ -39,16 +39,17 @@ class SourceSurfaceSharedData;
 
 namespace wr {
 class IpcResourceUpdateQueue;
 }  // namespace wr
 
 namespace layers {
 
 class CompositorManagerChild;
+class ImageContainer;
 class RenderRootStateManager;
 
 class SharedSurfacesChild {
  public:
   /**
    * Request that the surface be mapped into the compositor thread's memory
    * space. This is useful for when the caller itself has no present need for
    * the surface to be mapped, but knows there will be such a need in the
@@ -80,29 +81,50 @@ class SharedSurfacesChild {
    * This must be called from the main thread.
    */
   static nsresult Share(gfx::SourceSurface* aSurface,
                         RenderRootStateManager* aManager,
                         wr::IpcResourceUpdateQueue& aResources,
                         wr::ImageKey& aKey);
 
   /**
+   * Request that the first surface in the image container's current images be
+   * mapped into the compositor thread's memory space, and a valid ImageKey be
+   * generated for it for use with WebRender. If a different method should be
+   * used to share the image data for this particular container, it will return
+   * NS_ERROR_NOT_IMPLEMENTED. This must be called from the main thread.
+   */
+  static nsresult Share(ImageContainer* aContainer,
+                        RenderRootStateManager* aManager,
+                        wr::IpcResourceUpdateQueue& aResources,
+                        wr::ImageKey& aKey, ContainerProducerID aProducerId);
+
+  static nsresult ShareBlob(ImageContainer* aContainer,
+                            RenderRootStateManager* aManager,
+                            wr::IpcResourceUpdateQueue& aResources,
+                            wr::BlobImageKey& aKey);
+
+  /**
    * Get the external ID, if any, bound to the shared surface. Used for memory
    * reporting purposes.
    */
   static Maybe<wr::ExternalImageId> GetExternalId(
       const gfx::SourceSurfaceSharedData* aSurface);
 
   /**
    * Get the surface (or its underlying surface) as a SourceSurfaceSharedData
    * pointer, if valid.
    */
   static gfx::SourceSurfaceSharedData* AsSourceSurfaceSharedData(
       gfx::SourceSurface* aSurface);
 
+  static nsresult UpdateAnimation(ImageContainer* aContainer,
+                                  gfx::SourceSurface* aSurface,
+                                  const gfx::IntRect& aDirtyRect);
+
   class ImageKeyData {
    public:
     ImageKeyData(RenderRootStateManager* aManager,
                  const wr::ImageKey& aImageKey);
     virtual ~ImageKeyData();
 
     ImageKeyData(ImageKeyData&& aOther);
     ImageKeyData& operator=(ImageKeyData&& aOther);
--- a/gfx/layers/wr/WebRenderCommandBuilder.cpp
+++ b/gfx/layers/wr/WebRenderCommandBuilder.cpp
@@ -12,17 +12,16 @@
 #include "mozilla/EffectCompositor.h"
 #include "mozilla/ProfilerLabels.h"
 #include "mozilla/StaticPrefs_gfx.h"
 #include "mozilla/SVGGeometryFrame.h"
 #include "mozilla/UniquePtr.h"
 #include "mozilla/gfx/2D.h"
 #include "mozilla/gfx/Logging.h"
 #include "mozilla/gfx/Types.h"
-#include "mozilla/image/WebRenderImageProvider.h"
 #include "mozilla/layers/AnimationHelper.h"
 #include "mozilla/layers/ClipManager.h"
 #include "mozilla/layers/ImageClient.h"
 #include "mozilla/layers/RenderRootStateManager.h"
 #include "mozilla/layers/WebRenderBridgeChild.h"
 #include "mozilla/layers/WebRenderLayerManager.h"
 #include "mozilla/layers/IpcResourceUpdateQueue.h"
 #include "mozilla/layers/SharedSurfacesChild.h"
@@ -36,17 +35,16 @@
 #include "nsLayoutUtils.h"
 #include "nsTHashSet.h"
 #include "WebRenderCanvasRenderer.h"
 
 namespace mozilla {
 namespace layers {
 
 using namespace gfx;
-using namespace image;
 static int sIndent;
 #include <stdarg.h>
 #include <stdio.h>
 
 static void GP(const char* fmt, ...) {
   va_list args;
   va_start(args, fmt);
 #if 0
@@ -2006,40 +2004,43 @@ bool WebRenderCommandBuilder::PushImage(
 
   auto r = wr::ToLayoutRect(aRect);
   auto c = wr::ToLayoutRect(aClip);
   aBuilder.PushImage(r, c, !aItem->BackfaceIsHidden(), rendering, key.value());
 
   return true;
 }
 
-Maybe<wr::ImageKey> WebRenderCommandBuilder::CreateImageProviderKey(
-    nsDisplayItem* aItem, image::WebRenderImageProvider* aProvider,
+Maybe<wr::BlobImageKey> WebRenderCommandBuilder::CreateBlobImageKey(
+    nsDisplayItem* aItem, ImageContainer* aContainer,
     mozilla::wr::IpcResourceUpdateQueue& aResources) {
-  RefPtr<WebRenderImageProviderData> imageData =
-      CreateOrRecycleWebRenderUserData<WebRenderImageProviderData>(aItem);
+  MOZ_ASSERT(!aContainer->IsAsync());
+
+  RefPtr<WebRenderBlobImageData> imageData =
+      CreateOrRecycleWebRenderUserData<WebRenderBlobImageData>(aItem);
   MOZ_ASSERT(imageData);
-  return imageData->UpdateImageKey(aProvider, aResources);
+  return imageData->UpdateImageKey(aContainer, aResources);
 }
 
-bool WebRenderCommandBuilder::PushImageProvider(
-    nsDisplayItem* aItem, image::WebRenderImageProvider* aProvider,
+bool WebRenderCommandBuilder::PushBlobImage(
+    nsDisplayItem* aItem, ImageContainer* aContainer,
     mozilla::wr::DisplayListBuilder& aBuilder,
     mozilla::wr::IpcResourceUpdateQueue& aResources,
     const LayoutDeviceRect& aRect, const LayoutDeviceRect& aClip) {
-  Maybe<wr::ImageKey> key =
-      CreateImageProviderKey(aItem, aProvider, aResources);
+  Maybe<wr::BlobImageKey> key =
+      CreateBlobImageKey(aItem, aContainer, aResources);
   if (!key) {
     return false;
   }
 
   auto rendering = wr::ToImageRendering(aItem->Frame()->UsedImageRendering());
   auto r = wr::ToLayoutRect(aRect);
   auto c = wr::ToLayoutRect(aClip);
-  aBuilder.PushImage(r, c, !aItem->BackfaceIsHidden(), rendering, key.value());
+  aBuilder.PushImage(r, c, !aItem->BackfaceIsHidden(), rendering,
+                     wr::AsImageKey(key.value()));
 
   return true;
 }
 
 static void PaintItemByDrawTarget(nsDisplayItem* aItem, gfx::DrawTarget* aDT,
                                   const LayoutDevicePoint& aOffset,
                                   const IntRect& visibleRect,
                                   nsDisplayListBuilder* aDisplayListBuilder,
--- a/gfx/layers/wr/WebRenderCommandBuilder.h
+++ b/gfx/layers/wr/WebRenderCommandBuilder.h
@@ -16,20 +16,16 @@
 #include "mozilla/SVGIntegrationUtils.h"  // for WrFiltersHolder
 #include "nsDisplayList.h"
 #include "nsIFrame.h"
 #include "nsTHashSet.h"
 #include "DisplayItemCache.h"
 
 namespace mozilla {
 
-namespace image {
-class WebRenderImageProvider;
-}
-
 namespace layers {
 
 class ImageClient;
 class ImageContainer;
 class WebRenderBridgeChild;
 class WebRenderCanvasData;
 class WebRenderCanvasRendererAsync;
 class WebRenderImageData;
@@ -64,36 +60,35 @@ class WebRenderCommandBuilder final {
 
   Maybe<wr::ImageKey> CreateImageKey(
       nsDisplayItem* aItem, ImageContainer* aContainer,
       mozilla::wr::DisplayListBuilder& aBuilder,
       mozilla::wr::IpcResourceUpdateQueue& aResources,
       mozilla::wr::ImageRendering aRendering, const StackingContextHelper& aSc,
       gfx::IntSize& aSize, const Maybe<LayoutDeviceRect>& aAsyncImageBounds);
 
-  Maybe<wr::ImageKey> CreateImageProviderKey(
-      nsDisplayItem* aItem, image::WebRenderImageProvider* aProvider,
+  Maybe<wr::BlobImageKey> CreateBlobImageKey(
+      nsDisplayItem* aItem, ImageContainer* aContainer,
       mozilla::wr::IpcResourceUpdateQueue& aResources);
 
   WebRenderUserDataRefTable* GetWebRenderUserDataTable() {
     return &mWebRenderUserDatas;
   }
 
   bool PushImage(nsDisplayItem* aItem, ImageContainer* aContainer,
                  mozilla::wr::DisplayListBuilder& aBuilder,
                  mozilla::wr::IpcResourceUpdateQueue& aResources,
                  const StackingContextHelper& aSc,
                  const LayoutDeviceRect& aRect, const LayoutDeviceRect& aClip);
 
-  bool PushImageProvider(nsDisplayItem* aItem,
-                         image::WebRenderImageProvider* aProvider,
-                         mozilla::wr::DisplayListBuilder& aBuilder,
-                         mozilla::wr::IpcResourceUpdateQueue& aResources,
-                         const LayoutDeviceRect& aRect,
-                         const LayoutDeviceRect& aClip);
+  bool PushBlobImage(nsDisplayItem* aItem, ImageContainer* aContainer,
+                     mozilla::wr::DisplayListBuilder& aBuilder,
+                     mozilla::wr::IpcResourceUpdateQueue& aResources,
+                     const LayoutDeviceRect& aRect,
+                     const LayoutDeviceRect& aClip);
 
   Maybe<wr::ImageMask> BuildWrMaskImage(
       nsDisplayMasksAndClipPaths* aMaskItem, wr::DisplayListBuilder& aBuilder,
       wr::IpcResourceUpdateQueue& aResources, const StackingContextHelper& aSc,
       nsDisplayListBuilder* aDisplayListBuilder,
       const LayoutDeviceRect& aBounds);
 
   bool PushItemAsImage(nsDisplayItem* aItem, wr::DisplayListBuilder& aBuilder,
--- a/gfx/layers/wr/WebRenderUserData.cpp
+++ b/gfx/layers/wr/WebRenderUserData.cpp
@@ -1,32 +1,29 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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 "WebRenderUserData.h"
 
-#include "mozilla/image/WebRenderImageProvider.h"
 #include "mozilla/layers/AnimationHelper.h"
 #include "mozilla/layers/CompositorBridgeChild.h"
 #include "mozilla/layers/ImageClient.h"
 #include "mozilla/layers/WebRenderBridgeChild.h"
 #include "mozilla/layers/RenderRootStateManager.h"
 #include "mozilla/layers/WebRenderMessages.h"
 #include "mozilla/layers/IpcResourceUpdateQueue.h"
 #include "mozilla/layers/SharedSurfacesChild.h"
 #include "mozilla/webgpu/WebGPUChild.h"
 #include "nsDisplayListInvalidation.h"
 #include "nsIFrame.h"
 #include "WebRenderCanvasRenderer.h"
 
-using namespace mozilla::image;
-
 namespace mozilla {
 namespace layers {
 
 void WebRenderBackgroundData::AddWebRenderCommands(
     wr::DisplayListBuilder& aBuilder) {
   aBuilder.PushRect(mBounds, mBounds, true, mColor);
 }
 
@@ -40,37 +37,36 @@ bool WebRenderUserData::SupportsAsyncUpd
   if (data) {
     return data->IsAsync();
   }
 
   return false;
 }
 
 /* static */
-bool WebRenderUserData::ProcessInvalidateForImage(nsIFrame* aFrame,
-                                                  DisplayItemType aType,
-                                                  ImageProviderId aProviderId) {
+bool WebRenderUserData::ProcessInvalidateForImage(
+    nsIFrame* aFrame, DisplayItemType aType, ContainerProducerID aProducerId) {
   MOZ_ASSERT(aFrame);
 
   if (!aFrame->HasProperty(WebRenderUserDataProperty::Key())) {
     return false;
   }
 
   auto type = static_cast<uint32_t>(aType);
   RefPtr<WebRenderFallbackData> fallback =
       GetWebRenderUserData<WebRenderFallbackData>(aFrame, type);
   if (fallback) {
     fallback->SetInvalid(true);
     aFrame->SchedulePaint();
     return true;
   }
 
-  RefPtr<WebRenderImageProviderData> image =
-      GetWebRenderUserData<WebRenderImageProviderData>(aFrame, type);
-  if (image && image->Invalidate(aProviderId)) {
+  RefPtr<WebRenderImageData> image =
+      GetWebRenderUserData<WebRenderImageData>(aFrame, type);
+  if (image && image->UsingSharedSurface(aProducerId)) {
     return true;
   }
 
   aFrame->SchedulePaint();
   return false;
 }
 
 WebRenderUserData::WebRenderUserData(RenderRootStateManager* aManager,
@@ -94,52 +90,92 @@ WebRenderUserData::~WebRenderUserData() 
 void WebRenderUserData::RemoveFromTable() { mTable->Remove(this); }
 
 WebRenderBridgeChild* WebRenderUserData::WrBridge() const {
   return mManager->WrBridge();
 }
 
 WebRenderImageData::WebRenderImageData(RenderRootStateManager* aManager,
                                        nsDisplayItem* aItem)
-    : WebRenderUserData(aManager, aItem) {}
+    : WebRenderUserData(aManager, aItem), mOwnsKey(false) {}
 
 WebRenderImageData::WebRenderImageData(RenderRootStateManager* aManager,
                                        uint32_t aDisplayItemKey,
                                        nsIFrame* aFrame)
-    : WebRenderUserData(aManager, aDisplayItemKey, aFrame) {}
+    : WebRenderUserData(aManager, aDisplayItemKey, aFrame), mOwnsKey(false) {}
 
 WebRenderImageData::~WebRenderImageData() {
   ClearImageKey();
 
   if (mPipelineId) {
     mManager->RemovePipelineIdForCompositable(mPipelineId.ref());
   }
 }
 
+bool WebRenderImageData::UsingSharedSurface(
+    ContainerProducerID aProducerId) const {
+  if (!mContainer || !mKey || mOwnsKey) {
+    return false;
+  }
+
+  // If this is just an update with the same image key, then we know that the
+  // share request initiated an asynchronous update so that we don't need to
+  // rebuild the scene.
+  wr::ImageKey key;
+  nsresult rv = SharedSurfacesChild::Share(
+      mContainer, mManager, mManager->AsyncResourceUpdates(), key, aProducerId);
+  return NS_SUCCEEDED(rv) && mKey.ref() == key;
+}
+
 void WebRenderImageData::ClearImageKey() {
   if (mKey) {
-    mManager->AddImageKeyForDiscard(mKey.value());
-    if (mTextureOfImage) {
-      WrBridge()->ReleaseTextureOfImage(mKey.value());
-      mTextureOfImage = nullptr;
+    // If we don't own the key, then the owner is responsible for discarding the
+    // key when appropriate.
+    if (mOwnsKey) {
+      mManager->AddImageKeyForDiscard(mKey.value());
+      if (mTextureOfImage) {
+        WrBridge()->ReleaseTextureOfImage(mKey.value());
+        mTextureOfImage = nullptr;
+      }
     }
     mKey.reset();
   }
+  mOwnsKey = false;
   MOZ_ASSERT(!mTextureOfImage);
 }
 
 Maybe<wr::ImageKey> WebRenderImageData::UpdateImageKey(
     ImageContainer* aContainer, wr::IpcResourceUpdateQueue& aResources,
     bool aFallback) {
   MOZ_ASSERT(aContainer);
 
   if (mContainer != aContainer) {
     mContainer = aContainer;
   }
 
+  wr::WrImageKey key;
+  if (!aFallback) {
+    nsresult rv = SharedSurfacesChild::Share(aContainer, mManager, aResources,
+                                             key, kContainerProducerID_Invalid);
+    if (NS_SUCCEEDED(rv)) {
+      // Ensure that any previously owned keys are released before replacing. We
+      // don't own this key, the surface itself owns it, so that it can be
+      // shared across multiple elements.
+      ClearImageKey();
+      mKey = Some(key);
+      return mKey;
+    }
+
+    if (rv != NS_ERROR_NOT_IMPLEMENTED) {
+      // We should be using the shared surface but somehow sharing it failed.
+      ClearImageKey();
+      return Nothing();
+    }
+  }
+
   CreateImageClientIfNeeded();
   if (!mImageClient) {
     return Nothing();
   }
 
   MOZ_ASSERT(mImageClient->AsImageClientSingle());
 
   ImageClientSingle* imageClient = mImageClient->AsImageClientSingle();
@@ -170,23 +206,25 @@ Maybe<wr::ImageKey> WebRenderImageData::
 
   if (useUpdate) {
     MOZ_ASSERT(mKey.isSome());
     MOZ_ASSERT(mTextureOfImage);
     aResources.PushExternalImageForTexture(
         extId.ref(), mKey.ref(), currentTexture, /* aIsUpdate */ true);
   } else {
     ClearImageKey();
-    wr::WrImageKey key = WrBridge()->GetNextImageKey();
+    key = WrBridge()->GetNextImageKey();
     aResources.PushExternalImageForTexture(extId.ref(), key, currentTexture,
                                            /* aIsUpdate */ false);
     mKey = Some(key);
   }
 
   mTextureOfImage = currentTexture;
+  mOwnsKey = true;
+
   return mKey;
 }
 
 already_AddRefed<ImageClient> WebRenderImageData::GetImageClient() {
   RefPtr<ImageClient> imageClient = mImageClient;
   return imageClient.forget();
 }
 
@@ -238,53 +276,43 @@ void WebRenderImageData::CreateImageClie
     if (!mImageClient) {
       return;
     }
 
     mImageClient->Connect();
   }
 }
 
-WebRenderImageProviderData::WebRenderImageProviderData(
-    RenderRootStateManager* aManager, nsDisplayItem* aItem)
+WebRenderBlobImageData::WebRenderBlobImageData(RenderRootStateManager* aManager,
+                                               nsDisplayItem* aItem)
     : WebRenderUserData(aManager, aItem) {}
 
-WebRenderImageProviderData::WebRenderImageProviderData(
-    RenderRootStateManager* aManager, uint32_t aDisplayItemKey,
-    nsIFrame* aFrame)
+WebRenderBlobImageData::WebRenderBlobImageData(RenderRootStateManager* aManager,
+                                               uint32_t aDisplayItemKey,
+                                               nsIFrame* aFrame)
     : WebRenderUserData(aManager, aDisplayItemKey, aFrame) {}
 
-WebRenderImageProviderData::~WebRenderImageProviderData() = default;
+Maybe<wr::BlobImageKey> WebRenderBlobImageData::UpdateImageKey(
+    ImageContainer* aContainer, wr::IpcResourceUpdateQueue& aResources) {
+  MOZ_ASSERT(aContainer);
 
-Maybe<wr::ImageKey> WebRenderImageProviderData::UpdateImageKey(
-    WebRenderImageProvider* aProvider, wr::IpcResourceUpdateQueue& aResources) {
-  MOZ_ASSERT(aProvider);
-
-  if (mProvider != aProvider) {
-    mProvider = aProvider;
+  if (mContainer != aContainer) {
+    mContainer = aContainer;
   }
 
-  wr::ImageKey key = {};
-  nsresult rv = mProvider->UpdateKey(mManager, aResources, key);
-  if (NS_FAILED(rv)) {
-    return Nothing();
+  wr::BlobImageKey key = {};
+  nsresult rv =
+      SharedSurfacesChild::ShareBlob(aContainer, mManager, aResources, key);
+  if (NS_SUCCEEDED(rv)) {
+    mKey = Some(key);
+  } else {
+    mKey.reset();
   }
 
-  return Some(key);
-}
-
-bool WebRenderImageProviderData::Invalidate(ImageProviderId aProviderId) const {
-  if (!aProviderId || mProvider->GetProviderId() != aProviderId) {
-    return false;
-  }
-
-  wr::ImageKey key = {};
-  nsresult rv =
-      mProvider->UpdateKey(mManager, mManager->AsyncResourceUpdates(), key);
-  return NS_SUCCEEDED(rv);
+  return mKey;
 }
 
 WebRenderFallbackData::WebRenderFallbackData(RenderRootStateManager* aManager,
                                              nsDisplayItem* aItem)
     : WebRenderUserData(aManager, aItem), mOpacity(1.0f), mInvalid(false) {}
 
 WebRenderFallbackData::~WebRenderFallbackData() { ClearImageKey(); }
 
--- a/gfx/layers/wr/WebRenderUserData.h
+++ b/gfx/layers/wr/WebRenderUserData.h
@@ -4,17 +4,16 @@
  * 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 GFX_WEBRENDERUSERDATA_H
 #define GFX_WEBRENDERUSERDATA_H
 
 #include <vector>
 #include "mozilla/webrender/WebRenderAPI.h"
-#include "mozilla/image/WebRenderImageProvider.h"
 #include "mozilla/layers/AnimationInfo.h"
 #include "mozilla/dom/RemoteBrowser.h"
 #include "mozilla/UniquePtr.h"
 #include "nsIFrame.h"
 #include "nsRefPtrHashtable.h"
 #include "nsTHashSet.h"
 #include "ImageTypes.h"
 #include "DisplayItemClip.h"
@@ -40,17 +39,16 @@ namespace layers {
 class BasicLayerManager;
 class ImageClient;
 class ImageContainer;
 class WebRenderBridgeChild;
 class WebRenderCanvasData;
 class WebRenderCanvasRenderer;
 class WebRenderCanvasRendererAsync;
 class WebRenderImageData;
-class WebRenderImageProviderData;
 class WebRenderFallbackData;
 class WebRenderLocalCanvasData;
 class RenderRootStateManager;
 class WebRenderGroupData;
 
 class WebRenderBackgroundData {
  public:
   WebRenderBackgroundData(wr::LayoutRect aBounds, wr::ColorF aColor)
@@ -66,42 +64,41 @@ class WebRenderBackgroundData {
 /// to an nsFrame.
 class WebRenderUserData {
  public:
   typedef nsTHashSet<RefPtr<WebRenderUserData>> WebRenderUserDataRefTable;
 
   static bool SupportsAsyncUpdate(nsIFrame* aFrame);
 
   static bool ProcessInvalidateForImage(nsIFrame* aFrame, DisplayItemType aType,
-                                        image::ImageProviderId aProviderId);
+                                        ContainerProducerID aProducerId);
 
   NS_INLINE_DECL_REFCOUNTING(WebRenderUserData)
 
   WebRenderUserData(RenderRootStateManager* aManager, nsDisplayItem* aItem);
   WebRenderUserData(RenderRootStateManager* aManager, uint32_t mDisplayItemKey,
                     nsIFrame* aFrame);
 
   virtual WebRenderImageData* AsImageData() { return nullptr; }
-  virtual WebRenderImageProviderData* AsImageProviderData() { return nullptr; }
   virtual WebRenderFallbackData* AsFallbackData() { return nullptr; }
   virtual WebRenderCanvasData* AsCanvasData() { return nullptr; }
   virtual WebRenderLocalCanvasData* AsLocalCanvasData() { return nullptr; }
   virtual WebRenderGroupData* AsGroupData() { return nullptr; }
 
   enum class UserDataType {
     eImage,
     eFallback,
     eAPZAnimation,
     eAnimation,
     eCanvas,
     eLocalCanvas,
     eRemote,
     eGroup,
     eMask,
-    eImageProvider,  // ImageLib
+    eBlobImage,  // SVG image
   };
 
   virtual UserDataType GetType() = 0;
   bool IsUsed() { return mUsed; }
   void SetUsed(bool aUsed) { mUsed = aUsed; }
   nsIFrame* GetFrame() { return mFrame; }
   uint32_t GetDisplayItemKey() { return mDisplayItemKey; }
   void RemoveFromTable();
@@ -167,47 +164,53 @@ class WebRenderImageData : public WebRen
       const LayoutDeviceRect& aSCBounds, VideoInfo::Rotation aRotation,
       const wr::ImageRendering& aFilter, const wr::MixBlendMode& aMixBlendMode,
       bool aIsBackfaceVisible);
 
   void CreateImageClientIfNeeded();
 
   bool IsAsync() { return mPipelineId.isSome(); }
 
+  bool UsingSharedSurface(ContainerProducerID aProducerId) const;
+
   void ClearImageKey();
 
  protected:
   Maybe<wr::ImageKey> mKey;
   RefPtr<TextureClient> mTextureOfImage;
   RefPtr<ImageClient> mImageClient;
   Maybe<wr::PipelineId> mPipelineId;
   RefPtr<ImageContainer> mContainer;
+  // The key can be owned by a shared surface that is used by several elements.
+  // when this is the case the shared surface is responsible for managing the
+  // destruction of the key.
+  // TODO: we surely can come up with a simpler/safer way to model this.
+  bool mOwnsKey;
 };
 
-/// Holds some data used to share ImageLib results with the parent process.
-/// This may be either in the form of a blob recording or a rasterized surface.
-class WebRenderImageProviderData final : public WebRenderUserData {
+/// Holds some data used to share blob recordings from VectorImages with the
+/// parent process.
+class WebRenderBlobImageData : public WebRenderUserData {
  public:
-  WebRenderImageProviderData(RenderRootStateManager* aManager,
-                             nsDisplayItem* aItem);
-  WebRenderImageProviderData(RenderRootStateManager* aManager,
-                             uint32_t aDisplayItemKey, nsIFrame* aFrame);
-  ~WebRenderImageProviderData() override;
+  WebRenderBlobImageData(RenderRootStateManager* aManager,
+                         nsDisplayItem* aItem);
+  WebRenderBlobImageData(RenderRootStateManager* aManager,
+                         uint32_t aDisplayItemKey, nsIFrame* aFrame);
+  virtual ~WebRenderBlobImageData() {}
 
-  WebRenderImageProviderData* AsImageProviderData() override { return this; }
-  UserDataType GetType() override { return UserDataType::eImageProvider; }
-  static UserDataType Type() { return UserDataType::eImageProvider; }
+  UserDataType GetType() override { return UserDataType::eBlobImage; }
+  static UserDataType Type() { return UserDataType::eBlobImage; }
+  Maybe<wr::BlobImageKey> GetImageKey() { return mKey; }
 
-  Maybe<wr::ImageKey> UpdateImageKey(image::WebRenderImageProvider* aProvider,
-                                     wr::IpcResourceUpdateQueue& aResources);
-
-  bool Invalidate(image::ImageProviderId aProviderId) const;
+  Maybe<wr::BlobImageKey> UpdateImageKey(
+      ImageContainer* aContainer, wr::IpcResourceUpdateQueue& aResources);
 
  protected:
-  RefPtr<image::WebRenderImageProvider> mProvider;
+  Maybe<wr::BlobImageKey> mKey;
+  RefPtr<ImageContainer> mContainer;
 };
 
 /// Used for fallback rendering.
 ///
 /// In most cases this uses blob images but it can also render on the content
 /// side directly into a texture.
 class WebRenderFallbackData : public WebRenderUserData {
  public:
--- a/image/AnimationFrameBuffer.cpp
+++ b/image/AnimationFrameBuffer.cpp
@@ -55,17 +55,16 @@ bool AnimationFrameRetainedBuffer::Reset
   // Either the decoder is still running, or we have enough frames already.
   // No need for us to restart it.
   return false;
 }
 
 bool AnimationFrameRetainedBuffer::MarkComplete(
     const gfx::IntRect& aFirstFrameRefreshArea) {
   MOZ_ASSERT(!mSizeKnown);
-  mFirstFrameRefreshArea = aFirstFrameRefreshArea;
   mSizeKnown = true;
   mPending = 0;
   mFrames.Compact();
   return false;
 }
 
 void AnimationFrameRetainedBuffer::AdvanceInternal() {
   // We should not have advanced if we never inserted.
@@ -188,21 +187,16 @@ bool AnimationFrameDiscardingQueue::Rese
 
 bool AnimationFrameDiscardingQueue::MarkComplete(
     const gfx::IntRect& aFirstFrameRefreshArea) {
   if (NS_WARN_IF(mInsertIndex != mSize)) {
     mRedecodeError = true;
     mPending = 0;
   }
 
-  // If we encounter a redecode error, just make the first frame refresh area to
-  // be the full frame, because we don't really know what we can safely recycle.
-  mFirstFrameRefreshArea =
-      mRedecodeError ? mFirstFrame->GetRect() : aFirstFrameRefreshArea;
-
   // We reached the end of the animation, the next frame we get, if we get
   // another, will be the first frame again.
   mInsertIndex = 0;
   mSizeKnown = true;
 
   // Since we only request advancing when we want to resume at a certain point
   // in the animation, we should never exceed the number of frames.
   MOZ_ASSERT(mAdvance == 0);
@@ -462,10 +456,22 @@ RawAccessFrameRef AnimationFrameRecyclin
     }
   } else {
     mRecycle.pop_front();
   }
 
   return recycledFrame;
 }
 
+bool AnimationFrameRecyclingQueue::MarkComplete(
+    const gfx::IntRect& aFirstFrameRefreshArea) {
+  bool continueDecoding =
+      AnimationFrameDiscardingQueue::MarkComplete(aFirstFrameRefreshArea);
+
+  // If we encounter a redecode error, just make the first frame refresh area to
+  // be the full frame, because we don't really know what we can safely recycle.
+  mFirstFrameRefreshArea =
+      mRedecodeError ? mFirstFrame->GetRect() : aFirstFrameRefreshArea;
+  return continueDecoding;
+}
+
 }  // namespace image
 }  // namespace mozilla
--- a/image/AnimationFrameBuffer.h
+++ b/image/AnimationFrameBuffer.h
@@ -99,24 +99,16 @@ class AnimationFrameBuffer {
   /**
    * @returns The total number of frames in the animation. If SizeKnown() is
    *          true, then this is a constant, else it is just the total number of
    *          frames we have decoded thus far.
    */
   size_t Size() const { return mSize; }
 
   /**
-   * @returns The first frame refresh area. This is used instead of the dirty
-   * rect for the last frame when transitioning back to the first frame.
-   */
-  const gfx::IntRect& FirstFrameRefreshArea() const {
-    return mFirstFrameRefreshArea;
-  }
-
-  /**
    * @returns True if encountered an error during redecode which should cause
    *          the caller to stop inserting frames.
    */
   bool HasRedecodeError() const { return mRedecodeError; }
 
   /**
    * @returns The current frame index we have advanced to.
    */
@@ -289,20 +281,16 @@ class AnimationFrameBuffer {
   /**
    * Discard any frames as necessary for the reset. mPending may be adjusted to
    * request more frames.
    *
    * @returns True if the caller should resume decoding new frames, else false.
    */
   virtual bool ResetInternal() = 0;
 
-  /// The first frame refresh area. This is used instead of the dirty rect for
-  /// the last frame when transitioning back to the first frame.
-  gfx::IntRect mFirstFrameRefreshArea;
-
   // The total number of frames in the animation. If mSizeKnown is true, it is
   // the actual total regardless of how many frames are available, otherwise it
   // is the total number of inserted frames.
   size_t mSize;
 
   // The minimum number of frames that we want buffered ahead of the display.
   size_t mBatch;
 
@@ -427,16 +415,17 @@ class AnimationFrameDiscardingQueue : pu
  * so that the buffers are all identically sized and contain the complete frame
  * data.
  */
 class AnimationFrameRecyclingQueue final
     : public AnimationFrameDiscardingQueue {
  public:
   explicit AnimationFrameRecyclingQueue(AnimationFrameRetainedBuffer&& aQueue);
 
+  bool MarkComplete(const gfx::IntRect& aFirstFrameRefreshArea) override;
   void AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
                               const AddSizeOfCb& aCallback) override;
 
   RawAccessFrameRef RecycleFrame(gfx::IntRect& aRecycleRect) override;
 
   struct RecycleEntry {
     explicit RecycleEntry(const gfx::IntRect& aDirtyRect)
         : mDirtyRect(aDirtyRect) {}
@@ -453,26 +442,33 @@ class AnimationFrameRecyclingQueue final
     RecycleEntry(const RecycleEntry& aOther) = delete;
     RecycleEntry& operator=(const RecycleEntry& aOther) = delete;
 
     RefPtr<imgFrame> mFrame;  // The frame containing the buffer to recycle.
     gfx::IntRect mDirtyRect;  // The dirty rect of the frame itself.
   };
 
   const std::deque<RecycleEntry>& Recycle() const { return mRecycle; }
+  const gfx::IntRect& FirstFrameRefreshArea() const {
+    return mFirstFrameRefreshArea;
+  }
 
  protected:
   void AdvanceInternal() override;
   bool ResetInternal() override;
 
   /// Queue storing frames to be recycled by the decoder to produce its future
   /// frames. May contain up to mBatch frames, where the last frame in the queue
   /// is adjacent to the first frame in the mDisplay queue.
   std::deque<RecycleEntry> mRecycle;
 
+  /// The first frame refresh area. This is used instead of the dirty rect for
+  /// the last frame when transitioning back to the first frame.
+  gfx::IntRect mFirstFrameRefreshArea;
+
   /// Force recycled frames to use the first frame refresh area as their dirty
   /// rect. This is used when we are recycling frames from the end of an
   /// animation to produce frames at the beginning of an animation.
   bool mForceUseFirstFrameRefreshArea;
 };
 
 }  // namespace image
 }  // namespace mozilla
--- a/image/AnimationSurfaceProvider.cpp
+++ b/image/AnimationSurfaceProvider.cpp
@@ -2,40 +2,35 @@
 /* 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 "AnimationSurfaceProvider.h"
 
 #include "mozilla/StaticPrefs_image.h"
 #include "mozilla/gfx/gfxVars.h"
-#include "mozilla/layers/SharedSurfacesChild.h"
-#include "mozilla/layers/SourceSurfaceSharedData.h"
 #include "nsProxyRelease.h"
 
 #include "DecodePool.h"
 #include "Decoder.h"
 
 using namespace mozilla::gfx;
-using namespace mozilla::layers;
 
 namespace mozilla {
 namespace image {
 
 AnimationSurfaceProvider::AnimationSurfaceProvider(
     NotNull<RasterImage*> aImage, const SurfaceKey& aSurfaceKey,
     NotNull<Decoder*> aDecoder, size_t aCurrentFrame)
     : ISurfaceProvider(ImageKey(aImage.get()), aSurfaceKey,
                        AvailabilityState::StartAsPlaceholder()),
       mImage(aImage.get()),
       mDecodingMutex("AnimationSurfaceProvider::mDecoder"),
       mDecoder(aDecoder.get()),
-      mFramesMutex("AnimationSurfaceProvider::mFrames"),
-      mCompositedFrameRequested(false),
-      mSharedAnimation(MakeRefPtr<SharedSurfacesAnimation>()) {
+      mFramesMutex("AnimationSurfaceProvider::mFrames") {
   MOZ_ASSERT(!mDecoder->IsMetadataDecode(),
              "Use MetadataDecodingTask for metadata decodes");
   MOZ_ASSERT(!mDecoder->IsFirstFrameDecode(),
              "Use DecodedSurfaceProvider for single-frame image decodes");
 
   // Calculate how many frames we need to decode in this animation before we
   // enter decode-on-demand mode.
   IntSize frameSize = aSurfaceKey.Size();
@@ -47,17 +42,16 @@ AnimationSurfaceProvider::AnimationSurfa
 
   mFrames.reset(
       new AnimationFrameRetainedBuffer(threshold, batch, aCurrentFrame));
 }
 
 AnimationSurfaceProvider::~AnimationSurfaceProvider() {
   DropImageReference();
 
-  mSharedAnimation->Destroy();
   if (mDecoder) {
     mDecoder->SetFrameRecycler(nullptr);
   }
 }
 
 void AnimationSurfaceProvider::DropImageReference() {
   if (!mImage) {
     return;  // Nothing to do.
@@ -116,42 +110,25 @@ void AnimationSurfaceProvider::Reset() {
   if (restartDecoder) {
     DecodePool::Singleton()->AsyncRun(this);
   }
 }
 
 void AnimationSurfaceProvider::Advance(size_t aFrame) {
   bool restartDecoder;
 
-  RefPtr<SourceSurface> surface;
-  IntRect dirtyRect;
   {
     // Typical advancement of a frame.
     MutexAutoLock lock(mFramesMutex);
     restartDecoder = mFrames->AdvanceTo(aFrame);
-
-    imgFrame* frame = mFrames->Get(aFrame, /* aForDisplay */ true);
-    MOZ_ASSERT(frame);
-    if (aFrame != 0) {
-      dirtyRect = frame->GetDirtyRect();
-    } else {
-      MOZ_ASSERT(mFrames->SizeKnown());
-      dirtyRect = mFrames->FirstFrameRefreshArea();
-    }
-    surface = frame->GetSourceSurface();
-    MOZ_ASSERT(surface);
   }
 
   if (restartDecoder) {
     DecodePool::Singleton()->AsyncRun(this);
   }
-
-  mCompositedFrameRequested = false;
-  auto* sharedSurface = static_cast<SourceSurfaceSharedData*>(surface.get());
-  mSharedAnimation->SetCurrentFrame(sharedSurface, dirtyRect);
 }
 
 DrawableFrameRef AnimationSurfaceProvider::DrawableRef(size_t aFrame) {
   MutexAutoLock lock(mFramesMutex);
 
   if (Availability().IsPlaceholder()) {
     MOZ_ASSERT_UNREACHABLE("Calling DrawableRef() on a placeholder");
     return DrawableFrameRef();
@@ -503,32 +480,10 @@ bool AnimationSurfaceProvider::ShouldPre
 
 RawAccessFrameRef AnimationSurfaceProvider::RecycleFrame(
     gfx::IntRect& aRecycleRect) {
   MutexAutoLock lock(mFramesMutex);
   MOZ_ASSERT(mFrames->IsRecycling());
   return mFrames->RecycleFrame(aRecycleRect);
 }
 
-nsresult AnimationSurfaceProvider::UpdateKey(
-    layers::RenderRootStateManager* aManager,
-    wr::IpcResourceUpdateQueue& aResources, wr::ImageKey& aKey) {
-  MOZ_ASSERT(NS_IsMainThread());
-
-  RefPtr<SourceSurface> surface;
-  {
-    MutexAutoLock lock(mFramesMutex);
-    imgFrame* frame =
-        mFrames->Get(mFrames->Displayed(), /* aForDisplay */ true);
-    if (!frame) {
-      return NS_ERROR_NOT_AVAILABLE;
-    }
-
-    surface = frame->GetSourceSurface();
-  }
-
-  mCompositedFrameRequested = true;
-  auto* sharedSurface = static_cast<SourceSurfaceSharedData*>(surface.get());
-  return mSharedAnimation->UpdateKey(sharedSurface, aManager, aResources, aKey);
-}
-
 }  // namespace image
 }  // namespace mozilla
--- a/image/AnimationSurfaceProvider.h
+++ b/image/AnimationSurfaceProvider.h
@@ -14,20 +14,16 @@
 
 #include "Decoder.h"
 #include "FrameAnimator.h"
 #include "IDecodingTask.h"
 #include "ISurfaceProvider.h"
 #include "AnimationFrameBuffer.h"
 
 namespace mozilla {
-namespace layers {
-class SharedSurfacesAnimation;
-}
-
 namespace image {
 
 /**
  * An ISurfaceProvider that manages the decoding of animated images and
  * dynamically generates surfaces for the current playback state of the
  * animation.
  */
 class AnimationSurfaceProvider final : public ISurfaceProvider,
@@ -40,25 +36,29 @@ class AnimationSurfaceProvider final : p
                            const SurfaceKey& aSurfaceKey,
                            NotNull<Decoder*> aDecoder, size_t aCurrentFrame);
 
   //////////////////////////////////////////////////////////////////////////////
   // ISurfaceProvider implementation.
   //////////////////////////////////////////////////////////////////////////////
 
  public:
+  // We use the ISurfaceProvider constructor of DrawableSurface to indicate that
+  // our surfaces are computed lazily.
+  DrawableSurface Surface() override {
+    return DrawableSurface(WrapNotNull(this));
+  }
+
   bool IsFinished() const override;
   bool IsFullyDecoded() const override;
   size_t LogicalSizeInBytes() const override;
   void AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
                               const AddSizeOfCb& aCallback) override;
   void Reset() override;
   void Advance(size_t aFrame) override;
-  bool MayAdvance() const override { return mCompositedFrameRequested; }
-  void MarkMayAdvance() override { mCompositedFrameRequested = true; }
 
  protected:
   DrawableFrameRef DrawableRef(size_t aFrame) override;
   already_AddRefed<imgFrame> GetFrame(size_t aFrame) override;
 
   // Animation frames are always locked. This is because we only want to release
   // their memory atomically (due to the surface cache discarding them). If they
   // were unlocked, the OS could end up releasing the memory of random frames
@@ -81,25 +81,16 @@ class AnimationSurfaceProvider final : p
 
   //////////////////////////////////////////////////////////////////////////////
   // IDecoderFrameRecycler implementation.
   //////////////////////////////////////////////////////////////////////////////
 
  public:
   RawAccessFrameRef RecycleFrame(gfx::IntRect& aRecycleRect) override;
 
-  //////////////////////////////////////////////////////////////////////////////
-  // IDecoderFrameRecycler implementation.
-  //////////////////////////////////////////////////////////////////////////////
-
- public:
-  nsresult UpdateKey(layers::RenderRootStateManager* aManager,
-                     wr::IpcResourceUpdateQueue& aResources,
-                     wr::ImageKey& aKey) override;
-
  private:
   virtual ~AnimationSurfaceProvider();
 
   void DropImageReference();
   void AnnounceSurfaceAvailable();
   void FinishDecoding();
   void RequestFrameDiscarding();
 
@@ -118,21 +109,14 @@ class AnimationSurfaceProvider final : p
   /// The decoder used to decode this animation.
   RefPtr<Decoder> mDecoder;
 
   /// A mutex to protect mFrames. Always taken after mDecodingMutex.
   mutable Mutex mFramesMutex;
 
   /// The frames of this animation, in order.
   UniquePtr<AnimationFrameBuffer> mFrames;
-
-  /// Whether the current frame was requested for display since the last time we
-  /// advanced the animation.
-  bool mCompositedFrameRequested;
-
-  ///
-  RefPtr<layers::SharedSurfacesAnimation> mSharedAnimation;
 };
 
 }  // namespace image
 }  // namespace mozilla
 
 #endif  // mozilla_image_AnimationSurfaceProvider_h
--- a/image/ClippedImage.cpp
+++ b/image/ClippedImage.cpp
@@ -293,31 +293,31 @@ ClippedImage::IsImageContainerAvailable(
                                         uint32_t aFlags) {
   if (!ShouldClip()) {
     return InnerImage()->IsImageContainerAvailable(aRenderer, aFlags);
   }
   return false;
 }
 
 NS_IMETHODIMP_(ImgDrawResult)
-ClippedImage::GetImageProvider(WindowRenderer* aRenderer,
-                               const gfx::IntSize& aSize,
-                               const Maybe<SVGImageContext>& aSVGContext,
-                               const Maybe<ImageIntRegion>& aRegion,
-                               uint32_t aFlags,
-                               WebRenderImageProvider** aProvider) {
+ClippedImage::GetImageContainerAtSize(WindowRenderer* aRenderer,
+                                      const gfx::IntSize& aSize,
+                                      const Maybe<SVGImageContext>& aSVGContext,
+                                      const Maybe<ImageIntRegion>& aRegion,
+                                      uint32_t aFlags,
+                                      layers::ImageContainer** aOutContainer) {
   // XXX(seth): We currently don't have a way of clipping the result of
   // GetImageContainer. We work around this by always returning null, but if it
   // ever turns out that ClippedImage is widely used on codepaths that can
   // actually benefit from GetImageContainer, it would be a good idea to fix
   // that method for performance reasons.
 
   if (!ShouldClip()) {
-    return InnerImage()->GetImageProvider(aRenderer, aSize, aSVGContext,
-                                          aRegion, aFlags, aProvider);
+    return InnerImage()->GetImageContainerAtSize(
+        aRenderer, aSize, aSVGContext, aRegion, aFlags, aOutContainer);
   }
 
   return ImgDrawResult::NOT_SUPPORTED;
 }
 
 static bool MustCreateSurface(gfxContext* aContext, const nsIntSize& aSize,
                               const ImageRegion& aRegion,
                               const uint32_t aFlags) {
--- a/image/ClippedImage.h
+++ b/image/ClippedImage.h
@@ -41,20 +41,20 @@ class ClippedImage : public ImageWrapper
   GetFrame(uint32_t aWhichFrame, uint32_t aFlags) override;
   NS_IMETHOD_(already_AddRefed<SourceSurface>)
   GetFrameAtSize(const gfx::IntSize& aSize, uint32_t aWhichFrame,
                  uint32_t aFlags) override;
   NS_IMETHOD_(bool)
   IsImageContainerAvailable(WindowRenderer* aRenderer,
                             uint32_t aFlags) override;
   NS_IMETHOD_(ImgDrawResult)
-  GetImageProvider(WindowRenderer* aRenderer, const gfx::IntSize& aSize,
-                   const Maybe<SVGImageContext>& aSVGContext,
-                   const Maybe<ImageIntRegion>& aRegion, uint32_t aFlags,
-                   WebRenderImageProvider** aProvider) override;
+  GetImageContainerAtSize(WindowRenderer* aRenderer, const gfx::IntSize& aSize,
+                          const Maybe<SVGImageContext>& aSVGContext,
+                          const Maybe<ImageIntRegion>& aRegion, uint32_t aFlags,
+                          layers::ImageContainer** aOutContainer) override;
   NS_IMETHOD_(ImgDrawResult)
   Draw(gfxContext* aContext, const nsIntSize& aSize, const ImageRegion& aRegion,
        uint32_t aWhichFrame, gfx::SamplingFilter aSamplingFilter,
        const Maybe<SVGImageContext>& aSVGContext, uint32_t aFlags,
        float aOpacity) override;
   NS_IMETHOD RequestDiscard() override;
   NS_IMETHOD_(Orientation) GetOrientation() override;
   NS_IMETHOD_(nsIntRect)
--- a/image/DecodedSurfaceProvider.cpp
+++ b/image/DecodedSurfaceProvider.cpp
@@ -1,23 +1,21 @@
 /* -*- Mode: C++; tab-width: 2; 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 "DecodedSurfaceProvider.h"
 
 #include "mozilla/StaticPrefs_image.h"
-#include "mozilla/layers/SharedSurfacesChild.h"
 #include "nsProxyRelease.h"
 
 #include "Decoder.h"
 
 using namespace mozilla::gfx;
-using namespace mozilla::layers;
 
 namespace mozilla {
 namespace image {
 
 DecodedSurfaceProvider::DecodedSurfaceProvider(NotNull<RasterImage*> aImage,
                                                const SurfaceKey& aSurfaceKey,
                                                NotNull<Decoder*> aDecoder)
     : ISurfaceProvider(ImageKey(aImage.get()), aSurfaceKey,
@@ -202,33 +200,10 @@ void DecodedSurfaceProvider::FinishDecod
   DropImageReference();
 }
 
 bool DecodedSurfaceProvider::ShouldPreferSyncRun() const {
   return mDecoder->ShouldSyncDecode(
       StaticPrefs::image_mem_decode_bytes_at_a_time_AtStartup());
 }
 
-nsresult DecodedSurfaceProvider::UpdateKey(
-    layers::RenderRootStateManager* aManager,
-    wr::IpcResourceUpdateQueue& aResources, wr::ImageKey& aKey) {
-  MOZ_ASSERT(mSurface);
-  RefPtr<SourceSurface> surface = mSurface->GetSourceSurface();
-  if (!surface) {
-    return NS_ERROR_FAILURE;
-  }
-
-  return SharedSurfacesChild::Share(surface, aManager, aResources, aKey);
-}
-
-nsresult SimpleSurfaceProvider::UpdateKey(
-    layers::RenderRootStateManager* aManager,
-    wr::IpcResourceUpdateQueue& aResources, wr::ImageKey& aKey) {
-  RefPtr<SourceSurface> surface = mSurface->GetSourceSurface();
-  if (!surface) {
-    return NS_ERROR_FAILURE;
-  }
-
-  return SharedSurfacesChild::Share(surface, aManager, aResources, aKey);
-}
-
 }  // namespace image
 }  // namespace mozilla
--- a/image/DecodedSurfaceProvider.h
+++ b/image/DecodedSurfaceProvider.h
@@ -50,25 +50,16 @@ class DecodedSurfaceProvider final : pub
  public:
   void Run() override;
   bool ShouldPreferSyncRun() const override;
 
   // Full decodes are low priority compared to metadata decodes because they
   // don't block layout or page load.
   TaskPriority Priority() const override { return TaskPriority::eLow; }
 
-  //////////////////////////////////////////////////////////////////////////////
-  // WebRenderImageProvider implementation.
-  //////////////////////////////////////////////////////////////////////////////
-
- public:
-  nsresult UpdateKey(layers::RenderRootStateManager* aManager,
-                     wr::IpcResourceUpdateQueue& aResources,
-                     wr::ImageKey& aKey) override;
-
  private:
   virtual ~DecodedSurfaceProvider();
 
   void DropImageReference();
   void CheckForNewSurface();
   void FinishDecoding();
 
   /// The image associated with our decoder. Dropped after decoding.
--- a/image/DynamicImage.cpp
+++ b/image/DynamicImage.cpp
@@ -118,17 +118,17 @@ DynamicImage::GetResolution() { return {
 
 NS_IMETHODIMP
 DynamicImage::GetType(uint16_t* aType) {
   *aType = imgIContainer::TYPE_RASTER;
   return NS_OK;
 }
 
 NS_IMETHODIMP
-DynamicImage::GetProviderId(uint32_t* aId) {
+DynamicImage::GetProducerId(uint32_t* aId) {
   *aId = 0;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 DynamicImage::GetAnimated(bool* aAnimated) {
   *aAnimated = false;
   return NS_OK;
@@ -165,22 +165,22 @@ DynamicImage::WillDrawOpaqueNow() { retu
 
 NS_IMETHODIMP_(bool)
 DynamicImage::IsImageContainerAvailable(WindowRenderer* aRenderer,
                                         uint32_t aFlags) {
   return false;
 }
 
 NS_IMETHODIMP_(ImgDrawResult)
-DynamicImage::GetImageProvider(WindowRenderer* aRenderer,
-                               const gfx::IntSize& aSize,
-                               const Maybe<SVGImageContext>& aSVGContext,
-                               const Maybe<ImageIntRegion>& aRegion,
-                               uint32_t aFlags,
-                               WebRenderImageProvider** aProvider) {
+DynamicImage::GetImageContainerAtSize(WindowRenderer* aRenderer,
+                                      const gfx::IntSize& aSize,
+                                      const Maybe<SVGImageContext>& aSVGContext,
+                                      const Maybe<ImageIntRegion>& aRegion,
+                                      uint32_t aFlags,
+                                      layers::ImageContainer** aContainer) {
   return ImgDrawResult::NOT_SUPPORTED;
 }
 
 NS_IMETHODIMP_(ImgDrawResult)
 DynamicImage::Draw(gfxContext* aContext, const nsIntSize& aSize,
                    const ImageRegion& aRegion, uint32_t aWhichFrame,
                    SamplingFilter aSamplingFilter,
                    const Maybe<SVGImageContext>& aSVGContext, uint32_t aFlags,
--- a/image/FrameAnimator.cpp
+++ b/image/FrameAnimator.cpp
@@ -313,16 +313,17 @@ RefreshResult FrameAnimator::AdvanceFram
         MOZ_ASSERT(loops <= CheckedUint64(aState.mLoopRemainingCount).value());
         aState.mLoopRemainingCount -= CheckedInt32(loops).value();
       }
     }
   }
 
   // Set currentAnimationFrameIndex at the last possible moment
   aState.mCurrentAnimationFrameIndex = nextFrameIndex;
+  aState.mCompositedFrameRequested = false;
   aCurrentFrame = std::move(nextFrame);
   aFrames.Advance(nextFrameIndex);
 
   // If we're here, we successfully advanced the frame.
   ret.mFrameAdvanced = true;
 
   return ret;
 }
@@ -389,17 +390,17 @@ RefreshResult FrameAnimator::RequestRefr
   }
 
   TimeStamp currentFrameEndTime =
       GetCurrentImgFrameEndTime(aState, currentFrame->GetTimeout());
 
   // If nothing has accessed the composited frame since the last time we
   // advanced, then there is no point in continuing to advance the animation.
   // This has the effect of freezing the animation while not in view.
-  if (!result.Surface().MayAdvance() &&
+  if (!aState.mCompositedFrameRequested &&
       aState.MaybeAdvanceAnimationFrameTime(aTime)) {
     return ret;
   }
 
   while (currentFrameEndTime <= aTime) {
     TimeStamp oldFrameEndTime = currentFrameEndTime;
 
     RefreshResult frameRes =
@@ -437,28 +438,23 @@ RefreshResult FrameAnimator::RequestRefr
 
   MOZ_ASSERT(!aState.mIsCurrentlyDecoded || !aState.mCompositedFrameInvalid);
 
   return ret;
 }
 
 LookupResult FrameAnimator::GetCompositedFrame(AnimationState& aState,
                                                bool aMarkUsed) {
+  aState.mCompositedFrameRequested = true;
+
   LookupResult result = SurfaceCache::Lookup(
       ImageKey(mImage),
       RasterSurfaceKey(mSize, DefaultSurfaceFlags(), PlaybackType::eAnimated),
       aMarkUsed);
 
-  if (result) {
-    // If we are getting the frame directly (e.g. through tests or canvas), we
-    // need to ensure the animation is marked to allow advancing to the next
-    // frame.
-    result.Surface().MarkMayAdvance();
-  }
-
   if (aState.mCompositedFrameInvalid) {
     MOZ_ASSERT(StaticPrefs::image_mem_animated_discardable_AtStartup());
     MOZ_ASSERT(aState.GetHasRequestedDecode());
     MOZ_ASSERT(!aState.GetIsCurrentlyDecoded());
 
     if (result.Type() == MatchType::EXACT) {
       // If our composited frame is marked as invalid but our frames are in the
       // surface cache we might just have not updated our internal state yet.
--- a/image/FrameAnimator.h
+++ b/image/FrameAnimator.h
@@ -30,16 +30,17 @@ class AnimationState {
         mLoopRemainingCount(-1),
         mLoopCount(-1),
         mFirstFrameTimeout(FrameTimeout::FromRawMilliseconds(0)),
         mAnimationMode(aAnimationMode),
         mHasBeenDecoded(false),
         mHasRequestedDecode(false),
         mIsCurrentlyDecoded(false),
         mCompositedFrameInvalid(false),
+        mCompositedFrameRequested(false),
         mDiscarded(false) {}
 
   /**
    * Call this whenever a decode completes, a decode starts, or the image is
    * discarded. It will update the internal state. Specifically mDiscarded,
    * mCompositedFrameInvalid, and mIsCurrentlyDecoded. If aAllowInvalidation
    * is true then returns a rect to invalidate.
    */
@@ -234,16 +235,20 @@ class AnimationState {
   //! Whether this image is currently fully decoded.
   bool mIsCurrentlyDecoded;
 
   //! Whether the composited frame is valid to draw to the screen, note that
   //! the composited frame can exist and be filled with image data but not
   //! valid to draw to the screen.
   bool mCompositedFrameInvalid;
 
+  //! Whether the composited frame was requested from the animator since the
+  //! last time we advanced the animation.
+  bool mCompositedFrameRequested;
+
   //! Whether this image is currently discarded. Only set to true after the
   //! image has been decoded at least once.
   bool mDiscarded;
 };
 
 /**
  * RefreshResult is used to let callers know how the state of the animation
  * changed during a call to FrameAnimator::RequestRefresh().
--- a/image/FrozenImage.cpp
+++ b/image/FrozenImage.cpp
@@ -55,25 +55,25 @@ FrozenImage::IsImageContainerAvailable(W
                                        uint32_t aFlags) {
   if (IsNonAnimated()) {
     return InnerImage()->IsImageContainerAvailable(aRenderer, aFlags);
   }
   return false;
 }
 
 NS_IMETHODIMP_(ImgDrawResult)
-FrozenImage::GetImageProvider(WindowRenderer* aRenderer,
-                              const gfx::IntSize& aSize,
-                              const Maybe<SVGImageContext>& aSVGContext,
-                              const Maybe<ImageIntRegion>& aRegion,
-                              uint32_t aFlags,
-                              WebRenderImageProvider** aProvider) {
+FrozenImage::GetImageContainerAtSize(WindowRenderer* aRenderer,
+                                     const gfx::IntSize& aSize,
+                                     const Maybe<SVGImageContext>& aSVGContext,
+                                     const Maybe<ImageIntRegion>& aRegion,
+                                     uint32_t aFlags,
+                                     layers::ImageContainer** aOutContainer) {
   if (IsNonAnimated()) {
-    return InnerImage()->GetImageProvider(aRenderer, aSize, aSVGContext,
-                                          aRegion, aFlags, aProvider);
+    return InnerImage()->GetImageContainerAtSize(
+        aRenderer, aSize, aSVGContext, aRegion, aFlags, aOutContainer);
   }
 
   // XXX(seth): GetImageContainer does not currently support anything but the
   // current frame. We work around this by always returning null, but if it ever
   // turns out that FrozenImage is widely used on codepaths that can actually
   // benefit from GetImageContainer, it would be a good idea to fix that method
   // for performance reasons.
   return ImgDrawResult::NOT_SUPPORTED;
--- a/image/FrozenImage.h
+++ b/image/FrozenImage.h
@@ -40,20 +40,20 @@ class FrozenImage : public ImageWrapper 
   GetFrame(uint32_t aWhichFrame, uint32_t aFlags) override;
   NS_IMETHOD_(already_AddRefed<SourceSurface>)
   GetFrameAtSize(const gfx::IntSize& aSize, uint32_t aWhichFrame,
                  uint32_t aFlags) override;
   NS_IMETHOD_(bool)
   IsImageContainerAvailable(WindowRenderer* aRenderer,
                             uint32_t aFlags) override;
   NS_IMETHOD_(ImgDrawResult)
-  GetImageProvider(WindowRenderer* aRenderer, const gfx::IntSize& aSize,
-                   const Maybe<SVGImageContext>& aSVGContext,
-                   const Maybe<ImageIntRegion>& aRegion, uint32_t aFlags,
-                   WebRenderImageProvider** aProvider) override;
+  GetImageContainerAtSize(WindowRenderer* aRenderer, const gfx::IntSize& aSize,
+                          const Maybe<SVGImageContext>& aSVGContext,
+                          const Maybe<ImageIntRegion>& aRegion, uint32_t aFlags,
+                          layers::ImageContainer** aOutContainer) override;
   NS_IMETHOD_(ImgDrawResult)
   Draw(gfxContext* aContext, const nsIntSize& aSize, const ImageRegion& aRegion,
        uint32_t aWhichFrame, gfx::SamplingFilter aSamplingFilter,
        const Maybe<SVGImageContext>& aSVGContext, uint32_t aFlags,
        float aOpacity) override;
   NS_IMETHOD StartDecoding(uint32_t aFlags, uint32_t aWhichFrame) override;
   NS_IMETHOD_(bool)
   StartDecodingWithResult(uint32_t aFlags, uint32_t aWhichFrame) override;
--- a/image/ISurfaceProvider.h
+++ b/image/ISurfaceProvider.h
@@ -12,47 +12,46 @@
 #define mozilla_image_ISurfaceProvider_h
 
 #include "mozilla/Attributes.h"
 #include "mozilla/Maybe.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/NotNull.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/gfx/2D.h"
-#include "mozilla/image/WebRenderImageProvider.h"
 
 #include "imgFrame.h"
 #include "SurfaceCache.h"
 
 namespace mozilla {
 namespace image {
 
 class CachedSurface;
 class DrawableSurface;
 
 /**
  * An interface for objects which can either store a surface or dynamically
  * generate one.
  */
-class ISurfaceProvider : public WebRenderImageProvider {
+class ISurfaceProvider {
  public:
   // Subclasses may or may not be XPCOM classes, so we just require that they
   // implement AddRef and Release.
   NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING
 
   /// @return key data used for identifying which image this ISurfaceProvider is
   /// associated with in the surface cache.
   ImageKey GetImageKey() const { return mImageKey; }
 
   /// @return key data used to uniquely identify this ISurfaceProvider's cache
   /// entry in the surface cache.
   const SurfaceKey& GetSurfaceKey() const { return mSurfaceKey; }
 
-  /// @return a drawable reference to a surface.
-  DrawableSurface Surface();
+  /// @return a (potentially lazily computed) drawable reference to a surface.
+  virtual DrawableSurface Surface();
 
   /// @return true if DrawableRef() will return a completely decoded surface.
   virtual bool IsFinished() const = 0;
 
   /// @return true if the underlying decoder is currently fully decoded. For
   /// animated images, this means that at least every frame has been decoded
   /// at least once. It does not guarantee that all of the frames are present,
   /// as the surface provider has the option to discard as it deems necessary.
@@ -77,31 +76,28 @@ class ISurfaceProvider : public WebRende
       return;
     }
 
     ref->AddSizeOfExcludingThis(aMallocSizeOf, aCallback);
   }
 
   virtual void Reset() {}
   virtual void Advance(size_t aFrame) {}
-  virtual bool MayAdvance() const { return false; }
-  virtual void MarkMayAdvance() {}
 
   /// @return the availability state of this ISurfaceProvider, which indicates
   /// whether DrawableRef() could successfully return a surface. Should only be
   /// called from SurfaceCache code as it relies on SurfaceCache for
   /// synchronization.
   AvailabilityState& Availability() { return mAvailability; }
   const AvailabilityState& Availability() const { return mAvailability; }
 
  protected:
   ISurfaceProvider(const ImageKey aImageKey, const SurfaceKey& aSurfaceKey,
                    AvailabilityState aAvailability)
-      : WebRenderImageProvider(aImageKey),
-        mImageKey(aImageKey),
+      : mImageKey(aImageKey),
         mSurfaceKey(aSurfaceKey),
         mAvailability(aAvailability) {
     MOZ_ASSERT(aImageKey, "Must have a valid image key");
   }
 
   virtual ~ISurfaceProvider() {}
 
   /// @return an eagerly computed drawable reference to a surface. For
@@ -144,16 +140,20 @@ class ISurfaceProvider : public WebRende
  * Note that the surface may be computed lazily, so a DrawableSurface should not
  * be dereferenced (i.e., operator->() should not be called) until you're
  * sure that you want to draw it.
  */
 class MOZ_STACK_CLASS DrawableSurface final {
  public:
   DrawableSurface() : mHaveSurface(false) {}
 
+  explicit DrawableSurface(DrawableFrameRef&& aDrawableRef)
+      : mDrawableRef(std::move(aDrawableRef)),
+        mHaveSurface(bool(mDrawableRef)) {}
+
   explicit DrawableSurface(NotNull<ISurfaceProvider*> aProvider)
       : mProvider(aProvider), mHaveSurface(true) {}
 
   DrawableSurface(DrawableSurface&& aOther)
       : mDrawableRef(std::move(aOther.mDrawableRef)),
         mProvider(std::move(aOther.mProvider)),
         mHaveSurface(aOther.mHaveSurface) {
     aOther.mHaveSurface = false;
@@ -217,48 +217,26 @@ class MOZ_STACK_CLASS DrawableSurface fi
     if (!mProvider) {
       MOZ_ASSERT_UNREACHABLE("Trying to advance a static DrawableSurface?");
       return;
     }
 
     mProvider->Advance(aFrame);
   }
 
-  bool MayAdvance() const {
-    if (!mProvider) {
-      MOZ_ASSERT_UNREACHABLE("Trying to advance a static DrawableSurface?");
-      return false;
-    }
-
-    return mProvider->MayAdvance();
-  }
-
-  void MarkMayAdvance() {
-    if (!mProvider) {
-      MOZ_ASSERT_UNREACHABLE("Trying to advance a static DrawableSurface?");
-      return;
-    }
-
-    mProvider->MarkMayAdvance();
-  }
-
   bool IsFullyDecoded() const {
     if (!mProvider) {
       MOZ_ASSERT_UNREACHABLE(
           "Trying to check decoding state of a static DrawableSurface?");
       return false;
     }
 
     return mProvider->IsFullyDecoded();
   }
 
-  void TakeProvider(WebRenderImageProvider** aOutProvider) {
-    mProvider.forget(aOutProvider);
-  }
-
   explicit operator bool() const { return mHaveSurface; }
   imgFrame* operator->() { return DrawableRef().get(); }
 
  private:
   DrawableSurface(const DrawableSurface& aOther) = delete;
   DrawableSurface& operator=(const DrawableSurface& aOther) = delete;
 
   DrawableFrameRef& DrawableRef() {
@@ -278,19 +256,20 @@ class MOZ_STACK_CLASS DrawableSurface fi
   }
 
   DrawableFrameRef mDrawableRef;
   RefPtr<ISurfaceProvider> mProvider;
   bool mHaveSurface;
 };
 
 // Surface() is implemented here so that DrawableSurface's definition is
-// visible.
+// visible. This default implementation eagerly obtains a DrawableFrameRef for
+// the first frame and is intended for static ISurfaceProviders.
 inline DrawableSurface ISurfaceProvider::Surface() {
-  return DrawableSurface(WrapNotNull(this));
+  return DrawableSurface(DrawableRef(/* aFrame = */ 0));
 }
 
 /**
  * An ISurfaceProvider that stores a single surface.
  */
 class SimpleSurfaceProvider final : public ISurfaceProvider {
  public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SimpleSurfaceProvider, override)
@@ -305,20 +284,16 @@ class SimpleSurfaceProvider final : publ
 
   bool IsFinished() const override { return mSurface->IsFinished(); }
 
   size_t LogicalSizeInBytes() const override {
     gfx::IntSize size = mSurface->GetSize();
     return size.width * size.height * mSurface->GetBytesPerPixel();
   }
 
-  nsresult UpdateKey(layers::RenderRootStateManager* aManager,
-                     wr::IpcResourceUpdateQueue& aResources,
-                     wr::ImageKey& aKey) override;
-
  protected:
   DrawableFrameRef DrawableRef(size_t aFrame) override {
     MOZ_ASSERT(aFrame == 0,
                "Requesting an animation frame from a SimpleSurfaceProvider?");
     return mSurface->DrawableRef();
   }
 
   bool IsLocked() const override { return bool(mLockRef); }
--- a/image/Image.cpp
+++ b/image/Image.cpp
@@ -2,42 +2,32 @@
 /* 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 "Image.h"
 
 #include "imgRequest.h"
 #include "Layers.h"  // for LayerManager
-#include "WebRenderImageProvider.h"
 #include "nsIObserverService.h"
 #include "nsRefreshDriver.h"
 #include "nsContentUtils.h"
-#include "mozilla/Atomics.h"
 #include "mozilla/gfx/Point.h"
 #include "mozilla/gfx/Rect.h"
 #include "mozilla/gfx/SourceSurfaceRawData.h"
 #include "mozilla/Services.h"
 #include "mozilla/SizeOfState.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/Tuple.h"  // for Tie
 #include "mozilla/layers/SharedSurfacesChild.h"
+#include "SourceSurfaceBlobImage.h"
 
 namespace mozilla {
 namespace image {
 
-WebRenderImageProvider::WebRenderImageProvider(const ImageResource* aImage)
-    : mProviderId(aImage->GetImageProviderId()) {}
-
-/* static */ ImageProviderId WebRenderImageProvider::AllocateProviderId() {
-  // Callable on all threads.
-  static Atomic<ImageProviderId> sProviderId(0u);
-  return ++sProviderId;
-}
-
 ///////////////////////////////////////////////////////////////////////////////
 // Memory Reporting
 ///////////////////////////////////////////////////////////////////////////////
 
 ImageMemoryCounter::ImageMemoryCounter(imgRequest* aRequest,
                                        SizeOfState& aState, bool aIsUsed)
     : mProgress(UINT32_MAX),
       mType(UINT16_MAX),
@@ -115,32 +105,352 @@ bool ImageResource::GetSpecTruncatedTo1k
   if (sMaxTruncatedLength >= aSpec.Length()) {
     return true;
   }
 
   aSpec.Truncate(sMaxTruncatedLength);
   return false;
 }
 
+void ImageResource::SetCurrentImage(layers::ImageContainer* aContainer,
+                                    gfx::SourceSurface* aSurface,
+                                    const Maybe<gfx::IntRect>& aDirtyRect) {
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(aContainer);
+
+  if (!aSurface) {
+    // The OS threw out some or all of our buffer. We'll need to wait for the
+    // redecode (which was automatically triggered by GetFrame) to complete.
+    return;
+  }
+
+  // |image| holds a reference to a SourceSurface which in turn holds a lock on
+  // the current frame's data buffer, ensuring that it doesn't get freed as
+  // long as the layer system keeps this ImageContainer alive.
+  RefPtr<layers::Image> image = new layers::SourceSurfaceImage(aSurface);
+
+  // We can share the producer ID with other containers because it is only
+  // used internally to validate the frames given to a particular container
+  // so that another object cannot add its own. Similarly the frame ID is
+  // only used internally to ensure it is always increasing, and skipping
+  // IDs from an individual container's perspective is acceptable.
+  AutoTArray<layers::ImageContainer::NonOwningImage, 1> imageList;
+  imageList.AppendElement(layers::ImageContainer::NonOwningImage(
+      image, TimeStamp(), mLastFrameID++, mImageProducerID));
+
+  if (aDirtyRect) {
+    aContainer->SetCurrentImagesInTransaction(imageList);
+  } else {
+    aContainer->SetCurrentImages(imageList);
+  }
+
+  // If we are animated, then we should request that the image container be
+  // treated as such, to avoid display list rebuilding to update frames for
+  // WebRender.
+  if (mProgressTracker->GetProgress() & FLAG_IS_ANIMATED) {
+    if (aDirtyRect) {
+      layers::SharedSurfacesChild::UpdateAnimation(aContainer, aSurface,
+                                                   aDirtyRect.ref());
+    } else {
+      gfx::IntRect dirtyRect(gfx::IntPoint(0, 0), aSurface->GetSize());
+      layers::SharedSurfacesChild::UpdateAnimation(aContainer, aSurface,
+                                                   dirtyRect);
+    }
+  }
+}
+
+ImgDrawResult ImageResource::GetImageContainerImpl(
+    WindowRenderer* aRenderer, const gfx::IntSize& aSize,
+    const Maybe<SVGImageContext>& aSVGContext,
+    const Maybe<ImageIntRegion>& aRegion, uint32_t aFlags,
+    layers::ImageContainer** aOutContainer) {
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(aRenderer);
+  MOZ_ASSERT((aFlags &
+              ~(FLAG_SYNC_DECODE | FLAG_SYNC_DECODE_IF_FAST | FLAG_RECORD_BLOB |
+                FLAG_ASYNC_NOTIFY | FLAG_HIGH_QUALITY_SCALING)) == FLAG_NONE,
+             "Unsupported flag passed to GetImageContainer");
+
+  ImgDrawResult drawResult;
+  gfx::IntSize size;
+  Tie(drawResult, size) = GetImageContainerSize(aRenderer, aSize, aFlags);
+  if (drawResult != ImgDrawResult::SUCCESS) {
+    return drawResult;
+  }
+
+  MOZ_ASSERT(!size.IsEmpty());
+
+  if (mAnimationConsumers == 0) {
+    SendOnUnlockedDraw(aFlags);
+  }
+
+  uint32_t flags = (aFlags & ~(FLAG_SYNC_DECODE | FLAG_SYNC_DECODE_IF_FAST)) |
+                   FLAG_ASYNC_NOTIFY;
+  RefPtr<layers::ImageContainer> container;
+  ImageContainerEntry* entry = nullptr;
+  int i = mImageContainers.Length() - 1;
+  for (; i >= 0; --i) {
+    entry = &mImageContainers[i];
+    if (size == entry->mSize && flags == entry->mFlags &&
+        aSVGContext == entry->mSVGContext && aRegion == entry->mRegion) {
+      // Lack of a container is handled below.
+      container = RefPtr<layers::ImageContainer>(entry->mContainer);
+      break;
+    } else if (entry->mContainer.IsDead()) {
+      // Stop tracking if our weak pointer to the image container was freed.
+      mImageContainers.RemoveElementAt(i);
+    }
+  }
+
+  if (container) {
+    switch (entry->mLastDrawResult) {
+      case ImgDrawResult::SUCCESS:
+      case ImgDrawResult::BAD_IMAGE:
+      case ImgDrawResult::BAD_ARGS:
+      case ImgDrawResult::NOT_SUPPORTED:
+        container.forget(aOutContainer);
+        return entry->mLastDrawResult;
+      case ImgDrawResult::NOT_READY:
+      case ImgDrawResult::INCOMPLETE:
+      case ImgDrawResult::TEMPORARY_ERROR:
+        // Temporary conditions where we need to rerequest the frame to recover.
+        break;
+      case ImgDrawResult::WRONG_SIZE:
+        // Unused by GetFrameInternal
+      default:
+        MOZ_ASSERT_UNREACHABLE("Unhandled ImgDrawResult type!");
+        container.forget(aOutContainer);
+        return entry->mLastDrawResult;
+    }
+  }
+
+  AutoProfilerImagePaintMarker PROFILER_RAII(this);
+#ifdef DEBUG
+  NotifyDrawingObservers();
+#endif
+
+  gfx::IntSize bestSize;
+  RefPtr<gfx::SourceSurface> surface;
+  Tie(drawResult, bestSize, surface) = GetFrameInternal(
+      size, aSVGContext, aRegion, FRAME_CURRENT, aFlags | FLAG_ASYNC_NOTIFY);
+
+  // The requested size might be refused by the surface cache (i.e. due to
+  // factor-of-2 mode). In that case we don't want to create an entry for this
+  // specific size, but rather re-use the entry for the substituted size.
+  if (bestSize != size) {
+    MOZ_ASSERT(!bestSize.IsEmpty());
+
+    // We can only remove the entry if we no longer have a container, because if
+    // there are strong references to it remaining, we need to still update it
+    // in UpdateImageContainer.
+    if (i >= 0 && !container) {
+      mImageContainers.RemoveElementAt(i);
+    }
+
+    // Forget about the stale container, if any. This lets the entry creation
+    // logic do its job below, if it turns out there is no existing best entry
+    // or the best entry doesn't have a container.
+    container = nullptr;
+
+    // We need to do the entry search again for the new size. We skip pruning
+    // because we did this above once already, but ImageContainer is threadsafe,
+    // so there is a remote possibility it got freed.
+    i = mImageContainers.Length() - 1;
+    for (; i >= 0; --i) {
+      entry = &mImageContainers[i];
+      if (bestSize == entry->mSize && flags == entry->mFlags &&
+          aSVGContext == entry->mSVGContext && aRegion == entry->mRegion) {
+        container = RefPtr<layers::ImageContainer>(entry->mContainer);
+        if (container) {
+          switch (entry->mLastDrawResult) {
+            case ImgDrawResult::SUCCESS:
+            case ImgDrawResult::BAD_IMAGE:
+            case ImgDrawResult::BAD_ARGS:
+            case ImgDrawResult::NOT_SUPPORTED:
+              container.forget(aOutContainer);
+              return entry->mLastDrawResult;
+            case ImgDrawResult::NOT_READY:
+            case ImgDrawResult::INCOMPLETE:
+            case ImgDrawResult::TEMPORARY_ERROR:
+              // Temporary conditions where we need to rerequest the frame to
+              // recover. We have already done so!
+              break;
+            case ImgDrawResult::WRONG_SIZE:
+              // Unused by GetFrameInternal
+            default:
+              MOZ_ASSERT_UNREACHABLE("Unhandled DrawResult type!");
+              container.forget(aOutContainer);
+              return entry->mLastDrawResult;
+          }
+        }
+        break;
+      }
+    }
+  }
+
+  if (!container) {
+    // We need a new ImageContainer, so create one.
+    container = MakeAndAddRef<layers::ImageContainer>();
+
+    if (i >= 0) {
+      entry->mContainer = container;
+    } else {
+      entry = mImageContainers.AppendElement(ImageContainerEntry(
+          bestSize, aSVGContext, aRegion, container.get(), flags));
+    }
+  }
+
+  SetCurrentImage(container, surface, Nothing());
+  entry->mLastDrawResult = drawResult;
+  container.forget(aOutContainer);
+  return drawResult;
+}
+
+bool ImageResource::UpdateImageContainer(
+    const Maybe<gfx::IntRect>& aDirtyRect) {
+  MOZ_ASSERT(NS_IsMainThread());
+
+  for (int i = mImageContainers.Length() - 1; i >= 0; --i) {
+    ImageContainerEntry& entry = mImageContainers[i];
+    RefPtr<layers::ImageContainer> container(entry.mContainer);
+    if (container) {
+      // Blob recordings should just be marked as dirty. We will regenerate the
+      // recording when the display list update comes around.
+      if (entry.mFlags & FLAG_RECORD_BLOB) {
+        AutoTArray<layers::ImageContainer::OwningImage, 1> images;
+        container->GetCurrentImages(&images);
+        if (images.IsEmpty()) {
+          MOZ_ASSERT_UNREACHABLE("Empty container!");
+          continue;
+        }
+
+        RefPtr<gfx::SourceSurface> surface =
+            images[0].mImage->GetAsSourceSurface();
+        if (!surface || surface->GetType() != gfx::SurfaceType::BLOB_IMAGE) {
+          MOZ_ASSERT_UNREACHABLE("No/wrong surface in container!");
+          continue;
+        }
+
+        static_cast<SourceSurfaceBlobImage*>(surface.get())->MarkDirty();
+        continue;
+      }
+
+      gfx::IntSize bestSize;
+      RefPtr<gfx::SourceSurface> surface;
+      Tie(entry.mLastDrawResult, bestSize, surface) =
+          GetFrameInternal(entry.mSize, entry.mSVGContext, entry.mRegion,
+                           FRAME_CURRENT, entry.mFlags);
+
+      // It is possible that this is a factor-of-2 substitution. Since we
+      // managed to convert the weak reference into a strong reference, that
+      // means that an imagelib user still is holding onto the container. thus
+      // we cannot consolidate and must keep updating the duplicate container.
+      if (aDirtyRect) {
+        SetCurrentImage(container, surface, aDirtyRect);
+      } else {
+        gfx::IntRect dirtyRect(gfx::IntPoint(0, 0), bestSize);
+        SetCurrentImage(container, surface, Some(dirtyRect));
+      }
+    } else {
+      // Stop tracking if our weak pointer to the image container was freed.
+      mImageContainers.RemoveElementAt(i);
+    }
+  }
+
+  return !mImageContainers.IsEmpty();
+}
+
 void ImageResource::CollectSizeOfSurfaces(
     nsTArray<SurfaceMemoryCounter>& aCounters,
     MallocSizeOf aMallocSizeOf) const {
-  SurfaceCache::CollectSizeOfSurfaces(ImageKey(this), aCounters, aMallocSizeOf);
+  MOZ_ASSERT(NS_IsMainThread());
+
+  for (const auto& entry : mImageContainers) {
+    RefPtr<layers::ImageContainer> container(entry.mContainer);
+    if (!container) {
+      continue;
+    }
+
+    AutoTArray<layers::ImageContainer::OwningImage, 1> images;
+    container->GetCurrentImages(&images);
+    if (images.IsEmpty()) {
+      continue;
+    }
+
+    RefPtr<gfx::SourceSurface> surface = images[0].mImage->GetAsSourceSurface();
+    if (!surface) {
+      MOZ_ASSERT_UNREACHABLE("No surface in container!");
+      continue;
+    }
+
+    // The surface might be wrapping another.
+    bool isMappedSurface = surface->GetType() == gfx::SurfaceType::DATA_MAPPED;
+    const gfx::SourceSurface* actualSurface =
+        isMappedSurface
+            ? static_cast<gfx::SourceSurfaceMappedData*>(surface.get())
+                  ->GetScopedSurface()
+            : surface.get();
+
+    // Check if the surface is already in the report. Ignore if so.
+    bool found = false;
+    for (const auto& counter : aCounters) {
+      if (counter.Surface() == actualSurface) {
+        found = true;
+        break;
+      }
+    }
+    if (found) {
+      continue;
+    }
+
+    // The surface isn't in the report, so it isn't stored in SurfaceCache. We
+    // need to add our own entry here so that it will be included in the memory
+    // report.
+    gfx::SourceSurface::SizeOfInfo info;
+    surface->SizeOfExcludingThis(aMallocSizeOf, info);
+
+    uint32_t heapBytes = aMallocSizeOf(actualSurface);
+    if (isMappedSurface) {
+      heapBytes += aMallocSizeOf(surface.get());
+    }
+
+    SurfaceKey key = ContainerSurfaceKey(surface->GetSize(), entry.mSVGContext,
+                                         ToSurfaceFlags(entry.mFlags));
+    SurfaceMemoryCounter counter(key, actualSurface, /* aIsLocked */ false,
+                                 /* aCannotSubstitute */ false,
+                                 /* aIsFactor2 */ false, /* aFinished */ true,
+                                 SurfaceMemoryCounterType::CONTAINER);
+
+    counter.Values().SetDecodedHeap(info.mHeapBytes + heapBytes);
+    counter.Values().SetDecodedNonHeap(info.mNonHeapBytes);
+    counter.Values().SetDecodedUnknown(info.mUnknownBytes);
+    counter.Values().SetExternalHandles(info.mExternalHandles);
+    counter.Values().SetExternalId(info.mExternalId);
+    counter.Values().SetSurfaceTypes(info.mTypes);
+
+    aCounters.AppendElement(counter);
+  }
+}
+
+void ImageResource::ReleaseImageContainer() {
+  MOZ_ASSERT(NS_IsMainThread());
+  mImageContainers.Clear();
 }
 
 // Constructor
 ImageResource::ImageResource(nsIURI* aURI)
     : mURI(aURI),
       mInnerWindowId(0),
       mAnimationConsumers(0),
       mAnimationMode(kNormalAnimMode),
       mInitialized(false),
       mAnimating(false),
       mError(false),
-      mProviderId(WebRenderImageProvider::AllocateProviderId()) {}
+      mImageProducerID(layers::ImageContainer::AllocateProducerID()),
+      mLastFrameID(0) {}
 
 ImageResource::~ImageResource() {
   // Ask our ProgressTracker to drop its weak reference to us.
   mProgressTracker->ResetImage();
 }
 
 void ImageResource::IncrementAnimationConsumers() {
   MOZ_ASSERT(NS_IsMainThread(),
--- a/image/Image.h
+++ b/image/Image.h
@@ -17,17 +17,16 @@
 #include "gfx2DGlue.h"
 #include "imgIContainer.h"
 #include "ImageContainer.h"
 #include "ImageRegion.h"
 #include "LookupResult.h"
 #include "nsStringFwd.h"
 #include "ProgressTracker.h"
 #include "SurfaceCache.h"
-#include "WebRenderImageProvider.h"
 
 class imgRequest;
 class nsIRequest;
 class nsIInputStream;
 
 namespace mozilla {
 namespace image {
 
@@ -85,37 +84,40 @@ struct MemoryCounter {
   uint64_t mExternalId;
   uint32_t mSurfaceTypes;
 };
 
 enum class SurfaceMemoryCounterType { NORMAL, CONTAINER };
 
 struct SurfaceMemoryCounter {
   SurfaceMemoryCounter(
-      const SurfaceKey& aKey, bool aIsLocked, bool aCannotSubstitute,
-      bool aIsFactor2, bool aFinished,
+      const SurfaceKey& aKey, const gfx::SourceSurface* aSurface,
+      bool aIsLocked, bool aCannotSubstitute, bool aIsFactor2, bool aFinished,
       SurfaceMemoryCounterType aType = SurfaceMemoryCounterType::NORMAL)
       : mKey(aKey),
+        mSurface(aSurface),
         mType(aType),
         mIsLocked(aIsLocked),
         mCannotSubstitute(aCannotSubstitute),
         mIsFactor2(aIsFactor2),
         mFinished(aFinished) {}
 
   const SurfaceKey& Key() const { return mKey; }
+  const gfx::SourceSurface* Surface() const { return mSurface; }
   MemoryCounter& Values() { return mValues; }
   const MemoryCounter& Values() const { return mValues; }
   SurfaceMemoryCounterType Type() const { return mType; }
   bool IsLocked() const { return mIsLocked; }
   bool CannotSubstitute() const { return mCannotSubstitute; }
   bool IsFactor2() const { return mIsFactor2; }
   bool IsFinished() const { return mFinished; }
 
  private:
   const SurfaceKey mKey;
+  const gfx::SourceSurface* MOZ_NON_OWNING_REF mSurface;
   MemoryCounter mValues;
   const SurfaceMemoryCounterType mType;
   const bool mIsLocked;
   const bool mCannotSubstitute;
   const bool mIsFactor2;
   const bool mFinished;
 };
 
@@ -324,22 +326,24 @@ class ImageResource : public Image {
   /*
    * Should be called by its subclasses after they populate @aCounters so that
    * we can cross reference against any of our ImageContainers that contain
    * surfaces not in the cache.
    */
   void CollectSizeOfSurfaces(nsTArray<SurfaceMemoryCounter>& aCounters,
                              MallocSizeOf aMallocSizeOf) const override;
 
-  ImageProviderId GetImageProviderId() const { return mProviderId; }
-
  protected:
   explicit ImageResource(nsIURI* aURI);
   ~ImageResource();
 
+  layers::ContainerProducerID GetImageProducerId() const {
+    return mImageProducerID;
+  }
+
   bool GetSpecTruncatedTo1k(nsCString& aSpec) const;
 
   // Shared functionality for implementors of imgIContainer. Every
   // implementation of attribute animationMode should forward here.
   nsresult GetAnimationModeInternal(uint16_t* aAnimationMode);
   nsresult SetAnimationModeInternal(uint16_t aAnimationMode);
 
   /**
@@ -383,16 +387,70 @@ class ImageResource : public Image {
   TimeStamp mLastRefreshTime;
   uint64_t mInnerWindowId;
   uint32_t mAnimationConsumers;
   uint16_t mAnimationMode;  // Enum values in imgIContainer
   bool mInitialized : 1;    // Have we been initialized?
   bool mAnimating : 1;      // Are we currently animating?
   bool mError : 1;          // Error handling
 
+  /**
+   * Attempt to find a matching cached surface in the SurfaceCache, and if not
+   * available, request the production of such a surface (either synchronously
+   * or asynchronously).
+   *
+   * If the draw result is BAD_IMAGE, BAD_ARGS or NOT_READY, the size will be
+   * the same as aSize. If it is TEMPORARY_ERROR, INCOMPLETE, or SUCCESS, the
+   * size is a hint as to what we expect the surface size to be, once the best
+   * fitting size is available. It may or may not match the size of the surface
+   * returned at this moment. This is useful for choosing how to store the final
+   * result (e.g. if going into an ImageContainer, ideally we would share the
+   * same container for many requested sizes, if they all end up with the same
+   * best fit size in the end).
+   *
+   * A valid surface should only be returned for SUCCESS and INCOMPLETE.
+   *
+   * Any other draw result is invalid.
+   */
+  virtual Tuple<ImgDrawResult, gfx::IntSize, RefPtr<gfx::SourceSurface>>
+  GetFrameInternal(const gfx::IntSize& aSize,
+                   const Maybe<SVGImageContext>& aSVGContext,
+                   const Maybe<ImageIntRegion>& aRegion, uint32_t aWhichFrame,
+                   uint32_t aFlags) {
+    return MakeTuple(ImgDrawResult::BAD_IMAGE, aSize,
+                     RefPtr<gfx::SourceSurface>());
+  }
+
+  /**
+   * Calculate the estimated size to use for an image container with the given
+   * parameters. It may not be the same as the given size, and it may not be
+   * the same as the size of the surface in the image container, but it is the
+   * best effort estimate.
+   */
+  virtual Tuple<ImgDrawResult, gfx::IntSize> GetImageContainerSize(
+      WindowRenderer* aRenderer, const gfx::IntSize& aSize, uint32_t aFlags) {
+    return MakeTuple(ImgDrawResult::NOT_SUPPORTED, gfx::IntSize(0, 0));
+  }
+
+  ImgDrawResult GetImageContainerImpl(WindowRenderer* aRenderer,
+                                      const gfx::IntSize& aSize,
+                                      const Maybe<SVGImageContext>& aSVGContext,
+                                      const Maybe<ImageIntRegion>& aRegion,
+                                      uint32_t aFlags,
+                                      layers::ImageContainer** aContainer);
+
+  /**
+   * Re-requests the appropriate frames for each image container using
+   * GetFrameInternal.
+   * @returns True if any image containers were updated, else false.
+   */
+  bool UpdateImageContainer(const Maybe<gfx::IntRect>& aDirtyRect);
+
+  void ReleaseImageContainer();
+
   class MOZ_RAII AutoProfilerImagePaintMarker {
    public:
     explicit AutoProfilerImagePaintMarker(ImageResource* self)
         : mStartTime(TimeStamp::Now()) {
       nsAutoCString spec;
       if (self->mURI && profiler_thread_is_being_profiled()) {
         static const size_t sMaxTruncatedLength = 1024;
         self->mURI->GetSpec(mSpec);
@@ -411,15 +469,47 @@ class ImageResource : public Image {
     }
 
    protected:
     TimeStamp mStartTime;
     nsAutoCString mSpec;
   };
 
  private:
-  ImageProviderId mProviderId;
+  void SetCurrentImage(layers::ImageContainer* aContainer,
+                       gfx::SourceSurface* aSurface,
+                       const Maybe<gfx::IntRect>& aDirtyRect);
+
+  struct ImageContainerEntry {
+    ImageContainerEntry(const gfx::IntSize& aSize,
+                        const Maybe<SVGImageContext>& aSVGContext,
+                        const Maybe<ImageIntRegion>& aRegion,
+                        layers::ImageContainer* aContainer, uint32_t aFlags)
+        : mSize(aSize),
+          mSVGContext(aSVGContext),
+          mRegion(aRegion),
+          mContainer(aContainer),
+          mLastDrawResult(ImgDrawResult::NOT_READY),
+          mFlags(aFlags) {}
+
+    gfx::IntSize mSize;
+    Maybe<SVGImageContext> mSVGContext;
+    Maybe<ImageIntRegion> mRegion;
+    // A weak pointer to our ImageContainer, which stays alive only as long as
+    // the layer system needs it.
+    ThreadSafeWeakPtr<layers::ImageContainer> mContainer;
+    // If mContainer is non-null, this contains the ImgDrawResult we obtained
+    // the last time we updated it.
+    ImgDrawResult mLastDrawResult;
+    // Cached flags to use for decoding. FLAG_ASYNC_NOTIFY should always be set
+    // but FLAG_HIGH_QUALITY_SCALING may vary.
+    uint32_t mFlags;
+  };
+
+  AutoTArray<ImageContainerEntry, 1> mImageContainers;
+  layers::ImageContainer::ProducerID mImageProducerID;
+  layers::ImageContainer::FrameID mLastFrameID;
 };
 
 }  // namespace image
 }  // namespace mozilla
 
 #endif  // mozilla_image_Image_h
--- a/image/ImageRegion.h
+++ b/image/ImageRegion.h
@@ -8,17 +8,16 @@
 
 #include "gfxMatrix.h"
 #include "gfxPoint.h"
 #include "gfxRect.h"
 #include "gfxTypes.h"
 #include "mozilla/gfx/Matrix.h"
 #include "mozilla/gfx/Types.h"
 #include "nsSize.h"
-#include "PLDHashTable.h"  // for PLDHashNumber
 
 namespace mozilla {
 namespace image {
 
 /**
  * An axis-aligned rectangle in tiled image space, with an optional sampling
  * restriction rect. The drawing code ensures that if a sampling restriction
  * rect is present, any pixels sampled during the drawing process are found
@@ -235,22 +234,16 @@ class ImageIntRegion {
 
   bool operator==(const ImageIntRegion& aOther) const {
     return mExtendMode == aOther.mExtendMode &&
            mIsRestricted == aOther.mIsRestricted &&
            mRect.IsEqualEdges(aOther.mRect) &&
            (!mIsRestricted || mRestriction.IsEqualEdges(aOther.mRestriction));
   }
 
-  PLDHashNumber Hash() const {
-    return HashGeneric(mRect.x, mRect.y, mRect.width, mRect.height,
-                       mRestriction.x, mRestriction.y, mRestriction.width,
-                       mRestriction.height, mExtendMode, mIsRestricted);
-  }
-
   /* ImageIntRegion() : mIsRestricted(false) { } */
 
  private:
   explicit ImageIntRegion(const mozilla::gfx::IntRect& aRect,
                           ExtendMode aExtendMode)
       : mRect(aRect), mExtendMode(aExtendMode), mIsRestricted(false) {}
 
   ImageIntRegion(const mozilla::gfx::IntRect& aRect,
--- a/image/ImageWrapper.cpp
+++ b/image/ImageWrapper.cpp
@@ -136,18 +136,18 @@ ImageWrapper::GetOrientation() { return 
 
 NS_IMETHODIMP_(Resolution)
 ImageWrapper::GetResolution() { return mInnerImage->GetResolution(); }
 
 NS_IMETHODIMP
 ImageWrapper::GetType(uint16_t* aType) { return mInnerImage->GetType(aType); }
 
 NS_IMETHODIMP
-ImageWrapper::GetProviderId(uint32_t* aId) {
-  return mInnerImage->GetProviderId(aId);
+ImageWrapper::GetProducerId(uint32_t* aId) {
+  return mInnerImage->GetProducerId(aId);
 }
 
 NS_IMETHODIMP
 ImageWrapper::GetAnimated(bool* aAnimated) {
   return mInnerImage->GetAnimated(aAnimated);
 }
 
 NS_IMETHODIMP_(already_AddRefed<SourceSurface>)
@@ -166,24 +166,24 @@ ImageWrapper::WillDrawOpaqueNow() { retu
 
 NS_IMETHODIMP_(bool)
 ImageWrapper::IsImageContainerAvailable(WindowRenderer* aRenderer,
                                         uint32_t aFlags) {
   return mInnerImage->IsImageContainerAvailable(aRenderer, aFlags);
 }
 
 NS_IMETHODIMP_(ImgDrawResult)
-ImageWrapper::GetImageProvider(WindowRenderer* aRenderer,
-                               const gfx::IntSize& aSize,
-                               const Maybe<SVGImageContext>& aSVGContext,
-                               const Maybe<ImageIntRegion>& aRegion,
-                               uint32_t aFlags,
-                               WebRenderImageProvider** aProvider) {
-  return mInnerImage->GetImageProvider(aRenderer, aSize, aSVGContext, aRegion,
-                                       aFlags, aProvider);
+ImageWrapper::GetImageContainerAtSize(WindowRenderer* aRenderer,
+                                      const gfx::IntSize& aSize,
+                                      const Maybe<SVGImageContext>& aSVGContext,
+                                      const Maybe<ImageIntRegion>& aRegion,
+                                      uint32_t aFlags,
+                                      layers::ImageContainer** aOutContainer) {
+  return mInnerImage->GetImageContainerAtSize(aRenderer, aSize, aSVGContext,
+                                              aRegion, aFlags, aOutContainer);
 }
 
 NS_IMETHODIMP_(ImgDrawResult)
 ImageWrapper::Draw(gfxContext* aContext, const nsIntSize& aSize,
                    const ImageRegion& aRegion, uint32_t aWhichFrame,
                    SamplingFilter aSamplingFilter,
                    const Maybe<SVGImageContext>& aSVGContext, uint32_t aFlags,
                    float aOpacity) {
--- a/image/OrientedImage.cpp
+++ b/image/OrientedImage.cpp
@@ -151,31 +151,30 @@ OrientedImage::IsImageContainerAvailable
                                          uint32_t aFlags) {
   if (mOrientation.IsIdentity()) {
     return InnerImage()->IsImageContainerAvailable(aRenderer, aFlags);
   }
   return false;
 }
 
 NS_IMETHODIMP_(ImgDrawResult)
-OrientedImage::GetImageProvider(WindowRenderer* aRenderer,
-                                const gfx::IntSize& aSize,
-                                const Maybe<SVGImageContext>& aSVGContext,
-                                const Maybe<ImageIntRegion>& aRegion,
-                                uint32_t aFlags,
-                                WebRenderImageProvider** aProvider) {
+OrientedImage::GetImageContainerAtSize(
+    WindowRenderer* aRenderer, const gfx::IntSize& aSize,
+    const Maybe<SVGImageContext>& aSVGContext,
+    const Maybe<ImageIntRegion>& aRegion, uint32_t aFlags,
+    layers::ImageContainer** aOutContainer) {
   // XXX(seth): We currently don't have a way of orienting the result of
   // GetImageContainer. We work around this by always returning null, but if it
   // ever turns out that OrientedImage is widely used on codepaths that can
   // actually benefit from GetImageContainer, it would be a good idea to fix
   // that method for performance reasons.
 
   if (mOrientation.IsIdentity()) {
-    return InnerImage()->GetImageProvider(aRenderer, aSize, aSVGContext,
-                                          aRegion, aFlags, aProvider);
+    return InnerImage()->GetImageContainerAtSize(
+        aRenderer, aSize, aSVGContext, aRegion, aFlags, aOutContainer);
   }
 
   return ImgDrawResult::NOT_SUPPORTED;
 }
 
 struct MatrixBuilder {
   explicit MatrixBuilder(bool aInvert) : mInvert(aInvert) {}
 
--- a/image/OrientedImage.h
+++ b/image/OrientedImage.h
@@ -36,20 +36,20 @@ class OrientedImage : public ImageWrappe
   GetFrame(uint32_t aWhichFrame, uint32_t aFlags) override;
   NS_IMETHOD_(already_AddRefed<SourceSurface>)
   GetFrameAtSize(const gfx::IntSize& aSize, uint32_t aWhichFrame,
                  uint32_t aFlags) override;
   NS_IMETHOD_(bool)
   IsImageContainerAvailable(WindowRenderer* aRenderer,
                             uint32_t aFlags) override;
   NS_IMETHOD_(ImgDrawResult)
-  GetImageProvider(WindowRenderer* aRenderer, const gfx::IntSize& aSize,
-                   const Maybe<SVGImageContext>& aSVGContext,
-                   const Maybe<ImageIntRegion>& aRegion, uint32_t aFlags,
-                   WebRenderImageProvider** aProvider) override;
+  GetImageContainerAtSize(WindowRenderer* aRenderer, const gfx::IntSize& aSize,
+                          const Maybe<SVGImageContext>& aSVGContext,
+                          const Maybe<ImageIntRegion>& aRegion, uint32_t aFlags,
+                          layers::ImageContainer** aOutContainer) override;
   NS_IMETHOD_(ImgDrawResult)
   Draw(gfxContext* aContext, const nsIntSize& aSize, const ImageRegion& aRegion,
        uint32_t aWhichFrame, gfx::SamplingFilter aSamplingFilter,
        const Maybe<SVGImageContext>& aSVGContext, uint32_t aFlags,
        float aOpacity) override;
   NS_IMETHOD_(nsIntRect)
   GetImageSpaceInvalidationRect(const nsIntRect& aRect) override;
   nsIntSize OptimalImageSizeForDest(const gfxSize& aDest, uint32_t aWhichFrame,
--- a/image/RasterImage.cpp
+++ b/image/RasterImage.cpp
@@ -268,20 +268,20 @@ NS_IMETHODIMP
 RasterImage::GetType(uint16_t* aType) {
   NS_ENSURE_ARG_POINTER(aType);
 
   *aType = imgIContainer::TYPE_RASTER;
   return NS_OK;
 }
 
 NS_IMETHODIMP
-RasterImage::GetProviderId(uint32_t* aId) {
+RasterImage::GetProducerId(uint32_t* aId) {
   NS_ENSURE_ARG_POINTER(aId);
 
-  *aId = ImageResource::GetImageProviderId();
+  *aId = ImageResource::GetImageProducerId();
   return NS_OK;
 }
 
 LookupResult RasterImage::LookupFrameInternal(const OrientedIntSize& aSize,
                                               uint32_t aFlags,
                                               PlaybackType aPlaybackType,
                                               bool aMarkUsed) {
   if (mAnimationState && aPlaybackType == PlaybackType::eAnimated) {
@@ -474,16 +474,17 @@ void RasterImage::OnSurfaceDiscarded(con
   eventTarget->Dispatch(ev.forget(), NS_DISPATCH_NORMAL);
 }
 
 void RasterImage::OnSurfaceDiscardedInternal(bool aAnimatedFramesDiscarded) {
   MOZ_ASSERT(NS_IsMainThread());
 
   if (aAnimatedFramesDiscarded && mAnimationState) {
     MOZ_ASSERT(StaticPrefs::image_mem_animated_discardable_AtStartup());
+    ReleaseImageContainer();
 
     IntRect rect = mAnimationState->UpdateState(this, mSize.ToUnknownSize());
 
     auto dirtyRect = OrientedIntRect::FromUnknownRect(rect);
     NotifyProgress(NoProgress, dirtyRect);
   }
 
   if (mProgressTracker) {
@@ -540,111 +541,136 @@ RasterImage::GetFirstFrameDelay() {
 NS_IMETHODIMP_(already_AddRefed<SourceSurface>)
 RasterImage::GetFrame(uint32_t aWhichFrame, uint32_t aFlags) {
   return GetFrameAtSize(mSize.ToUnknownSize(), aWhichFrame, aFlags);
 }
 
 NS_IMETHODIMP_(already_AddRefed<SourceSurface>)
 RasterImage::GetFrameAtSize(const IntSize& aSize, uint32_t aWhichFrame,
                             uint32_t aFlags) {
-  MOZ_ASSERT(aWhichFrame <= FRAME_MAX_VALUE);
-
   AutoProfilerImagePaintMarker PROFILER_RAII(this);
 #ifdef DEBUG
   NotifyDrawingObservers();
 #endif
 
-  if (aSize.IsEmpty() || aWhichFrame > FRAME_MAX_VALUE || mError) {
-    return nullptr;
+  auto result =
+      GetFrameInternal(aSize, Nothing(), Nothing(), aWhichFrame, aFlags);
+  return mozilla::Get<2>(result).forget();
+}
+
+Tuple<ImgDrawResult, IntSize, RefPtr<SourceSurface>>
+RasterImage::GetFrameInternal(const IntSize& aSize,
+                              const Maybe<SVGImageContext>& aSVGContext,
+                              const Maybe<ImageIntRegion>& aRegion,
+                              uint32_t aWhichFrame, uint32_t aFlags) {
+  MOZ_ASSERT(aWhichFrame <= FRAME_MAX_VALUE);
+
+  auto size = OrientedIntSize::FromUnknownSize(aSize);
+
+  if (aSize.IsEmpty() || aWhichFrame > FRAME_MAX_VALUE) {
+    return MakeTuple(ImgDrawResult::BAD_ARGS, aSize, RefPtr<SourceSurface>());
   }
 
-  auto size = OrientedIntSize::FromUnknownSize(aSize);
+  if (mError) {
+    return MakeTuple(ImgDrawResult::BAD_IMAGE, aSize, RefPtr<SourceSurface>());
+  }
 
   // Get the frame. If it's not there, it's probably the caller's fault for
   // not waiting for the data to be loaded from the network or not passing
   // FLAG_SYNC_DECODE.
   LookupResult result = LookupFrame(size, aFlags, ToPlaybackType(aWhichFrame),
                                     /* aMarkUsed = */ true);
+
+  // The surface cache may have suggested we use a different size than the
+  // given size in the future. This may or may not be accompanied by an
+  // actual surface, depending on what it has in its cache.
+  auto suggestedSize = OrientedIntSize::FromUnknownSize(result.SuggestedSize());
+  if (suggestedSize.IsEmpty()) {
+    suggestedSize = size;
+  }
+  MOZ_ASSERT_IF(result.Type() == MatchType::SUBSTITUTE_BECAUSE_BEST,
+                suggestedSize != size);
+
   if (!result) {
     // The OS threw this frame away and we couldn't redecode it.
-    return nullptr;
+    return MakeTuple(ImgDrawResult::TEMPORARY_ERROR,
+                     suggestedSize.ToUnknownSize(), RefPtr<SourceSurface>());
+  }
+
+  RefPtr<SourceSurface> surface = result.Surface()->GetSourceSurface();
+  if (!result.Surface()->IsFinished()) {
+    return MakeTuple(ImgDrawResult::INCOMPLETE, suggestedSize.ToUnknownSize(),
+                     std::move(surface));
+  }
+
+  return MakeTuple(ImgDrawResult::SUCCESS, suggestedSize.ToUnknownSize(),
+                   std::move(surface));
+}
+
+Tuple<ImgDrawResult, IntSize> RasterImage::GetImageContainerSize(
+    WindowRenderer* aRenderer, const IntSize& aRequestedSize, uint32_t aFlags) {
+  if (!LoadHasSize()) {
+    return MakeTuple(ImgDrawResult::NOT_READY, IntSize(0, 0));
   }
 
-  return result.Surface()->GetSourceSurface();
+  if (aRequestedSize.IsEmpty()) {
+    return MakeTuple(ImgDrawResult::BAD_ARGS, IntSize(0, 0));
+  }
+
+  // We check the minimum size because while we support downscaling, we do not
+  // support upscaling. If aRequestedSize > mSize, we will never give a larger
+  // surface than mSize. If mSize > aRequestedSize, and mSize > maxTextureSize,
+  // we still want to use image containers if aRequestedSize <= maxTextureSize.
+  int32_t maxTextureSize = aRenderer->GetMaxTextureSize();
+  if (min(mSize.width, aRequestedSize.width) > maxTextureSize ||
+      min(mSize.height, aRequestedSize.height) > maxTextureSize) {
+    return MakeTuple(ImgDrawResult::NOT_SUPPORTED, IntSize(0, 0));
+  }
+
+  auto requestedSize = OrientedIntSize::FromUnknownSize(aRequestedSize);
+  if (!CanDownscaleDuringDecode(requestedSize, aFlags)) {
+    return MakeTuple(ImgDrawResult::SUCCESS, mSize.ToUnknownSize());
+  }
+
+  return MakeTuple(ImgDrawResult::SUCCESS, aRequestedSize);
 }
 
 NS_IMETHODIMP_(bool)
 RasterImage::IsImageContainerAvailable(WindowRenderer* aRenderer,
                                        uint32_t aFlags) {
   return LoadHasSize();
 }
 
 NS_IMETHODIMP_(ImgDrawResult)
-RasterImage::GetImageProvider(WindowRenderer* aRenderer,
-                              const gfx::IntSize& aSize,
-                              const Maybe<SVGImageContext>& aSVGContext,
-                              const Maybe<ImageIntRegion>& aRegion,
-                              uint32_t aFlags,
-                              WebRenderImageProvider** aProvider) {
-  MOZ_ASSERT(NS_IsMainThread());
-  MOZ_ASSERT(aRenderer);
-
-  if (mError) {
-    return ImgDrawResult::BAD_IMAGE;
-  }
-
-  if (!LoadHasSize()) {
-    return ImgDrawResult::NOT_READY;
-  }
-
-  if (aSize.IsEmpty()) {
-    return ImgDrawResult::BAD_ARGS;
-  }
-
-  // We check the minimum size because while we support downscaling, we do not
-  // support upscaling. If aRequestedSize > mSize, we will never give a larger
-  // surface than mSize. If mSize > aRequestedSize, and mSize > maxTextureSize,
-  // we still want to use image containers if aRequestedSize <= maxTextureSize.
-  int32_t maxTextureSize = aRenderer->GetMaxTextureSize();
-  if (min(mSize.width, aSize.width) > maxTextureSize ||
-      min(mSize.height, aSize.height) > maxTextureSize) {
-    return ImgDrawResult::NOT_SUPPORTED;
-  }
-
-  AutoProfilerImagePaintMarker PROFILER_RAII(this);
-#ifdef DEBUG
-  NotifyDrawingObservers();
-#endif
-
-  // Get the frame. If it's not there, it's probably the caller's fault for
-  // not waiting for the data to be loaded from the network or not passing
-  // FLAG_SYNC_DECODE.
-  LookupResult result = LookupFrame(OrientedIntSize::FromUnknownSize(aSize),
-                                    aFlags, PlaybackType::eAnimated,
-                                    /* aMarkUsed = */ true);
-  if (!result) {
-    // The OS threw this frame away and we couldn't redecode it.
-    return ImgDrawResult::NOT_READY;
-  }
-
-  if (!result.Surface()->IsFinished()) {
-    result.Surface().TakeProvider(aProvider);
-    return ImgDrawResult::INCOMPLETE;
-  }
-
-  result.Surface().TakeProvider(aProvider);
-  return ImgDrawResult::SUCCESS;
+RasterImage::GetImageContainerAtSize(WindowRenderer* aRenderer,
+                                     const gfx::IntSize& aSize,
+                                     const Maybe<SVGImageContext>& aSVGContext,
+                                     const Maybe<ImageIntRegion>& aRegion,
+                                     uint32_t aFlags,
+                                     layers::ImageContainer** aOutContainer) {
+  // We do not pass in the given SVG context because in theory it could differ
+  // between calls, but actually have no impact on the actual contents of the
+  // image container.
+  return GetImageContainerImpl(aRenderer, aSize, Nothing(), Nothing(), aFlags,
+                               aOutContainer);
 }
 
 size_t RasterImage::SizeOfSourceWithComputedFallback(
     SizeOfState& aState) const {
   return mSourceBuffer->SizeOfIncludingThisWithComputedFallback(
       aState.mMallocSizeOf);
 }
 
+void RasterImage::CollectSizeOfSurfaces(
+    nsTArray<SurfaceMemoryCounter>& aCounters,
+    MallocSizeOf aMallocSizeOf) const {
+  SurfaceCache::CollectSizeOfSurfaces(ImageKey(this), aCounters, aMallocSizeOf);
+  ImageResource::CollectSizeOfSurfaces(aCounters, aMallocSizeOf);
+}
+
 bool RasterImage::SetMetadata(const ImageMetadata& aMetadata,
                               bool aFromMetadataDecode) {
   MOZ_ASSERT(NS_IsMainThread());
 
   if (mError) {
     return true;
   }
 
@@ -962,16 +988,18 @@ void RasterImage::Discard() {
   MOZ_ASSERT(!mAnimationState ||
                  StaticPrefs::image_mem_animated_discardable_AtStartup(),
              "Asked to discard for animated image");
 
   // Delete all the decoded frames.
   SurfaceCache::RemoveImage(ImageKey(this));
 
   if (mAnimationState) {
+    ReleaseImageContainer();
+
     IntRect rect = mAnimationState->UpdateState(this, mSize.ToUnknownSize());
 
     auto dirtyRect = OrientedIntRect::FromUnknownRect(rect);
     NotifyProgress(NoProgress, dirtyRect);
   }
 
   // Notify that we discarded.
   if (mProgressTracker) {
@@ -1565,16 +1593,23 @@ void RasterImage::NotifyProgress(
     if (mAnimationState) {
       IntRect rect = mAnimationState->UpdateState(this, mSize.ToUnknownSize());
 
       invalidRect.UnionRect(invalidRect,
                             OrientedIntRect::FromUnknownRect(rect));
     }
   }
 
+  const bool wasDefaultFlags = aSurfaceFlags == DefaultSurfaceFlags();
+
+  if (!invalidRect.IsEmpty() && wasDefaultFlags) {
+    // Update our image container since we're invalidating.
+    UpdateImageContainer(Some(invalidRect.ToUnknownRect()));
+  }
+
   // Tell the observers what happened.
   image->mProgressTracker->SyncNotifyProgress(aProgress,
                                               invalidRect.ToUnknownRect());
 }
 
 void RasterImage::NotifyDecodeComplete(
     const DecoderFinalStatus& aStatus, const ImageMetadata& aMetadata,
     const DecoderTelemetry& aTelemetry, Progress aProgress,
--- a/image/RasterImage.h
+++ b/image/RasterImage.h
@@ -161,16 +161,18 @@ class RasterImage final : public ImageRe
   virtual nsresult StartAnimation() override;
   virtual nsresult StopAnimation() override;
 
   // Methods inherited from Image
   virtual void OnSurfaceDiscarded(const SurfaceKey& aSurfaceKey) override;
 
   virtual size_t SizeOfSourceWithComputedFallback(
       SizeOfState& aState) const override;
+  virtual void CollectSizeOfSurfaces(nsTArray<SurfaceMemoryCounter>& aCounters,
+                                     MallocSizeOf aMallocSizeOf) const override;
 
   /* Triggers discarding. */
   void Discard();
 
   //////////////////////////////////////////////////////////////////////////////
   // Decoder callbacks.
   //////////////////////////////////////////////////////////////////////////////
 
@@ -287,16 +289,26 @@ class RasterImage final : public ImageRe
                                    bool aMarkUsed);
 
   ImgDrawResult DrawInternal(DrawableSurface&& aFrameRef, gfxContext* aContext,
                              const OrientedIntSize& aSize,
                              const ImageRegion& aRegion,
                              gfx::SamplingFilter aSamplingFilter,
                              uint32_t aFlags, float aOpacity);
 
+  Tuple<ImgDrawResult, gfx::IntSize, RefPtr<gfx::SourceSurface>>
+  GetFrameInternal(const gfx::IntSize& aSize,
+                   const Maybe<SVGImageContext>& aSVGContext,
+                   const Maybe<ImageIntRegion>& aRegion, uint32_t aWhichFrame,
+                   uint32_t aFlags) override;
+
+  Tuple<ImgDrawResult, gfx::IntSize> GetImageContainerSize(
+      WindowRenderer* aRenderer, const gfx::IntSize& aSize,
+      uint32_t aFlags) override;
+
   //////////////////////////////////////////////////////////////////////////////
   // Decoding.
   //////////////////////////////////////////////////////////////////////////////
 
   /**
    * Creates and runs a decoder, either synchronously or asynchronously
    * according to @aFlags. Decodes at the provided target size @aSize, using
    * decode flags @aFlags. Performs a single-frame decode of this image unless
rename from image/BlobSurfaceProvider.cpp
rename to image/SourceSurfaceBlobImage.cpp
--- a/image/BlobSurfaceProvider.cpp
+++ b/image/SourceSurfaceBlobImage.cpp
@@ -1,160 +1,154 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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 "BlobSurfaceProvider.h"
+#include "SourceSurfaceBlobImage.h"
 #include "AutoRestoreSVGState.h"
 #include "ImageRegion.h"
 #include "SVGDocumentWrapper.h"
 #include "mozilla/PresShell.h"
 #include "mozilla/dom/Document.h"
 #include "mozilla/gfx/2D.h"
 #include "mozilla/layers/IpcResourceUpdateQueue.h"
 #include "mozilla/layers/WebRenderBridgeChild.h"
 #include "mozilla/layers/WebRenderDrawEventRecorder.h"
 
 using namespace mozilla::gfx;
 using namespace mozilla::layers;
 
 namespace mozilla::image {
 
-BlobSurfaceProvider::BlobSurfaceProvider(
-    const ImageKey aImageKey, const SurfaceKey& aSurfaceKey,
-    image::SVGDocumentWrapper* aSVGDocumentWrapper, uint32_t aImageFlags)
-    : ISurfaceProvider(aImageKey, aSurfaceKey,
-                       AvailabilityState::StartAvailable()),
-      mSVGDocumentWrapper(aSVGDocumentWrapper),
+SourceSurfaceBlobImage::SourceSurfaceBlobImage(
+    image::SVGDocumentWrapper* aSVGDocumentWrapper,
+    const Maybe<SVGImageContext>& aSVGContext,
+    const Maybe<ImageIntRegion>& aRegion, const IntSize& aSize,
+    uint32_t aWhichFrame, uint32_t aImageFlags)
+    : mSVGDocumentWrapper(aSVGDocumentWrapper),
+      mSVGContext(aSVGContext),
+      mRegion(aRegion),
+      mSize(aSize),
+      mWhichFrame(aWhichFrame),
       mImageFlags(aImageFlags) {
   MOZ_ASSERT(mSVGDocumentWrapper);
+  MOZ_ASSERT(aWhichFrame <= imgIContainer::FRAME_MAX_VALUE);
   MOZ_ASSERT(aImageFlags & imgIContainer::FLAG_RECORD_BLOB);
 }
 
-BlobSurfaceProvider::~BlobSurfaceProvider() {
+SourceSurfaceBlobImage::~SourceSurfaceBlobImage() {
   if (NS_IsMainThread()) {
     DestroyKeys(mKeys);
     return;
   }
 
   NS_ReleaseOnMainThread("SourceSurfaceBlobImage::mSVGDocumentWrapper",
                          mSVGDocumentWrapper.forget());
   NS_DispatchToMainThread(
       NS_NewRunnableFunction("SourceSurfaceBlobImage::DestroyKeys",
                              [keys = std::move(mKeys)] { DestroyKeys(keys); }));
 }
 
-/* static */ void BlobSurfaceProvider::DestroyKeys(
+/* static */ void SourceSurfaceBlobImage::DestroyKeys(
     const AutoTArray<BlobImageKeyData, 1>& aKeys) {
   for (const auto& entry : aKeys) {
     if (!entry.mManager->IsDestroyed()) {
       entry.mManager->GetRenderRootStateManager()->AddBlobImageKeyForDiscard(
           entry.mBlobKey);
     }
   }
 }
 
-nsresult BlobSurfaceProvider::UpdateKey(
-    layers::RenderRootStateManager* aManager,
-    wr::IpcResourceUpdateQueue& aResources, wr::ImageKey& aKey) {
+Maybe<wr::BlobImageKey> SourceSurfaceBlobImage::UpdateKey(
+    WebRenderLayerManager* aManager, wr::IpcResourceUpdateQueue& aResources) {
   MOZ_ASSERT(NS_IsMainThread());
 
-  layers::WebRenderLayerManager* manager = aManager->LayerManager();
-  MOZ_ASSERT(manager);
-
   Maybe<wr::BlobImageKey> key;
   auto i = mKeys.Length();
   while (i > 0) {
     --i;
     BlobImageKeyData& entry = mKeys[i];
     if (entry.mManager->IsDestroyed()) {
       mKeys.RemoveElementAt(i);
-    } else if (entry.mManager == manager) {
-      WebRenderBridgeChild* wrBridge = manager->WrBridge();
+    } else if (entry.mManager == aManager) {
+      WebRenderBridgeChild* wrBridge = aManager->WrBridge();
       MOZ_ASSERT(wrBridge);
 
       bool ownsKey = wrBridge->MatchesNamespace(entry.mBlobKey);
       if (ownsKey && !entry.mDirty) {
         key.emplace(entry.mBlobKey);
         continue;
       }
 
       // Even if the manager is the same, its underlying WebRenderBridgeChild
       // can change state. Either our namespace differs, and our old key has
       // already been discarded, or the blob has changed. Either way, we need
       // to rerecord it.
-      auto newEntry = RecordDrawing(manager, aResources,
+      auto newEntry = RecordDrawing(aManager, aResources,
                                     ownsKey ? Some(entry.mBlobKey) : Nothing());
       if (!newEntry) {
         if (ownsKey) {
-          aManager->AddBlobImageKeyForDiscard(entry.mBlobKey);
+          aManager->GetRenderRootStateManager()->AddBlobImageKeyForDiscard(
+              entry.mBlobKey);
         }
         mKeys.RemoveElementAt(i);
         continue;
       }
 
       key.emplace(newEntry.ref().mBlobKey);
       entry = std::move(newEntry.ref());
       MOZ_ASSERT(!entry.mDirty);
     }
   }
 
   // We didn't find an entry. Attempt to record the blob with a new key.
   if (!key) {
-    auto newEntry = RecordDrawing(manager, aResources, Nothing());
+    auto newEntry = RecordDrawing(aManager, aResources, Nothing());
     if (newEntry) {
       key.emplace(newEntry.ref().mBlobKey);
       mKeys.AppendElement(std::move(newEntry.ref()));
     }
   }
 
-  if (key) {
-    aKey = wr::AsImageKey(key.value());
-    return NS_OK;
-  }
-
-  return NS_ERROR_FAILURE;
+  return key;
 }
 
-void BlobSurfaceProvider::InvalidateRecording() {
+void SourceSurfaceBlobImage::MarkDirty() {
   MOZ_ASSERT(NS_IsMainThread());
 
   auto i = mKeys.Length();
   while (i > 0) {
     --i;
     BlobImageKeyData& entry = mKeys[i];
     if (entry.mManager->IsDestroyed()) {
       mKeys.RemoveElementAt(i);
     } else {
       entry.mDirty = true;
     }
   }
 }
 
-Maybe<BlobImageKeyData> BlobSurfaceProvider::RecordDrawing(
+Maybe<BlobImageKeyData> SourceSurfaceBlobImage::RecordDrawing(
     WebRenderLayerManager* aManager, wr::IpcResourceUpdateQueue& aResources,
     Maybe<wr::BlobImageKey> aBlobKey) {
   MOZ_ASSERT(!aManager->IsDestroyed());
 
   if (mSVGDocumentWrapper->IsDrawing()) {
     return Nothing();
   }
 
   // This is either our first pass, or we have a stale key requiring us to
   // re-record the SVG image draw commands.
   auto* rootManager = aManager->GetRenderRootStateManager();
   auto* wrBridge = aManager->WrBridge();
 
-  const auto& size = GetSurfaceKey().Size();
-  const auto& region = GetSurfaceKey().Region();
-  const auto& svgContext = GetSurfaceKey().SVGContext();
-
-  IntRect imageRect = region ? region->Rect() : IntRect(IntPoint(0, 0), size);
+  IntRect imageRect =
+      mRegion ? mRegion->Rect() : IntRect(IntPoint(0, 0), mSize);
   IntRect imageRectOrigin = imageRect - imageRect.TopLeft();
 
   std::vector<RefPtr<ScaledFont>> fonts;
   bool validFonts = true;
   RefPtr<WebRenderDrawEventRecorder> recorder =
       MakeAndAddRef<WebRenderDrawEventRecorder>(
           [&](MemStream& aStream,
               std::vector<RefPtr<ScaledFont>>& aScaledFonts) {
@@ -179,25 +173,25 @@ Maybe<BlobImageKeyData> BlobSurfaceProvi
       gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget();
   RefPtr<DrawTarget> dt =
       Factory::CreateRecordingDrawTarget(recorder, dummyDt, imageRectOrigin);
 
   if (!dt || !dt->IsValid()) {
     return Nothing();
   }
 
-  bool contextPaint = svgContext && svgContext->GetContextPaint();
+  bool contextPaint = mSVGContext && mSVGContext->GetContextPaint();
 
-  float animTime = (GetSurfaceKey().Playback() == PlaybackType::eStatic)
+  float animTime = (mWhichFrame == imgIContainer::FRAME_FIRST)
                        ? 0.0f
                        : mSVGDocumentWrapper->GetCurrentTimeAsFloat();
 
-  IntSize viewportSize = size;
-  if (svgContext) {
-    auto cssViewportSize = svgContext->GetViewportSize();
+  IntSize viewportSize = mSize;
+  if (mSVGContext) {
+    auto cssViewportSize = mSVGContext->GetViewportSize();
     if (cssViewportSize) {
       // XXX losing unit
       viewportSize.SizeTo(cssViewportSize->width, cssViewportSize->height);
     }
   }
 
   {
     // Get (& sanity-check) the helper-doc's presShell
@@ -207,34 +201,34 @@ Maybe<BlobImageKeyData> BlobSurfaceProvi
     nsPresContext* presContext = presShell->GetPresContext();
     MOZ_ASSERT(presContext, "pres shell w/out pres context");
 
     auto* doc = presShell->GetDocument();
     [[maybe_unused]] nsIURI* uri = doc ? doc->GetDocumentURI() : nullptr;
     AUTO_PROFILER_LABEL_DYNAMIC_NSCSTRING(
         "SVG Image recording", GRAPHICS,
         nsPrintfCString("(%d,%d) %dx%d from %dx%d %s", imageRect.x, imageRect.y,
-                        imageRect.width, imageRect.height, size.width,
-                        size.height,
+                        imageRect.width, imageRect.height, mSize.width,
+                        mSize.height,
                         uri ? uri->GetSpecOrDefault().get() : "N/A"));
 
-    AutoRestoreSVGState autoRestore(svgContext, animTime, mSVGDocumentWrapper,
+    AutoRestoreSVGState autoRestore(mSVGContext, animTime, mSVGDocumentWrapper,
                                     contextPaint);
 
     mSVGDocumentWrapper->UpdateViewportBounds(viewportSize);
     mSVGDocumentWrapper->FlushImageTransformInvalidation();
 
     RefPtr<gfxContext> ctx = gfxContext::CreateOrNull(dt);
     MOZ_ASSERT(ctx);  // Already checked the draw target above.
 
     nsRect svgRect;
     auto auPerDevPixel = presContext->AppUnitsPerDevPixel();
-    if (size != viewportSize) {
-      auto scaleX = double(size.width) / viewportSize.width;
-      auto scaleY = double(size.height) / viewportSize.height;
+    if (mSize != viewportSize) {
+      auto scaleX = double(mSize.width) / viewportSize.width;
+      auto scaleY = double(mSize.height) / viewportSize.height;
       ctx->SetMatrix(Matrix::Scaling(float(scaleX), float(scaleY)));
 
       auto scaledVisibleRect = IntRectToRect(imageRect);
       scaledVisibleRect.Scale(float(auPerDevPixel / scaleX),
                               float(auPerDevPixel / scaleY));
       scaledVisibleRect.Round();
       svgRect.SetRect(
           int32_t(scaledVisibleRect.x), int32_t(scaledVisibleRect.y),
rename from image/BlobSurfaceProvider.h
rename to image/SourceSurfaceBlobImage.h
--- a/image/BlobSurfaceProvider.h
+++ b/image/SourceSurfaceBlobImage.h
@@ -1,23 +1,22 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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_IMAGE_BLOBSURFACEPROVIDER_H_
-#define MOZILLA_IMAGE_BLOBSURFACEPROVIDER_H_
+#ifndef MOZILLA_IMAGE_SOURCESURFACEBLOBIMAGE_H_
+#define MOZILLA_IMAGE_SOURCESURFACEBLOBIMAGE_H_
 
 #include "mozilla/Maybe.h"
 #include "mozilla/SVGImageContext.h"
 #include "mozilla/gfx/2D.h"
 #include "mozilla/layers/WebRenderLayerManager.h"
 #include "ImageRegion.h"
-#include "ISurfaceProvider.h"
 
 #include <vector>
 
 namespace mozilla {
 namespace image {
 
 class BlobImageKeyData final {
  public:
@@ -66,75 +65,68 @@ namespace mozilla {
 namespace wr {
 class IpcResourceUpdateQueue;
 }  // namespace wr
 
 namespace image {
 class SVGDocumentWrapper;
 
 /**
- * An ISurfaceProvider that manages blob recordings of SVG images. Unlike the
- * rasterized ISurfaceProviders, it only provides a recording which may be
- * replayed in the compositor process by WebRender. It may be invalidated
- * directly in order to reuse the resource ids and underlying buffers when the
- * SVG image has changed (e.g. it is animated).
+ * This class is used to wrap blob recordings stored in ImageContainers, used
+ * by SVG images. Unlike rasterized images stored in SourceSurfaceSharedData,
+ * each SourceSurfaceBlobImage can only be used by one WebRenderLayerManager
+ * because the recording is tied to a particular instance.
  */
-class BlobSurfaceProvider final : public ISurfaceProvider {
+class SourceSurfaceBlobImage final : public gfx::SourceSurface {
  public:
-  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(BlobSurfaceProvider, override)
+  MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(SourceSurfaceBlobImage, override)
+
+  SourceSurfaceBlobImage(SVGDocumentWrapper* aSVGDocumentWrapper,
+                         const Maybe<SVGImageContext>& aSVGContext,
+                         const Maybe<ImageIntRegion>& aRegion,
+                         const gfx::IntSize& aSize, uint32_t aWhichFrame,
+                         uint32_t aImageFlags);
 
-  BlobSurfaceProvider(ImageKey aImageKey, const SurfaceKey& aSurfaceKey,
-                      SVGDocumentWrapper* aSVGDocumentWrapper,
-                      uint32_t aImageFlags);
+  Maybe<wr::BlobImageKey> UpdateKey(layers::WebRenderLayerManager* aManager,
+                                    wr::IpcResourceUpdateQueue& aResources);
+
+  void MarkDirty();
 
-  bool IsFinished() const override { return true; }
-
-  size_t LogicalSizeInBytes() const override {
-    const gfx::IntSize& size = GetSurfaceKey().Size();
-    return size.width * size.height * sizeof(uint32_t);
+  gfx::SurfaceType GetType() const override {
+    return gfx::SurfaceType::BLOB_IMAGE;
+  }
+  gfx::IntSize GetSize() const override { return mSize; }
+  gfx::SurfaceFormat GetFormat() const override {
+    return gfx::SurfaceFormat::OS_RGBA;
+  }
+  already_AddRefed<gfx::DataSourceSurface> GetDataSurface() override {
+    return nullptr;
   }
 
-  nsresult UpdateKey(layers::RenderRootStateManager* aManager,
-                     wr::IpcResourceUpdateQueue& aResources,
-                     wr::ImageKey& aKey) override;
-
-  void InvalidateRecording() override;
-
-  void AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
-                              const AddSizeOfCb& aCallback) override {
-    AddSizeOfCbData metadata;
-    metadata.mFinished = true;
-    metadata.mHeapBytes += mKeys.ShallowSizeOfExcludingThis(aMallocSizeOf);
-
-    gfx::SourceSurface::SizeOfInfo info;
-    info.AddType(gfx::SurfaceType::BLOB_IMAGE);
-    metadata.Accumulate(info);
-
-    aCallback(metadata);
+  void SizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
+                           SizeOfInfo& aInfo) const override {
+    aInfo.AddType(gfx::SurfaceType::BLOB_IMAGE);
+    aInfo.mHeapBytes += mKeys.ShallowSizeOfExcludingThis(aMallocSizeOf);
   }
 
- protected:
-  DrawableFrameRef DrawableRef(size_t aFrame) override {
-    MOZ_ASSERT_UNREACHABLE("BlobSurfaceProvider::DrawableRef not supported!");
-    return DrawableFrameRef();
-  }
-  bool IsLocked() const override { return true; }
-  void SetLocked(bool) override {}
-
  private:
-  ~BlobSurfaceProvider() override;
+  ~SourceSurfaceBlobImage() override;
 
   Maybe<BlobImageKeyData> RecordDrawing(layers::WebRenderLayerManager* aManager,
                                         wr::IpcResourceUpdateQueue& aResources,
                                         Maybe<wr::BlobImageKey> aBlobKey);
 
   static void DestroyKeys(const AutoTArray<BlobImageKeyData, 1>& aKeys);
 
   AutoTArray<BlobImageKeyData, 1> mKeys;
 
   RefPtr<image::SVGDocumentWrapper> mSVGDocumentWrapper;
+  Maybe<SVGImageContext> mSVGContext;
+  Maybe<ImageIntRegion> mRegion;
+  gfx::IntSize mSize;
+  uint32_t mWhichFrame;
   uint32_t mImageFlags;
 };
 
 }  // namespace image
 }  // namespace mozilla
 
-#endif /* MOZILLA_IMAGE_BLOBSURFACEPROVIDER_H_ */
+#endif /* MOZILLA_IMAGE_SOURCESURFACEBLOBIMAGE_H_ */
--- a/image/SurfaceCache.cpp
+++ b/image/SurfaceCache.cpp
@@ -171,18 +171,16 @@ class CachedSurface {
   CostEntry GetCostEntry() {
     return image::CostEntry(WrapNotNull(this), mProvider->LogicalSizeInBytes());
   }
 
   size_t ShallowSizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const {
     return aMallocSizeOf(this) + aMallocSizeOf(mProvider.get());
   }
 
-  void InvalidateRecording() { mProvider->InvalidateRecording(); }
-
   // A helper type used by SurfaceCacheImpl::CollectSizeOfSurfaces.
   struct MOZ_STACK_CLASS SurfaceMemoryReport {
     SurfaceMemoryReport(nsTArray<SurfaceMemoryCounter>& aCounters,
                         MallocSizeOf aMallocSizeOf)
         : mCounters(aCounters), mMallocSizeOf(aMallocSizeOf) {}
 
     void Add(NotNull<CachedSurface*> aCachedSurface, bool aIsFactor2) {
       if (aCachedSurface->IsPlaceholder()) {
@@ -190,20 +188,20 @@ class CachedSurface {
       }
 
       // Record the memory used by the ISurfaceProvider. This may not have a
       // straightforward relationship to the size of the surface that
       // DrawableRef() returns if the surface is generated dynamically. (i.e.,
       // for surfaces with PlaybackType::eAnimated.)
       aCachedSurface->mProvider->AddSizeOfExcludingThis(
           mMallocSizeOf, [&](ISurfaceProvider::AddSizeOfCbData& aMetadata) {
-            SurfaceMemoryCounter counter(aCachedSurface->GetSurfaceKey(),
-                                         aCachedSurface->IsLocked(),
-                                         aCachedSurface->CannotSubstitute(),
-                                         aIsFactor2, aMetadata.mFinished);
+            SurfaceMemoryCounter counter(
+                aCachedSurface->GetSurfaceKey(), aMetadata.mSurface,
+                aCachedSurface->IsLocked(), aCachedSurface->CannotSubstitute(),
+                aIsFactor2, aMetadata.mFinished);
 
             counter.Values().SetDecodedHeap(aMetadata.mHeapBytes);
             counter.Values().SetDecodedNonHeap(aMetadata.mNonHeapBytes);
             counter.Values().SetDecodedUnknown(aMetadata.mUnknownBytes);
             counter.Values().SetExternalHandles(aMetadata.mExternalHandles);
             counter.Values().SetFrameIndex(aMetadata.mIndex);
             counter.Values().SetExternalId(aMetadata.mExternalId);
             counter.Values().SetSurfaceTypes(aMetadata.mTypes);
@@ -273,24 +271,18 @@ class ImageSurfaceCache {
       bytes += value->ShallowSizeOfIncludingThis(aMallocSizeOf);
     }
     return bytes;
   }
 
   [[nodiscard]] bool Insert(NotNull<CachedSurface*> aSurface) {
     MOZ_ASSERT(!mLocked || aSurface->IsPlaceholder() || aSurface->IsLocked(),
                "Inserting an unlocked surface for a locked image");
-    const auto& surfaceKey = aSurface->GetSurfaceKey();
-    if (surfaceKey.Region()) {
-      // We don't allow substitutes for surfaces with regions, so we don't want
-      // to allow factor of 2 mode pruning to release these surfaces.
-      aSurface->SetCannotSubstitute();
-    }
-    return mSurfaces.InsertOrUpdate(surfaceKey, RefPtr<CachedSurface>{aSurface},
-                                    fallible);
+    return mSurfaces.InsertOrUpdate(aSurface->GetSurfaceKey(),
+                                    RefPtr<CachedSurface>{aSurface}, fallible);
   }
 
   already_AddRefed<CachedSurface> Remove(NotNull<CachedSurface*> aSurface) {
     MOZ_ASSERT(mSurfaces.GetWeak(aSurface->GetSurfaceKey()),
                "Should not be removing a surface we don't have");
 
     RefPtr<CachedSurface> surface;
     mSurfaces.Remove(aSurface->GetSurfaceKey(), getter_AddRefs(surface));
@@ -329,20 +321,16 @@ class ImageSurfaceCache {
       const SurfaceKey& aIdealKey) {
     // Try for an exact match first.
     RefPtr<CachedSurface> exactMatch;
     mSurfaces.Get(aIdealKey, getter_AddRefs(exactMatch));
     if (exactMatch) {
       if (exactMatch->IsDecoded()) {
         return MakeTuple(exactMatch.forget(), MatchType::EXACT, IntSize());
       }
-    } else if (aIdealKey.Region()) {
-      // We cannot substitute if we have a region. Allow it to create an exact
-      // match.
-      return MakeTuple(exactMatch.forget(), MatchType::NOT_FOUND, IntSize());
     } else if (!mFactor2Mode) {
       // If no exact match is found, and we are not in factor of 2 mode, then
       // we know that we will trigger a decode because at best we will provide
       // a substitute. Make sure we switch now to factor of 2 mode if necessary.
       MaybeSetFactor2Mode();
     }
 
     // Try for a best match second, if using compact.
@@ -360,18 +348,18 @@ class ImageSurfaceCache {
     }
 
     // There's no perfect match, so find the best match we can.
     RefPtr<CachedSurface> bestMatch;
     for (const auto& value : Values()) {
       NotNull<CachedSurface*> current = WrapNotNull(value);
       const SurfaceKey& currentKey = current->GetSurfaceKey();
 
-      // We never match a placeholder or a surface with a region.
-      if (current->IsPlaceholder() || currentKey.Region()) {
+      // We never match a placeholder.
+      if (current->IsPlaceholder()) {
         continue;
       }
       // Matching the playback type and SVG context is required.
       if (currentKey.Playback() != aIdealKey.Playback() ||
           currentKey.SVGContext() != aIdealKey.SVGContext()) {
         continue;
       }
       // Matching the flags is required.
@@ -537,38 +525,16 @@ class ImageSurfaceCache {
     }
 
     // We should never leave factor of 2 mode due to pruning in of itself, but
     // if we discarded surfaces due to the volatile buffers getting released,
     // it is possible.
     AfterMaybeRemove();
   }
 
-  template <typename Function>
-  bool Invalidate(Function&& aRemoveCallback) {
-    // Remove all non-blob recordings from the cache. Invalidate any blob
-    // recordings.
-    bool foundRecording = false;
-    for (auto iter = mSurfaces.Iter(); !iter.Done(); iter.Next()) {
-      NotNull<CachedSurface*> current = WrapNotNull(iter.UserData());
-
-      if (current->GetSurfaceKey().Flags() & SurfaceFlags::RECORD_BLOB) {
-        foundRecording = true;
-        current->InvalidateRecording();
-        continue;
-      }
-
-      aRemoveCallback(current);
-      iter.Remove();
-    }
-
-    AfterMaybeRemove();
-    return foundRecording;
-  }
-
   IntSize SuggestedSize(const IntSize& aSize) const {
     IntSize suggestedSize = SuggestedSizeInternal(aSize);
     if (mIsVectorImage) {
       suggestedSize = SurfaceCache::ClampVectorSize(suggestedSize);
     }
     return suggestedSize;
   }
 
@@ -1045,18 +1011,17 @@ class SurfaceCacheImpl final : public ns
       Remove(WrapNotNull(surface), /* aStopTracking */ true, aAutoLock);
     }
 
     MOZ_ASSERT_IF(matchType == MatchType::EXACT,
                   surface->GetSurfaceKey() == aSurfaceKey);
     MOZ_ASSERT_IF(
         matchType == MatchType::SUBSTITUTE_BECAUSE_NOT_FOUND ||
             matchType == MatchType::SUBSTITUTE_BECAUSE_PENDING,
-        surface->GetSurfaceKey().Region() == aSurfaceKey.Region() &&
-            surface->GetSurfaceKey().SVGContext() == aSurfaceKey.SVGContext() &&
+        surface->GetSurfaceKey().SVGContext() == aSurfaceKey.SVGContext() &&
             surface->GetSurfaceKey().Playback() == aSurfaceKey.Playback() &&
             surface->GetSurfaceKey().Flags() == aSurfaceKey.Flags());
 
     if (matchType == MatchType::EXACT ||
         matchType == MatchType::SUBSTITUTE_BECAUSE_BEST) {
       if (aMarkUsed &&
           !MarkUsed(WrapNotNull(surface), WrapNotNull(cache), aAutoLock)) {
         Remove(WrapNotNull(surface), /* aStopTracking */ false, aAutoLock);
@@ -1162,34 +1127,16 @@ class SurfaceCacheImpl final : public ns
       StopTracking(aSurface, /* aIsTracked */ true, aAutoLock);
       // Individual surfaces must be freed outside the lock.
       mCachedSurfacesDiscard.AppendElement(aSurface);
     });
 
     MaybeRemoveEmptyCache(aImageKey, cache);
   }
 
-  bool InvalidateImage(const ImageKey aImageKey,
-                       const StaticMutexAutoLock& aAutoLock) {
-    RefPtr<ImageSurfaceCache> cache = GetImageCache(aImageKey);
-    if (!cache) {
-      return false;  // No cached surfaces for this image, so nothing to do.
-    }
-
-    bool rv = cache->Invalidate(
-        [this, &aAutoLock](NotNull<CachedSurface*> aSurface) -> void {
-          StopTracking(aSurface, /* aIsTracked */ true, aAutoLock);
-          // Individual surfaces must be freed outside the lock.
-          mCachedSurfacesDiscard.AppendElement(aSurface);
-        });
-
-    MaybeRemoveEmptyCache(aImageKey, cache);
-    return rv;
-  }
-
   void DiscardAll(const StaticMutexAutoLock& aAutoLock) {
     // Remove in order of cost because mCosts is an array and the other data
     // structures are all hash tables. Note that locked surfaces are not
     // removed, since they aren't present in mCosts.
     while (!mCosts.IsEmpty()) {
       Remove(mCosts.LastElement().Surface(), /* aStopTracking */ true,
              aAutoLock);
     }
@@ -1766,30 +1713,16 @@ void SurfaceCache::PruneImage(const Imag
     if (sInstance) {
       sInstance->PruneImage(aImageKey, lock);
       sInstance->TakeDiscard(discard, lock);
     }
   }
 }
 
 /* static */
-bool SurfaceCache::InvalidateImage(const ImageKey aImageKey) {
-  nsTArray<RefPtr<CachedSurface>> discard;
-  bool rv = false;
-  {
-    StaticMutexAutoLock lock(sInstanceMutex);
-    if (sInstance) {
-      rv = sInstance->InvalidateImage(aImageKey, lock);
-      sInstance->TakeDiscard(discard, lock);
-    }
-  }
-  return rv;
-}
-
-/* static */
 void SurfaceCache::DiscardAll() {
   nsTArray<RefPtr<CachedSurface>> discard;
   {
     StaticMutexAutoLock lock(sInstanceMutex);
     if (sInstance) {
       sInstance->DiscardAll(lock);
       sInstance->TakeDiscard(discard, lock);
     }
--- a/image/SurfaceCache.h
+++ b/image/SurfaceCache.h
@@ -16,130 +16,116 @@
 #include "mozilla/MemoryReporting.h"  // for MallocSizeOf
 #include "mozilla/NotNull.h"
 #include "mozilla/SVGImageContext.h"  // for SVGImageContext
 #include "mozilla/gfx/2D.h"           // for SourceSurface
 #include "mozilla/gfx/Point.h"        // for mozilla::gfx::IntSize
 #include "gfx2DGlue.h"
 #include "gfxPoint.h"  // for gfxSize
 #include "nsCOMPtr.h"  // for already_AddRefed
-#include "ImageRegion.h"
 #include "PlaybackType.h"
 #include "SurfaceFlags.h"
 
 namespace mozilla {
 namespace image {
 
-class ImageResource;
+class Image;
 class ISurfaceProvider;
 class LookupResult;
 class SurfaceCacheImpl;
 struct SurfaceMemoryCounter;
 
 /*
  * ImageKey contains the information we need to look up all SurfaceCache entries
  * for a particular image.
  */
-using ImageKey = ImageResource*;
+typedef Image* ImageKey;
 
 /*
  * SurfaceKey contains the information we need to look up a specific
  * SurfaceCache entry. Together with an ImageKey, this uniquely identifies the
  * surface.
  *
  * Callers should construct a SurfaceKey using the appropriate helper function
  * for their image type - either RasterSurfaceKey or VectorSurfaceKey.
  */
 class SurfaceKey {
   typedef gfx::IntSize IntSize;
 
  public:
   bool operator==(const SurfaceKey& aOther) const {
-    return aOther.mSize == mSize && aOther.mRegion == mRegion &&
-           aOther.mSVGContext == mSVGContext && aOther.mPlayback == mPlayback &&
-           aOther.mFlags == mFlags;
+    return aOther.mSize == mSize && aOther.mSVGContext == mSVGContext &&
+           aOther.mPlayback == mPlayback && aOther.mFlags == mFlags;
   }
 
   PLDHashNumber Hash() const {
     PLDHashNumber hash = HashGeneric(mSize.width, mSize.height);
-    hash = AddToHash(hash, mRegion.map(HashIIR).valueOr(0));
     hash = AddToHash(hash, mSVGContext.map(HashSIC).valueOr(0));
     hash = AddToHash(hash, uint8_t(mPlayback), uint32_t(mFlags));
     return hash;
   }
 
   SurfaceKey CloneWithSize(const IntSize& aSize) const {
-    return SurfaceKey(aSize, mRegion, mSVGContext, mPlayback, mFlags);
+    return SurfaceKey(aSize, mSVGContext, mPlayback, mFlags);
   }
 
   const IntSize& Size() const { return mSize; }
-  const Maybe<ImageIntRegion>& Region() const { return mRegion; }
   const Maybe<SVGImageContext>& SVGContext() const { return mSVGContext; }
   PlaybackType Playback() const { return mPlayback; }
   SurfaceFlags Flags() const { return mFlags; }
 
  private:
-  SurfaceKey(const IntSize& aSize, const Maybe<ImageIntRegion>& aRegion,
-             const Maybe<SVGImageContext>& aSVGContext, PlaybackType aPlayback,
-             SurfaceFlags aFlags)
+  SurfaceKey(const IntSize& aSize, const Maybe<SVGImageContext>& aSVGContext,
+             PlaybackType aPlayback, SurfaceFlags aFlags)
       : mSize(aSize),
-        mRegion(aRegion),
         mSVGContext(aSVGContext),
         mPlayback(aPlayback),
         mFlags(aFlags) {}
 
-  static PLDHashNumber HashIIR(const ImageIntRegion& aIIR) {
-    return aIIR.Hash();
-  }
-
   static PLDHashNumber HashSIC(const SVGImageContext& aSIC) {
     return aSIC.Hash();
   }
 
   friend SurfaceKey RasterSurfaceKey(const IntSize&, SurfaceFlags,
                                      PlaybackType);
   friend SurfaceKey VectorSurfaceKey(const IntSize&,
                                      const Maybe<SVGImageContext>&);
-  friend SurfaceKey VectorSurfaceKey(const IntSize&,
-                                     const Maybe<ImageIntRegion>&,
-                                     const Maybe<SVGImageContext>&,
-                                     SurfaceFlags, PlaybackType);
+  friend SurfaceKey ContainerSurfaceKey(
+      const gfx::IntSize& aSize, const Maybe<SVGImageContext>& aSVGContext,
+      SurfaceFlags aFlags);
 
   IntSize mSize;
-  Maybe<ImageIntRegion> mRegion;
   Maybe<SVGImageContext> mSVGContext;
   PlaybackType mPlayback;
   SurfaceFlags mFlags;
 };
 
 inline SurfaceKey RasterSurfaceKey(const gfx::IntSize& aSize,
                                    SurfaceFlags aFlags,
                                    PlaybackType aPlayback) {
-  return SurfaceKey(aSize, Nothing(), Nothing(), aPlayback, aFlags);
-}
-
-inline SurfaceKey VectorSurfaceKey(const gfx::IntSize& aSize,
-                                   const Maybe<ImageIntRegion>& aRegion,
-                                   const Maybe<SVGImageContext>& aSVGContext,
-                                   SurfaceFlags aFlags,
-                                   PlaybackType aPlayback) {
-  return SurfaceKey(aSize, aRegion, aSVGContext, aPlayback, aFlags);
+  return SurfaceKey(aSize, Nothing(), aPlayback, aFlags);
 }
 
 inline SurfaceKey VectorSurfaceKey(const gfx::IntSize& aSize,
                                    const Maybe<SVGImageContext>& aSVGContext) {
   // We don't care about aFlags for VectorImage because none of the flags we
   // have right now influence VectorImage's rendering. If we add a new flag that
   // *does* affect how a VectorImage renders, we'll have to change this.
   // Similarly, we don't accept a PlaybackType parameter because we don't
   // currently cache frames of animated SVG images.
-  return SurfaceKey(aSize, Nothing(), aSVGContext, PlaybackType::eStatic,
+  return SurfaceKey(aSize, aSVGContext, PlaybackType::eStatic,
                     DefaultSurfaceFlags());
 }
 
+inline SurfaceKey ContainerSurfaceKey(const gfx::IntSize& aSize,
+                                      const Maybe<SVGImageContext>& aSVGContext,
+                                      SurfaceFlags aFlags) {
+  return SurfaceKey(aSize, aSVGContext, PlaybackType::eStatic, aFlags);
+}
+
 /**
  * AvailabilityState is used to track whether an ISurfaceProvider has a surface
  * available or is just a placeholder.
  *
  * To ensure that availability changes are atomic (and especially that internal
  * SurfaceCache code doesn't have to deal with asynchronous availability
  * changes), an ISurfaceProvider which starts as a placeholder can only reveal
  * the fact that it now has a surface available via a call to
@@ -419,27 +405,16 @@ struct SurfaceCache {
    * it is able substitute that entry with. Note that this only applies if the
    * image is in factor of 2 mode. If it is not, this operation does nothing.
    *
    * @param aImageKey  The image whose cache which should be pruned.
    */
   static void PruneImage(const ImageKey aImageKey);
 
   /**
-   * Removes all rasterized cache entries (including placeholders) associated
-   * with the given image from the cache. Any blob recordings are marked as
-   * dirty and must be regenerated.
-   *
-   * @param aImageKey  The image whose cache which should be regenerated.
-   *
-   * @returns true if any recordings were invalidated, else false.
-   */
-  static bool InvalidateImage(const ImageKey aImageKey);
-
-  /**
    * Evicts all evictable entries from the cache.
    *
    * All entries are evictable except for entries associated with locked images.
    * Non-evictable entries can only be removed by RemoveImage().
    */
   static void DiscardAll();
 
   /**
--- a/image/SurfaceFlags.h
+++ b/image/SurfaceFlags.h
@@ -15,18 +15,17 @@ namespace image {
 /**
  * Flags that change the output a decoder generates. Because different
  * combinations of these flags result in logically different surfaces, these
  * flags must be taken into account in SurfaceCache lookups.
  */
 enum class SurfaceFlags : uint8_t {
   NO_PREMULTIPLY_ALPHA = 1 << 0,
   NO_COLORSPACE_CONVERSION = 1 << 1,
-  TO_SRGB_COLORSPACE = 1 << 2,
-  RECORD_BLOB = 1 << 3,
+  TO_SRGB_COLORSPACE = 2 << 1,
 };
 MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(SurfaceFlags)
 
 /**
  * @return the default set of surface flags.
  */
 inline SurfaceFlags DefaultSurfaceFlags() { return SurfaceFlags(); }
 
@@ -40,19 +39,16 @@ inline SurfaceFlags ToSurfaceFlags(uint3
     flags |= SurfaceFlags::NO_PREMULTIPLY_ALPHA;
   }
   if (aFlags & imgIContainer::FLAG_DECODE_NO_COLORSPACE_CONVERSION) {
     flags |= SurfaceFlags::NO_COLORSPACE_CONVERSION;
   }
   if (aFlags & imgIContainer::FLAG_DECODE_TO_SRGB_COLORSPACE) {
     flags |= SurfaceFlags::TO_SRGB_COLORSPACE;
   }
-  if (aFlags & imgIContainer::FLAG_RECORD_BLOB) {
-    flags |= SurfaceFlags::RECORD_BLOB;
-  }
   return flags;
 }
 
 /**
  * Given a set of SurfaceFlags, returns a set of imgIContainer FLAG_* flags with
  * the corresponding flags set.
  */
 inline uint32_t FromSurfaceFlags(SurfaceFlags aFlags) {
@@ -61,18 +57,15 @@ inline uint32_t FromSurfaceFlags(Surface
     flags |= imgIContainer::FLAG_DECODE_NO_PREMULTIPLY_ALPHA;
   }
   if (aFlags & SurfaceFlags::NO_COLORSPACE_CONVERSION) {
     flags |= imgIContainer::FLAG_DECODE_NO_COLORSPACE_CONVERSION;
   }
   if (aFlags & SurfaceFlags::TO_SRGB_COLORSPACE) {
     flags |= imgIContainer::FLAG_DECODE_TO_SRGB_COLORSPACE;
   }
-  if (aFlags & SurfaceFlags::RECORD_BLOB) {
-    flags |= imgIContainer::FLAG_RECORD_BLOB;
-  }
   return flags;
 }
 
 }  // namespace image
 }  // namespace mozilla
 
 #endif  // mozilla_image_SurfaceFlags_h
--- a/image/VectorImage.cpp
+++ b/image/VectorImage.cpp
@@ -32,26 +32,25 @@
 #include "nsRect.h"
 #include "nsString.h"
 #include "nsStubDocumentObserver.h"
 #include "nsWindowSizes.h"
 #include "ImageRegion.h"
 #include "ISurfaceProvider.h"
 #include "LookupResult.h"
 #include "Orientation.h"
+#include "SourceSurfaceBlobImage.h"
 #include "SVGDocumentWrapper.h"
 #include "SVGDrawingCallback.h"
 #include "SVGDrawingParameters.h"
 #include "nsIDOMEventListener.h"
 #include "SurfaceCache.h"
-#include "BlobSurfaceProvider.h"
 #include "mozilla/dom/Document.h"
 #include "mozilla/dom/DocumentInlines.h"
 #include "mozilla/image/Resolution.h"
-#include "WindowRenderer.h"
 
 namespace mozilla {
 
 using namespace dom;
 using namespace dom::SVGPreserveAspectRatio_Binding;
 using namespace gfx;
 using namespace layers;
 
@@ -367,16 +366,23 @@ size_t VectorImage::SizeOfSourceWithComp
     // size accurately, so we just use a constant value of 100KB, which will
     // generally underestimate the true size.
     return 100 * 1024;
   }
 
   return windowSizes.getTotalSize();
 }
 
+void VectorImage::CollectSizeOfSurfaces(
+    nsTArray<SurfaceMemoryCounter>& aCounters,
+    MallocSizeOf aMallocSizeOf) const {
+  SurfaceCache::CollectSizeOfSurfaces(ImageKey(this), aCounters, aMallocSizeOf);
+  ImageResource::CollectSizeOfSurfaces(aCounters, aMallocSizeOf);
+}
+
 nsresult VectorImage::OnImageDataComplete(nsIRequest* aRequest,
                                           nsISupports* aContext,
                                           nsresult aStatus, bool aLastPart) {
   // Call our internal OnStopRequest method, which only talks to our embedded
   // SVG document. This won't have any effect on our ProgressTracker.
   nsresult finalStatus = OnStopRequest(aRequest, aStatus);
 
   // Give precedence to Necko failure codes.
@@ -510,20 +516,22 @@ void VectorImage::SendInvalidationNotifi
   // InvalidateObserversOnNextRefreshDriverTick, because RequestRefresh is never
   // called for them. Ordinarily this isn't needed, since we send out
   // invalidation notifications in OnSVGDocumentLoaded, but in rare cases the
   // SVG document may not be 100% ready to render at that time. In those cases
   // we would miss the subsequent invalidations if we didn't send out the
   // notifications indirectly in |InvalidateObservers...|.
 
   mHasPendingInvalidation = false;
+  SurfaceCache::RemoveImage(ImageKey(this));
 
-  if (SurfaceCache::InvalidateImage(ImageKey(this))) {
-    // If we still have recordings in the cache, make sure we handle future
-    // invalidations.
+  if (UpdateImageContainer(Nothing())) {
+    // If we have image containers, that means we probably won't get a Draw call
+    // from the owner since they are using the container. We must assume all
+    // invalidations need to be handled.
     MOZ_ASSERT(mRenderingObserver, "Should have a rendering observer by now");
     mRenderingObserver->ResumeHonoringInvalidations();
   }
 
   if (mProgressTracker) {
     mProgressTracker->SyncNotifyProgress(FLAG_FRAME_COMPLETE,
                                          GetMaxSizedIntRect());
   }
@@ -607,20 +615,20 @@ VectorImage::GetType(uint16_t* aType) {
   NS_ENSURE_ARG_POINTER(aType);
 
   *aType = imgIContainer::TYPE_VECTOR;
   return NS_OK;
 }
 
 //******************************************************************************
 NS_IMETHODIMP
-VectorImage::GetProviderId(uint32_t* aId) {
+VectorImage::GetProducerId(uint32_t* aId) {
   NS_ENSURE_ARG_POINTER(aId);
 
-  *aId = ImageResource::GetImageProviderId();
+  *aId = ImageResource::GetImageProducerId();
   return NS_OK;
 }
 
 //******************************************************************************
 NS_IMETHODIMP
 VectorImage::GetAnimated(bool* aAnimated) {
   if (mError || !mIsFullyLoaded) {
     return NS_ERROR_FAILURE;
@@ -673,71 +681,135 @@ VectorImage::GetFrame(uint32_t aWhichFra
   }
 
   return GetFrameAtSize(imageIntSize, aWhichFrame, aFlags);
 }
 
 NS_IMETHODIMP_(already_AddRefed<SourceSurface>)
 VectorImage::GetFrameAtSize(const IntSize& aSize, uint32_t aWhichFrame,
                             uint32_t aFlags) {
-  MOZ_ASSERT(aWhichFrame <= FRAME_MAX_VALUE);
-
   AutoProfilerImagePaintMarker PROFILER_RAII(this);
 #ifdef DEBUG
   NotifyDrawingObservers();
 #endif
 
-  if (aSize.IsEmpty() || aWhichFrame > FRAME_MAX_VALUE || mError ||
-      !mIsFullyLoaded) {
-    return nullptr;
+  auto result =
+      GetFrameInternal(aSize, Nothing(), Nothing(), aWhichFrame, aFlags);
+  return Get<2>(result).forget();
+}
+
+Tuple<ImgDrawResult, IntSize, RefPtr<SourceSurface>>
+VectorImage::GetFrameInternal(const IntSize& aSize,
+                              const Maybe<SVGImageContext>& aSVGContext,
+                              const Maybe<ImageIntRegion>& aRegion,
+                              uint32_t aWhichFrame, uint32_t aFlags) {
+  MOZ_ASSERT(aWhichFrame <= FRAME_MAX_VALUE);
+
+  if (aSize.IsEmpty() || aWhichFrame > FRAME_MAX_VALUE) {
+    return MakeTuple(ImgDrawResult::BAD_ARGS, aSize, RefPtr<SourceSurface>());
+  }
+
+  if (mError) {
+    return MakeTuple(ImgDrawResult::BAD_IMAGE, aSize, RefPtr<SourceSurface>());
+  }
+
+  if (!mIsFullyLoaded) {
+    return MakeTuple(ImgDrawResult::NOT_READY, aSize, RefPtr<SourceSurface>());
   }
 
   uint32_t whichFrame = mHaveAnimations ? aWhichFrame : FRAME_FIRST;
 
   RefPtr<SourceSurface> sourceSurface;
   IntSize decodeSize;
   Tie(sourceSurface, decodeSize) =
-      LookupCachedSurface(aSize, Nothing(), aFlags);
+      LookupCachedSurface(aSize, aSVGContext, aFlags);
   if (sourceSurface) {
-    return sourceSurface.forget();
+    return MakeTuple(ImgDrawResult::SUCCESS, decodeSize,
+                     std::move(sourceSurface));
   }
 
   if (mSVGDocumentWrapper->IsDrawing()) {
     NS_WARNING("Refusing to make re-entrant call to VectorImage::Draw");
-    return nullptr;
+    return MakeTuple(ImgDrawResult::TEMPORARY_ERROR, decodeSize,
+                     RefPtr<SourceSurface>());
   }
 
   float animTime = (whichFrame == FRAME_FIRST)
                        ? 0.0f
                        : mSVGDocumentWrapper->GetCurrentTimeAsFloat();
 
+  // If we aren't given a region, create one that covers the whole SVG image.
+  ImageRegion region =
+      aRegion ? aRegion->ToImageRegion() : ImageRegion::Create(decodeSize);
+
   // By using a null gfxContext, we ensure that we will always attempt to
   // create a surface, even if we aren't capable of caching it (e.g. due to our
   // flags, having an animation, etc). Otherwise CreateSurface will assume that
   // the caller is capable of drawing directly to its own draw target if we
   // cannot cache.
-  Maybe<SVGImageContext> svgContext;
-  SVGDrawingParameters params(
-      nullptr, decodeSize, aSize, ImageRegion::Create(decodeSize),
-      SamplingFilter::POINT, svgContext, animTime, aFlags, 1.0);
+  SVGDrawingParameters params(nullptr, decodeSize, aSize, region,
+                              SamplingFilter::POINT, aSVGContext, animTime,
+                              aFlags, 1.0);
+
+  // Blob recorded vector images just create a simple surface responsible for
+  // generating blob keys and recording bindings. The recording won't happen
+  // until the caller requests the key after GetImageContainerAtSize.
+  if (aFlags & FLAG_RECORD_BLOB) {
+    RefPtr<SourceSurface> surface =
+        new SourceSurfaceBlobImage(mSVGDocumentWrapper, aSVGContext, aRegion,
+                                   decodeSize, whichFrame, aFlags);
+
+    return MakeTuple(ImgDrawResult::SUCCESS, decodeSize, std::move(surface));
+  }
 
   bool didCache;  // Was the surface put into the cache?
+  bool contextPaint = aSVGContext && aSVGContext->GetContextPaint();
 
-  AutoRestoreSVGState autoRestore(params, mSVGDocumentWrapper,
-                                  /* aContextPaint */ false);
+  AutoRestoreSVGState autoRestore(params, mSVGDocumentWrapper, contextPaint);
 
   RefPtr<gfxDrawable> svgDrawable = CreateSVGDrawable(params);
   RefPtr<SourceSurface> surface = CreateSurface(params, svgDrawable, didCache);
   if (!surface) {
     MOZ_ASSERT(!didCache);
-    return nullptr;
+    return MakeTuple(ImgDrawResult::TEMPORARY_ERROR, decodeSize,
+                     RefPtr<SourceSurface>());
   }
 
   SendFrameComplete(didCache, params.flags);
-  return surface.forget();
+  return MakeTuple(ImgDrawResult::SUCCESS, decodeSize, std::move(surface));
+}
+
+//******************************************************************************
+Tuple<ImgDrawResult, IntSize> VectorImage::GetImageContainerSize(
+    WindowRenderer* aRenderer, const IntSize& aSize, uint32_t aFlags) {
+  if (mError) {
+    return MakeTuple(ImgDrawResult::BAD_IMAGE, IntSize(0, 0));
+  }
+
+  if (!mIsFullyLoaded) {
+    return MakeTuple(ImgDrawResult::NOT_READY, IntSize(0, 0));
+  }
+
+  if (mHaveAnimations && !StaticPrefs::image_svg_blob_image()) {
+    // We don't support rasterizing animation SVGs. We can put them in a blob
+    // recording however instead of using fallback.
+    return MakeTuple(ImgDrawResult::NOT_SUPPORTED, IntSize(0, 0));
+  }
+
+  if (aRenderer->GetBackendType() != LayersBackend::LAYERS_WR) {
+    return MakeTuple(ImgDrawResult::NOT_SUPPORTED, IntSize(0, 0));
+  }
+
+  // We don't need to check if the size is too big since we only support
+  // WebRender backends.
+  if (aSize.IsEmpty()) {
+    return MakeTuple(ImgDrawResult::BAD_ARGS, IntSize(0, 0));
+  }
+
+  return MakeTuple(ImgDrawResult::SUCCESS, aSize);
 }
 
 NS_IMETHODIMP_(bool)
 VectorImage::IsImageContainerAvailable(WindowRenderer* aRenderer,
                                        uint32_t aFlags) {
   if (mError || !mIsFullyLoaded ||
       aRenderer->GetBackendType() != LayersBackend::LAYERS_WR) {
     return false;
@@ -749,165 +821,40 @@ VectorImage::IsImageContainerAvailable(W
     return false;
   }
 
   return true;
 }
 
 //******************************************************************************
 NS_IMETHODIMP_(ImgDrawResult)
-VectorImage::GetImageProvider(WindowRenderer* aRenderer,
-                              const gfx::IntSize& aSize,
-                              const Maybe<SVGImageContext>& aSVGContext,
-                              const Maybe<ImageIntRegion>& aRegion,
-                              uint32_t aFlags,
-                              WebRenderImageProvider** aProvider) {
-  MOZ_ASSERT(NS_IsMainThread());
-  MOZ_ASSERT(aRenderer);
-  MOZ_ASSERT(!(aFlags & FLAG_BYPASS_SURFACE_CACHE), "Unsupported flags");
-
-  // We don't need to check if the size is too big since we only support
-  // WebRender backends.
-  if (aSize.IsEmpty()) {
-    return ImgDrawResult::BAD_ARGS;
-  }
-
-  if (mError) {
-    return ImgDrawResult::BAD_IMAGE;
-  }
-
-  if (!mIsFullyLoaded) {
-    return ImgDrawResult::NOT_READY;
-  }
-
-  if (mHaveAnimations && !(aFlags & FLAG_RECORD_BLOB)) {
-    // We don't support rasterizing animation SVGs. We can put them in a blob
-    // recording however instead of using fallback.
-    return ImgDrawResult::NOT_SUPPORTED;
-  }
+VectorImage::GetImageContainerAtSize(WindowRenderer* aRenderer,
+                                     const gfx::IntSize& aSize,
+                                     const Maybe<SVGImageContext>& aSVGContext,
+                                     const Maybe<ImageIntRegion>& aRegion,
+                                     uint32_t aFlags,
+                                     layers::ImageContainer** aOutContainer) {
+  Maybe<SVGImageContext> newSVGContext;
+  MaybeRestrictSVGContext(newSVGContext, aSVGContext, aFlags);
 
-  // Only blob recordings support a region to restrict drawing.
-  const bool blobRecording = aFlags & FLAG_RECORD_BLOB;
-  MOZ_ASSERT_IF(!blobRecording, aRegion.isNothing());
-
-  LookupResult result(MatchType::NOT_FOUND);
-  auto playbackType =
-      mHaveAnimations ? PlaybackType::eAnimated : PlaybackType::eStatic;
-  auto surfaceFlags = ToSurfaceFlags(aFlags);
+  // The aspect ratio flag was already handled as part of the SVG context
+  // restriction above.
+  uint32_t flags = aFlags & ~(FLAG_FORCE_PRESERVEASPECTRATIO_NONE);
+  auto rv = GetImageContainerImpl(aRenderer, aSize,
+                                  newSVGContext ? newSVGContext : aSVGContext,
+                                  aRegion, flags, aOutContainer);
 
-  SurfaceKey surfaceKey =
-      VectorSurfaceKey(aSize, aRegion, aSVGContext, surfaceFlags, playbackType);
-  if ((aFlags & FLAG_SYNC_DECODE) || !(aFlags & FLAG_HIGH_QUALITY_SCALING)) {
-    result = SurfaceCache::Lookup(ImageKey(this), surfaceKey,
-                                  /* aMarkUsed = */ true);
-  } else {
-    result = SurfaceCache::LookupBestMatch(ImageKey(this), surfaceKey,
-                                           /* aMarkUsed = */ true);
-  }
-
-  // Unless we get a best match (exact or factor of 2 limited), then we want to
-  // generate a new recording/rerasterize, even if we have a substitute.
-  if (result && (result.Type() == MatchType::EXACT ||
-                 result.Type() == MatchType::SUBSTITUTE_BECAUSE_BEST)) {
-    result.Surface().TakeProvider(aProvider);
-    return ImgDrawResult::SUCCESS;
-  }
-
-  // Ensure we store the surface with the correct key if we switched to factor
-  // of 2 sizing or we otherwise got clamped.
-  IntSize rasterSize(aSize);
-  if (!result.SuggestedSize().IsEmpty()) {
-    rasterSize = result.SuggestedSize();
-    surfaceKey = surfaceKey.CloneWithSize(rasterSize);
+  // Invalidations may still be suspended if we had a refresh tick and there
+  // were no image containers remaining. If we created a new container, we
+  // should resume invalidations to allow animations to progress.
+  if (*aOutContainer && mRenderingObserver) {
+    mRenderingObserver->ResumeHonoringInvalidations();
   }
 
-  // We're about to rerasterize, which may mean that some of the previous
-  // surfaces we've rasterized aren't useful anymore. We can allow them to
-  // expire from the cache by unlocking them here, and then sending out an
-  // invalidation. If this image is locked, any surfaces that are still useful
-  // will become locked again when Draw touches them, and the remainder will
-  // eventually expire.
-  bool mayCache = SurfaceCache::CanHold(rasterSize);
-  if (mayCache) {
-    SurfaceCache::UnlockEntries(ImageKey(this));
-  }
-
-  // Blob recorded vector images just create a provider responsible for
-  // generating blob keys and recording bindings. The recording won't happen
-  // until the caller requests the key explicitly.
-  RefPtr<ISurfaceProvider> provider;
-  if (blobRecording) {
-    provider = MakeRefPtr<BlobSurfaceProvider>(ImageKey(this), surfaceKey,
-                                               mSVGDocumentWrapper, aFlags);
-  } else {
-    if (mSVGDocumentWrapper->IsDrawing()) {
-      NS_WARNING("Refusing to make re-entrant call to VectorImage::Draw");
-      return ImgDrawResult::TEMPORARY_ERROR;
-    }
-
-    // We aren't using blobs, so we need to rasterize.
-    float animTime =
-        mHaveAnimations ? mSVGDocumentWrapper->GetCurrentTimeAsFloat() : 0.0f;
-
-    // By using a null gfxContext, we ensure that we will always attempt to
-    // create a surface, even if we aren't capable of caching it (e.g. due to
-    // our flags, having an animation, etc). Otherwise CreateSurface will assume
-    // that the caller is capable of drawing directly to its own draw target if
-    // we cannot cache.
-    SVGDrawingParameters params(
-        nullptr, rasterSize, aSize, ImageRegion::Create(rasterSize),
-        SamplingFilter::POINT, aSVGContext, animTime, aFlags, 1.0);
-
-    RefPtr<gfxDrawable> svgDrawable = CreateSVGDrawable(params);
-    bool contextPaint = aSVGContext && aSVGContext->GetContextPaint();
-    AutoRestoreSVGState autoRestore(params, mSVGDocumentWrapper, contextPaint);
-
-    mSVGDocumentWrapper->UpdateViewportBounds(params.viewportSize);
-    mSVGDocumentWrapper->FlushImageTransformInvalidation();
-
-    // Given we have no context, the default backend is fine.
-    BackendType backend =
-        gfxPlatform::GetPlatform()->GetDefaultContentBackend();
-
-    // Try to create an imgFrame, initializing the surface it contains by
-    // drawing our gfxDrawable into it. (We use FILTER_NEAREST since we never
-    // scale here.)
-    auto frame = MakeNotNull<RefPtr<imgFrame>>();
-    nsresult rv = frame->InitWithDrawable(
-        svgDrawable, params.size, SurfaceFormat::OS_RGBA, SamplingFilter::POINT,
-        params.flags, backend);
-
-    // If we couldn't create the frame, it was probably because it would end
-    // up way too big. Generally it also wouldn't fit in the cache, but the
-    // prefs could be set such that the cache isn't the limiting factor.
-    if (NS_FAILED(rv)) {
-      return ImgDrawResult::TEMPORARY_ERROR;
-    }
-
-    provider =
-        MakeRefPtr<SimpleSurfaceProvider>(ImageKey(this), surfaceKey, frame);
-  }
-
-  if (mayCache) {
-    // Attempt to cache the frame.
-    if (SurfaceCache::Insert(WrapNotNull(provider)) == InsertOutcome::SUCCESS) {
-      if (rasterSize != aSize) {
-        // We created a new surface that wasn't the size we requested, which
-        // means we entered factor-of-2 mode. We should purge any surfaces we
-        // no longer need rather than waiting for the cache to expire them.
-        SurfaceCache::PruneImage(ImageKey(this));
-      }
-
-      SendFrameComplete(/* aDidCache */ true, aFlags);
-    }
-  }
-
-  MOZ_ASSERT(provider);
-  provider.forget(aProvider);
-  return ImgDrawResult::SUCCESS;
+  return rv;
 }
 
 bool VectorImage::MaybeRestrictSVGContext(
     Maybe<SVGImageContext>& aNewSVGContext,
     const Maybe<SVGImageContext>& aSVGContext, uint32_t aFlags) {
   bool overridePAR =
       (aFlags & FLAG_FORCE_PRESERVEASPECTRATIO_NONE) && aSVGContext;
 
--- a/image/VectorImage.h
+++ b/image/VectorImage.h
@@ -36,16 +36,18 @@ class VectorImage final : public ImageRe
   // (no public constructor - use ImageFactory)
 
   // Methods inherited from Image
   void MediaFeatureValuesChangedAllDocuments(const MediaFeatureChange&) final;
   nsresult GetNativeSizes(nsTArray<gfx::IntSize>& aNativeSizes) const override;
   size_t GetNativeSizesLength() const override;
   virtual size_t SizeOfSourceWithComputedFallback(
       SizeOfState& aState) const override;
+  virtual void CollectSizeOfSurfaces(nsTArray<SurfaceMemoryCounter>& aCounters,
+                                     MallocSizeOf aMallocSizeOf) const override;
 
   virtual nsresult OnImageDataAvailable(nsIRequest* aRequest,
                                         nsISupports* aContext,
                                         nsIInputStream* aInStr,
                                         uint64_t aSourceOffset,
                                         uint32_t aCount) override;
   virtual nsresult OnImageDataComplete(nsIRequest* aRequest,
                                        nsISupports* aContext, nsresult aResult,
@@ -76,16 +78,26 @@ class VectorImage final : public ImageRe
 
   virtual nsresult StartAnimation() override;
   virtual nsresult StopAnimation() override;
   virtual bool ShouldAnimate() override;
 
  private:
   friend class SourceSurfaceBlobImage;
 
+  Tuple<ImgDrawResult, gfx::IntSize, RefPtr<gfx::SourceSurface>>
+  GetFrameInternal(const gfx::IntSize& aSize,
+                   const Maybe<SVGImageContext>& aSVGContext,
+                   const Maybe<ImageIntRegion>& aRegion, uint32_t aWhichFrame,
+                   uint32_t aFlags) override;
+
+  Tuple<ImgDrawResult, gfx::IntSize> GetImageContainerSize(
+      WindowRenderer* aRenderer, const gfx::IntSize& aSize,
+      uint32_t aFlags) override;
+
   /**
    * Attempt to find a matching cached surface in the SurfaceCache. Returns the
    * cached surface, if found, and the size to rasterize at, if applicable.
    * If we cannot rasterize, it will be the requested size to draw at (aSize).
    */
   Tuple<RefPtr<gfx::SourceSurface>, gfx::IntSize> LookupCachedSurface(
       const gfx::IntSize& aSize, const Maybe<SVGImageContext>& aSVGContext,
       uint32_t aFlags);
deleted file mode 100644
--- a/image/WebRenderImageProvider.h
+++ /dev/null
@@ -1,65 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=8 sts=2 et sw=2 tw=80: */
-/* 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_IMAGE_WEBRENDERIMAGEPROVIDER_H_
-#define MOZILLA_IMAGE_WEBRENDERIMAGEPROVIDER_H_
-
-#include "nsISupportsImpl.h"
-
-namespace mozilla {
-namespace layers {
-class RenderRootStateManager;
-}
-
-namespace wr {
-class IpcResourceUpdateQueue;
-struct ExternalImageId;
-struct ImageKey;
-}  // namespace wr
-
-namespace image {
-
-class ImageResource;
-using ImageProviderId = uint32_t;
-
-class WebRenderImageProvider {
- public:
-  // Subclasses may or may not be XPCOM classes, so we just require that they
-  // implement AddRef and Release.
-  NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING
-
-  ImageProviderId GetProviderId() const { return mProviderId; }
-
-  static ImageProviderId AllocateProviderId();
-
-  /**
-   * Generate an ImageKey for the given frame.
-   * @param aSurface  The current frame. This should match what was cached via
-   *                  SetCurrentFrame, but if it does not, it will need to
-   *                  regenerate the cached ImageKey.
-   */
-  virtual nsresult UpdateKey(layers::RenderRootStateManager* aManager,
-                             wr::IpcResourceUpdateQueue& aResources,
-                             wr::ImageKey& aKey) {
-    return NS_ERROR_NOT_AVAILABLE;
-  }
-
-  /**
-   * Invalidate if a blob recording, requiring it to be regenerated.
-   */
-  virtual void InvalidateRecording() {}
-
- protected:
-  WebRenderImageProvider(const ImageResource* aImage);
-
- private:
-  ImageProviderId mProviderId;
-};
-
-}  // namespace image
-}  // namespace mozilla
-
-#endif /* MOZILLA_IMAGE_WEBRENDERIMAGEPROVIDER_H_ */
--- a/image/imgFrame.cpp
+++ b/image/imgFrame.cpp
@@ -675,16 +675,17 @@ bool imgFrame::AreAllPixelsWritten() con
   return mDecoded.IsEqualInterior(GetRect());
 }
 
 void imgFrame::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
                                       const AddSizeOfCb& aCallback) const {
   MonitorAutoLock lock(mMonitor);
 
   AddSizeOfCbData metadata;
+  metadata.mSurface = mOptSurface ? mOptSurface.get() : mRawSurface.get();
   metadata.mFinished = mFinished;
 
   if (mOptSurface) {
     metadata.mHeapBytes += aMallocSizeOf(mOptSurface);
 
     SourceSurface::SizeOfInfo info;
     mOptSurface->SizeOfExcludingThis(aMallocSizeOf, info);
     metadata.Accumulate(info);
--- a/image/imgFrame.h
+++ b/image/imgFrame.h
@@ -163,16 +163,17 @@ class imgFrame {
 
   void FinalizeSurface();
   already_AddRefed<SourceSurface> GetSourceSurface();
 
   struct AddSizeOfCbData : public SourceSurface::SizeOfInfo {
     AddSizeOfCbData()
         : SourceSurface::SizeOfInfo(), mIndex(0), mFinished(false) {}
 
+    const gfx::SourceSurface* mSurface;
     size_t mIndex;
     bool mFinished;
   };
 
   typedef std::function<void(AddSizeOfCbData& aMetadata)> AddSizeOfCb;
 
   void AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
                               const AddSizeOfCb& aCallback) const;
--- a/image/imgIContainer.idl
+++ b/image/imgIContainer.idl
@@ -43,17 +43,16 @@ class SVGImageContext;
 struct MediaFeatureChange;
 }
 
 namespace mozilla {
 namespace image {
 
 class ImageRegion;
 class ImageIntRegion;
-class WebRenderImageProvider;
 struct Orientation;
 struct Resolution;
 
 }
 }
 
 %}
 
@@ -66,17 +65,16 @@ native ImgDrawResult(mozilla::image::Img
 native SamplingFilter(mozilla::gfx::SamplingFilter);
 [ref] native nsIntRect(nsIntRect);
 native nsIntRectByVal(nsIntRect);
 [ref] native nsIntSize(nsIntSize);
 native nsSize(nsSize);
 [ptr] native nsIFrame(nsIFrame);
 native TempRefImageContainer(already_AddRefed<mozilla::layers::ImageContainer>);
 [ptr] native ImageContainer(mozilla::layers::ImageContainer);
-[ptr] native WebRenderImageProvider(mozilla::image::WebRenderImageProvider);
 [ref] native ImageRegion(mozilla::image::ImageRegion);
 [ptr] native WindowRenderer(mozilla::WindowRenderer);
 native Orientation(mozilla::image::Orientation);
 native ImageResolution(mozilla::image::Resolution);
 [ref] native TimeStamp(mozilla::TimeStamp);
 [ref] native MaybeSVGImageContext(mozilla::Maybe<mozilla::SVGImageContext>);
 [ref] native MaybeImageIntRegion(mozilla::Maybe<mozilla::image::ImageIntRegion>);
 native TempRefSourceSurface(already_AddRefed<mozilla::gfx::SourceSurface>);
@@ -165,19 +163,19 @@ interface imgIContainer : nsISupports
    * Whether this image is animated. You can only be guaranteed that querying
    * this will not throw if STATUS_DECODE_COMPLETE is set on the imgIRequest.
    *
    * @throws NS_ERROR_NOT_AVAILABLE if the animated state cannot be determined.
    */
   readonly attribute boolean animated;
 
   /**
-   * Provider ID for image providers created by this image.
+   * Producer ID for image containers created by this image.
    */
-  [infallible] readonly attribute unsigned long providerId;
+  [infallible] readonly attribute unsigned long producerId;
 
   /**
    * Flags for imgIContainer operations.
    *
    * Meanings:
    *
    * FLAG_NONE: Lack of flags.
    *
@@ -321,41 +319,43 @@ interface imgIContainer : nsISupports
    * @return true if getImageContainer() is expected to return a valid
    *         ImageContainer when passed the given @Renderer and @Flags
    *         parameters.
    */
   [noscript, notxpcom] boolean isImageContainerAvailable(in WindowRenderer aRenderer,
                                                          in uint32_t aFlags);
 
   /**
-   * Attempts to find a WebRenderImageProvider containing the current frame at
-   * the given size. Match the requested size is best effort; it's not
-   * guaranteed that the surface you get will be a perfect match. (Some reasons
-   * you may get a surface of a different size include: if you requested
-   * upscaling, or if downscale-during-decode is disabled.)
+   * Attempts to create an ImageContainer (and Image) containing the current
+   * frame at the given size. Match the requested size is best effort; it's
+   * not guaranteed that the surface you get will be a perfect match. (Some
+   * reasons you may get a surface of a different size include: if you
+   * requested upscaling, or if downscale-during-decode is disabled.)
+   *
+   * Avoid calling this unless you're actually going to layerize this image.
    *
    * @param aRenderer The WindowRenderer which will be used to render the
    *                  ImageContainer.
    * @param aSVGContext If specified, SVG-related rendering context, such as
    *                    overridden attributes on the image document's root <svg>
    *                    node, and the size of the viewport that the full image
    *                    would occupy. Ignored for raster images.
    * @param aFlags Decoding / drawing flags (in other words, FLAG_* flags).
    *               Currently only FLAG_SYNC_DECODE and FLAG_SYNC_DECODE_IF_FAST
    *               are supported.
-   * @param aProvider Return value for WebRenderImageProvider for the current
-   *                  frame. May be null depending on the draw result.
+   * @param aContainer Return value for ImageContainer for the current frame.
+   *                   May be null depending on the draw result.
    * @return The draw result for the current frame.
    */
-  [noscript, notxpcom] ImgDrawResult getImageProvider(in WindowRenderer aRenderer,
-                                                      [const] in nsIntSize aSize,
-                                                      [const] in MaybeSVGImageContext aSVGContext,
-                                                      [const] in MaybeImageIntRegion aRegion,
-                                                      in uint32_t aFlags,
-                                                      out WebRenderImageProvider aProvider);
+  [noscript, notxpcom] ImgDrawResult getImageContainerAtSize(in WindowRenderer aRenderer,
+                                                             [const] in nsIntSize aSize,
+                                                             [const] in MaybeSVGImageContext aSVGContext,
+                                                             [const] in MaybeImageIntRegion aRegion,
+                                                             in uint32_t aFlags,
+                                                             out ImageContainer aOutContainer);
 
   /**
    * Draw the requested frame of this image onto the context specified.
    *
    * Drawing an image involves scaling it to a certain size (which may be
    * implemented as a "smart" scale by substituting an HQ-scaled frame or
    * rendering at a high DPI), and then selecting a region of that image to
    * draw. That region is drawn onto the graphics context and in the process
--- a/image/imgIRequest.idl
+++ b/image/imgIRequest.idl
@@ -26,19 +26,19 @@ interface imgIRequest : nsIRequest
   /**
    * the image container...
    * @return the image object associated with the request.
    * @attention NEED DOCS
    */
   readonly attribute imgIContainer image;
 
   /**
-   * Provider ID for image providers created by this image.
+   * Producer ID for image containers created by this image.
    */
-  [infallible] readonly attribute unsigned long providerId;
+  [infallible] readonly attribute unsigned long producerId;
 
   /**
    * Bits set in the return value from imageStatus
    * @name statusflags
    *
    * Meanings:
    *
    * STATUS_NONE: Nothing to report.
--- a/image/imgLoader.cpp
+++ b/image/imgLoader.cpp
@@ -394,49 +394,16 @@ class imgMemoryReporter final : public n
       }
 
       if (counter.Key().Flags() != DefaultSurfaceFlags()) {
         surfacePathPrefix.AppendLiteral(", flags:");
         surfacePathPrefix.AppendInt(uint32_t(counter.Key().Flags()),
                                     /* aRadix = */ 16);
       }
 
-      if (counter.Key().Region()) {
-        const ImageIntRegion& region = counter.Key().Region().ref();
-        const gfx::IntRect& rect = region.Rect();
-        surfacePathPrefix.AppendLiteral(", region:[ rect=(");
-        surfacePathPrefix.AppendInt(rect.x);
-        surfacePathPrefix.AppendLiteral(",");
-        surfacePathPrefix.AppendInt(rect.y);
-        surfacePathPrefix.AppendLiteral(") ");
-        surfacePathPrefix.AppendInt(rect.width);
-        surfacePathPrefix.AppendLiteral("x");
-        surfacePathPrefix.AppendInt(rect.height);
-        if (region.IsRestricted()) {
-          const gfx::IntRect& restrict = region.Restriction();
-          if (restrict == rect) {
-            surfacePathPrefix.AppendLiteral(", restrict=rect");
-          } else {
-            surfacePathPrefix.AppendLiteral(", restrict=(");
-            surfacePathPrefix.AppendInt(restrict.x);
-            surfacePathPrefix.AppendLiteral(",");
-            surfacePathPrefix.AppendInt(restrict.y);
-            surfacePathPrefix.AppendLiteral(") ");
-            surfacePathPrefix.AppendInt(restrict.width);
-            surfacePathPrefix.AppendLiteral("x");
-            surfacePathPrefix.AppendInt(restrict.height);
-          }
-        }
-        if (region.GetExtendMode() != gfx::ExtendMode::CLAMP) {
-          surfacePathPrefix.AppendLiteral(", extendMode=");
-          surfacePathPrefix.AppendInt(int32_t(region.GetExtendMode()));
-        }
-        surfacePathPrefix.AppendLiteral("]");
-      }
-
       if (counter.Key().SVGContext()) {
         const SVGImageContext& context = counter.Key().SVGContext().ref();
         surfacePathPrefix.AppendLiteral(", svgContext:[ ");
         if (context.GetViewportSize()) {
           const CSSIntSize& size = context.GetViewportSize().ref();
           surfacePathPrefix.AppendLiteral("viewport=(");
           surfacePathPrefix.AppendInt(size.width);
           surfacePathPrefix.AppendLiteral("x");
--- a/image/imgRequestProxy.cpp
+++ b/image/imgRequestProxy.cpp
@@ -662,25 +662,25 @@ imgRequestProxy::GetImage(imgIContainer*
   }
 
   imageToReturn.swap(*aImage);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
-imgRequestProxy::GetProviderId(uint32_t* aId) {
+imgRequestProxy::GetProducerId(uint32_t* aId) {
   NS_ENSURE_TRUE(aId, NS_ERROR_NULL_POINTER);
 
   nsCOMPtr<imgIContainer> image;
   nsresult rv = GetImage(getter_AddRefs(image));
   if (NS_SUCCEEDED(rv)) {
-    *aId = image->GetProviderId();
+    *aId = image->GetProducerId();
   } else {
-    *aId = 0;
+    *aId = layers::kContainerProducerID_Invalid;
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 imgRequestProxy::GetImageStatus(uint32_t* aStatus) {
   if (IsValidating()) {
--- a/image/moz.build
+++ b/image/moz.build
@@ -60,23 +60,22 @@ EXPORTS += [
 EXPORTS.mozilla.image += [
     "encoders/bmp/nsBMPEncoder.h",
     "encoders/ico/nsICOEncoder.h",
     "encoders/jpeg/nsJPEGEncoder.h",
     "encoders/png/nsPNGEncoder.h",
     "ICOFileHeaders.h",
     "ImageMemoryReporter.h",
     "Resolution.h",
-    "WebRenderImageProvider.h",
+    "SourceSurfaceBlobImage.h",
 ]
 
 UNIFIED_SOURCES += [
     "AnimationFrameBuffer.cpp",
     "AnimationSurfaceProvider.cpp",
-    "BlobSurfaceProvider.cpp",
     "ClippedImage.cpp",
     "DecodedSurfaceProvider.cpp",
     "Decoder.cpp",
     "DecoderFactory.cpp",
     "DynamicImage.cpp",
     "FrameAnimator.cpp",
     "FrozenImage.cpp",
     "IDecodingTask.cpp",
@@ -94,16 +93,17 @@ UNIFIED_SOURCES += [
     "imgTools.cpp",
     "MultipartImage.cpp",
     "OrientedImage.cpp",
     "ProgressTracker.cpp",
     "RasterImage.cpp",
     "ScriptedNotificationObserver.cpp",
     "ShutdownTracker.cpp",
     "SourceBuffer.cpp",
+    "SourceSurfaceBlobImage.cpp",
     "SurfaceCache.cpp",
     "SurfaceCacheUtils.cpp",
     "SurfacePipe.cpp",
     "SVGDocumentWrapper.cpp",
     "VectorImage.cpp",
 ]
 
 UNIFIED_SOURCES += ["Downscaler.cpp"]
new file mode 100644
--- /dev/null
+++ b/image/test/gtest/TestContainers.cpp
@@ -0,0 +1,107 @@
+/* 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 "gtest/gtest.h"
+
+#include "WindowRenderer.h"
+#include "Common.h"
+#include "imgIContainer.h"
+#include "ImageFactory.h"
+#include "ImageContainer.h"
+#include "mozilla/gfx/2D.h"
+#include "mozilla/RefPtr.h"
+#include "nsIInputStream.h"
+#include "nsString.h"
+#include "ProgressTracker.h"
+
+using namespace mozilla;
+using namespace mozilla::gfx;
+using namespace mozilla::image;
+
+class ImageContainers : public ::testing::Test {
+ protected:
+  AutoInitializeImageLib mInit;
+};
+
+TEST_F(ImageContainers, RasterImageContainer) {
+  ImageTestCase testCase = GreenPNGTestCase();
+
+  // Create an image.
+  RefPtr<Image> image = ImageFactory::CreateAnonymousImage(
+      nsDependentCString(testCase.mMimeType));
+  ASSERT_TRUE(!image->HasError());
+
+  nsCOMPtr<nsIInputStream> inputStream = LoadFile(testCase.mPath);
+  ASSERT_TRUE(inputStream);
+
+  // Figure out how much data we have.
+  uint64_t length;
+  nsresult rv = inputStream->Available(&length);
+  ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+  // Write the data into the image.
+  rv = image->OnImageDataAvailable(nullptr, nullptr, inputStream, 0,
+                                   static_cast<uint32_t>(length));
+  ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+  // Let the image know we've sent all the data.
+  rv = image->OnImageDataComplete(nullptr, nullptr, NS_OK, true);
+  ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+  RefPtr<ProgressTracker> tracker = image->GetProgressTracker();
+  tracker->SyncNotifyProgress(FLAG_LOAD_COMPLETE);
+
+  RefPtr<WindowRenderer> renderer = new FallbackRenderer;
+
+  // Get at native size.
+  ImgDrawResult drawResult;
+  RefPtr<layers::ImageContainer> nativeContainer;
+  drawResult = image->GetImageContainerAtSize(
+      renderer, testCase.mSize, Nothing(), Nothing(),
+      imgIContainer::FLAG_SYNC_DECODE, getter_AddRefs(nativeContainer));
+  EXPECT_EQ(drawResult, ImgDrawResult::SUCCESS);
+  ASSERT_TRUE(nativeContainer != nullptr);
+  IntSize containerSize = nativeContainer->GetCurrentSize();
+  EXPECT_EQ(testCase.mSize.width, containerSize.width);
+  EXPECT_EQ(testCase.mSize.height, containerSize.height);
+
+  // Upscaling should give the native size.
+  IntSize requestedSize = testCase.mSize;
+  requestedSize.Scale(2, 2);
+  RefPtr<layers::ImageContainer> upscaleContainer;
+  drawResult = image->GetImageContainerAtSize(
+      renderer, requestedSize, Nothing(), Nothing(),
+      imgIContainer::FLAG_SYNC_DECODE |
+          imgIContainer::FLAG_HIGH_QUALITY_SCALING,
+      getter_AddRefs(upscaleContainer));
+  EXPECT_EQ(drawResult, ImgDrawResult::SUCCESS);
+  ASSERT_TRUE(upscaleContainer != nullptr);
+  containerSize = upscaleContainer->GetCurrentSize();
+  EXPECT_EQ(testCase.mSize.width, containerSize.width);
+  EXPECT_EQ(testCase.mSize.height, containerSize.height);
+
+  // Downscaling should give the downscaled size.
+  requestedSize = testCase.mSize;
+  requestedSize.width /= 2;
+  requestedSize.height /= 2;
+  RefPtr<layers::ImageContainer> downscaleContainer;
+  drawResult = image->GetImageContainerAtSize(
+      renderer, requestedSize, Nothing(), Nothing(),
+      imgIContainer::FLAG_SYNC_DECODE |
+          imgIContainer::FLAG_HIGH_QUALITY_SCALING,
+      getter_AddRefs(downscaleContainer));
+  EXPECT_EQ(drawResult, ImgDrawResult::SUCCESS);
+  ASSERT_TRUE(downscaleContainer != nullptr);
+  containerSize = downscaleContainer->GetCurrentSize();
+  EXPECT_EQ(requestedSize.width, containerSize.width);
+  EXPECT_EQ(requestedSize.height, containerSize.height);
+
+  // Get at native size again. Should give same container.
+  RefPtr<layers::ImageContainer> againContainer;
+  drawResult = image->GetImageContainerAtSize(
+      renderer, testCase.mSize, Nothing(), Nothing(),
+      imgIContainer::FLAG_SYNC_DECODE, getter_AddRefs(againContainer));
+  EXPECT_EQ(drawResult, ImgDrawResult::SUCCESS);
+  ASSERT_EQ(nativeContainer.get(), againContainer.get());
+}
--- a/image/test/gtest/moz.build
+++ b/image/test/gtest/moz.build
@@ -6,16 +6,17 @@
 
 Library("imagetest")
 
 UNIFIED_SOURCES = [
     "Common.cpp",
     "TestADAM7InterpolatingFilter.cpp",
     "TestAnimationFrameBuffer.cpp",
     "TestBlendAnimationFilter.cpp",
+    "TestContainers.cpp",
     "TestCopyOnWrite.cpp",
     "TestDeinterlacingFilter.cpp",
     "TestFrameAnimator.cpp",
     "TestLoader.cpp",
     "TestRemoveFrameRectFilter.cpp",
     "TestStreamingLexer.cpp",
     "TestSurfaceSink.cpp",
     "TestSwizzleFilter.cpp",
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -6603,42 +6603,36 @@ IntSize nsLayoutUtils::ComputeImageConta
   // ImageIntRegion for vector images, for which we may only draw part of in
   // some cases.
   if (aImage->GetType() != imgIContainer::TYPE_VECTOR) {
     return aImage->OptimalImageSizeForDest(
         gfxSize(destRect.Width(), destRect.Height()),
         imgIContainer::FRAME_CURRENT, samplingFilter, aFlags);
   }
 
-  // We only use the region rect with blob recordings. This is because when we
-  // rasterize an SVG image in process, we always create a complete
-  // rasterization of the whole image which can be given to any caller, while
-  // we support partial rasterization with the blob recordings.
-  if (aFlags & imgIContainer::FLAG_RECORD_BLOB) {
-    // If the dest rect contains the fill rect, then we are only displaying part
-    // of the vector image. We need to calculate the restriction region to avoid
-    // drawing more than we need, and sampling outside the desired bounds.
-    LayerIntRect clipRect = SnapRectForImage(itm, scaleFactors, aFillRect);
-    if (destRect.Contains(clipRect)) {
-      LayerIntRect restrictRect = destRect.Intersect(clipRect);
-      restrictRect.MoveBy(-destRect.TopLeft());
-
-      if (restrictRect.Width() < 1) {
-        restrictRect.SetWidth(1);
-      }
-      if (restrictRect.Height() < 1) {
-        restrictRect.SetHeight(1);
-      }
-
-      if (restrictRect.X() != 0 || restrictRect.Y() != 0 ||
-          restrictRect.Size() != destRect.Size()) {
-        IntRect sampleRect = restrictRect.ToUnknownRect();
-        aRegion = Some(ImageIntRegion::CreateWithSamplingRestriction(
-            sampleRect, sampleRect, ExtendMode::CLAMP));
-      }
+  // If the dest rect contains the fill rect, then we are only displaying part
+  // of the vector image. We need to calculate the restriction region to avoid
+  // drawing more than we need, and sampling outside the desired bounds.
+  LayerIntRect clipRect = SnapRectForImage(itm, scaleFactors, aFillRect);
+  if (destRect.Contains(clipRect)) {
+    LayerIntRect restrictRect = destRect.Intersect(clipRect);
+    restrictRect.MoveBy(-destRect.TopLeft());
+
+    if (restrictRect.Width() < 1) {
+      restrictRect.SetWidth(1);
+    }
+    if (restrictRect.Height() < 1) {
+      restrictRect.SetHeight(1);
+    }
+
+    if (restrictRect.X() != 0 || restrictRect.Y() != 0 ||
+        restrictRect.Size() != destRect.Size()) {
+      IntRect sampleRect = restrictRect.ToUnknownRect();
+      aRegion = Some(ImageIntRegion::CreateWithSamplingRestriction(
+          sampleRect, sampleRect, ExtendMode::CLAMP));
     }
   }
 
   // VectorImage::OptimalImageSizeForDest will just round up, but we already
   // have an integer size.
   return destRect.Size().ToUnknownSize();
 }
 
--- a/layout/generic/nsImageFrame.cpp
+++ b/layout/generic/nsImageFrame.cpp
@@ -21,17 +21,16 @@
 #include "mozilla/dom/ImageTracker.h"
 #include "mozilla/gfx/2D.h"
 #include "mozilla/gfx/Helpers.h"
 #include "mozilla/gfx/PathHelpers.h"
 #include "mozilla/dom/GeneratedImageContent.h"
 #include "mozilla/dom/HTMLAreaElement.h"
 #include "mozilla/dom/HTMLImageElement.h"
 #include "mozilla/dom/ResponsiveImageSelector.h"
-#include "mozilla/image/WebRenderImageProvider.h"
 #include "mozilla/layers/RenderRootStateManager.h"
 #include "mozilla/layers/WebRenderLayerManager.h"
 #include "mozilla/MouseEvents.h"
 #include "mozilla/PresShell.h"
 #include "mozilla/PresShellInlines.h"
 #include "mozilla/StaticPrefs_image.h"
 #include "mozilla/StaticPrefs_layout.h"
 #include "mozilla/SVGImageContext.h"
@@ -994,18 +993,19 @@ void nsImageFrame::OnFrameUpdate(imgIReq
   InvalidateSelf(&layerInvalidRect, &frameInvalidRect);
 }
 
 void nsImageFrame::InvalidateSelf(const nsIntRect* aLayerInvalidRect,
                                   const nsRect* aFrameInvalidRect) {
   // Check if WebRender has interacted with this frame. If it has
   // we need to let it know that things have changed.
   const auto type = DisplayItemType::TYPE_IMAGE;
-  const auto providerId = mImage ? mImage->GetProviderId() : 0;
-  if (WebRenderUserData::ProcessInvalidateForImage(this, type, providerId)) {
+  const auto producerId =
+      mImage ? mImage->GetProducerId() : kContainerProducerID_Invalid;
+  if (WebRenderUserData::ProcessInvalidateForImage(this, type, producerId)) {
     return;
   }
 
   InvalidateLayer(type, aLayerInvalidRect, aFrameInvalidRect);
 
   if (!mFirstFrameComplete) {
     InvalidateLayer(DisplayItemType::TYPE_ALT_FEEDBACK, aLayerInvalidRect,
                     aFrameInvalidRect);
@@ -1838,23 +1838,23 @@ ImgDrawResult nsImageFrame::DisplayAltFe
       LayoutDeviceRect destRect(LayoutDeviceRect::FromAppUnits(dest, factor));
 
       Maybe<SVGImageContext> svgContext;
       Maybe<ImageIntRegion> region;
       IntSize decodeSize =
           nsLayoutUtils::ComputeImageContainerDrawingParameters(
               imgCon, this, destRect, destRect, aSc, aFlags, svgContext,
               region);
-      RefPtr<image::WebRenderImageProvider> provider;
-      result = imgCon->GetImageProvider(aManager->LayerManager(), decodeSize,
-                                        svgContext, region, aFlags,
-                                        getter_AddRefs(provider));
-      if (provider) {
-        bool wrResult = aManager->CommandBuilder().PushImageProvider(
-            aItem, provider, aBuilder, aResources, destRect, bounds);
+      RefPtr<ImageContainer> container;
+      result = imgCon->GetImageContainerAtSize(
+          aManager->LayerManager(), decodeSize, svgContext, region, aFlags,
+          getter_AddRefs(container));
+      if (container) {
+        bool wrResult = aManager->CommandBuilder().PushImage(
+            aItem, container, aBuilder, aResources, aSc, destRect, bounds);
         result &= wrResult ? ImgDrawResult::SUCCESS : ImgDrawResult::NOT_READY;
       } else {
         // We don't use &= here because we want the result to be NOT_READY so
         // the next block executes.
         result = ImgDrawResult::NOT_READY;
       }
     }
 
@@ -2093,20 +2093,20 @@ bool nsDisplayImage::CreateWebRenderComm
   LayoutDeviceRect destRect(
       LayoutDeviceRect::FromAppUnits(GetDestRect(), factor));
 
   Maybe<SVGImageContext> svgContext;
   Maybe<ImageIntRegion> region;
   IntSize decodeSize = nsLayoutUtils::ComputeImageContainerDrawingParameters(
       mImage, mFrame, destRect, destRect, aSc, flags, svgContext, region);
 
-  RefPtr<image::WebRenderImageProvider> provider;
-  ImgDrawResult drawResult =
-      mImage->GetImageProvider(aManager->LayerManager(), decodeSize, svgContext,
-                               region, flags, getter_AddRefs(provider));
+  RefPtr<layers::ImageContainer> container;
+  ImgDrawResult drawResult = mImage->GetImageContainerAtSize(
+      aManager->LayerManager(), decodeSize, svgContext, region, flags,
+      getter_AddRefs(container));
 
   // While we got a container, it may not contain a fully decoded surface. If
   // that is the case, and we have an image we were previously displaying which
   // has a fully decoded surface, then we should prefer the previous image.
   bool updatePrevImage = false;
   switch (drawResult) {
     case ImgDrawResult::NOT_READY:
     case ImgDrawResult::INCOMPLETE:
@@ -2118,23 +2118,23 @@ bool nsDisplayImage::CreateWebRenderComm
         uint32_t prevFlags = flags;
         if (StaticPrefs::image_svg_blob_image() &&
             mPrevImage->GetType() == imgIContainer::TYPE_VECTOR) {
           prevFlags |= imgIContainer::FLAG_RECORD_BLOB;
         } else {
           prevFlags &= ~imgIContainer::FLAG_RECORD_BLOB;
         }
 
-        RefPtr<image::WebRenderImageProvider> prevProvider;
-        ImgDrawResult newDrawResult = mPrevImage->GetImageProvider(
+        RefPtr<ImageContainer> prevContainer;
+        ImgDrawResult newDrawResult = mPrevImage->GetImageContainerAtSize(
             aManager->LayerManager(), decodeSize, svgContext, region, prevFlags,
-            getter_AddRefs(prevProvider));
-        if (prevProvider && newDrawResult == ImgDrawResult::SUCCESS) {
+            getter_AddRefs(prevContainer));
+        if (prevContainer && newDrawResult == ImgDrawResult::SUCCESS) {
           drawResult = newDrawResult;
-          provider = std::move(prevProvider);
+          container = std::move(prevContainer);
           flags = prevFlags;
           break;
         }
 
         // Previous image was unusable; we can forget about it.
         updatePrevImage = true;
       }
       break;
@@ -2151,19 +2151,24 @@ bool nsDisplayImage::CreateWebRenderComm
   if (updatePrevImage) {
     mPrevImage = mImage;
     frame->mPrevImage = frame->mImage;
   }
 
   // If the image container is empty, we don't want to fallback. Any other
   // failure will be due to resource constraints and fallback is unlikely to
   // help us. Hence we can ignore the return value from PushImage.
-  if (provider) {
-    aManager->CommandBuilder().PushImageProvider(
-        this, provider, aBuilder, aResources, destRect, destRect);
+  if (container) {
+    if (flags & imgIContainer::FLAG_RECORD_BLOB) {
+      aManager->CommandBuilder().PushBlobImage(this, container, aBuilder,
+                                               aResources, destRect, destRect);
+    } else {
+      aManager->CommandBuilder().PushImage(this, container, aBuilder,
+                                           aResources, aSc, destRect, destRect);
+    }
   }
 
   nsDisplayItemGenericImageGeometry::UpdateDrawResult(this, drawResult);
   return true;
 }
 
 ImgDrawResult nsImageFrame::PaintImage(gfxContext& aRenderingContext,
                                        nsPoint aPt, const nsRect& aDirtyRect,
--- a/layout/painting/nsCSSRenderingBorders.cpp
+++ b/layout/painting/nsCSSRenderingBorders.cpp
@@ -24,17 +24,16 @@
 #include "nsDisplayList.h"
 #include "nsExpirationTracker.h"
 #include "nsIScriptError.h"
 #include "nsClassHashtable.h"
 #include "nsPresContext.h"
 #include "nsStyleStruct.h"
 #include "gfx2DGlue.h"
 #include "gfxGradientCache.h"
-#include "mozilla/image/WebRenderImageProvider.h"
 #include "mozilla/layers/StackingContextHelper.h"
 #include "mozilla/layers/RenderRootStateManager.h"
 #include "mozilla/layers/WebRenderLayerManager.h"
 #include "mozilla/ProfilerLabels.h"
 #include "mozilla/Range.h"
 #include <algorithm>
 
 using namespace mozilla;
@@ -3624,43 +3623,44 @@ ImgDrawResult nsCSSBorderImageRenderer::
 
       Maybe<SVGImageContext> svgContext;
       Maybe<ImageIntRegion> region;
       gfx::IntSize decodeSize =
           nsLayoutUtils::ComputeImageContainerDrawingParameters(
               img, aForFrame, imageRect, imageRect, aSc, flags, svgContext,
               region);
 
-      RefPtr<WebRenderImageProvider> provider;
-      drawResult = img->GetImageProvider(aManager->LayerManager(), decodeSize,
-                                         svgContext, region, flags,
-                                         getter_AddRefs(provider));
-      if (!provider) {
+      RefPtr<layers::ImageContainer> container;
+      drawResult = img->GetImageContainerAtSize(
+          aManager->LayerManager(), decodeSize, svgContext, region, flags,
+          getter_AddRefs(container));
+      if (!container) {
         break;
       }
 
-      Maybe<wr::ImageKey> key =
-          aManager->CommandBuilder().CreateImageProviderKey(aItem, provider,
-                                                            aResources);
+      auto rendering =
+          wr::ToImageRendering(aItem->Frame()->UsedImageRendering());
+      gfx::IntSize size;
+      Maybe<wr::ImageKey> key = aManager->CommandBuilder().CreateImageKey(
+          aItem, container, aBuilder, aResources, rendering, aSc, size,
+          Nothing());
       if (key.isNothing()) {
         break;
       }
 
       if (mFill) {
         float epsilon = 0.0001;
         bool noVerticalBorders = widths[0] <= epsilon && widths[2] < epsilon;
         bool noHorizontalBorders = widths[1] <= epsilon && widths[3] < epsilon;
 
         // Border image with no border. It's a little silly but WebRender
         // currently does not handle this. We could fall back to a blob image
         // but there are reftests that are sensible to the test going through a
         // blob while the reference doesn't.
         if (noVerticalBorders && noHorizontalBorders) {
-          auto rendering =
-              wr::ToImageRendering(aItem->Frame()->UsedImageRendering());
           aBuilder.PushImage(dest, clip, !aItem->BackfaceIsHidden(), rendering,
                              key.value());
           break;
         }
 
         // Fall-back if we want to fill the middle area and opposite edges are
         // both empty.
         // TODO(bug 1609893): moving some of the repetition handling code out
--- a/layout/painting/nsImageRenderer.cpp
+++ b/layout/painting/nsImageRenderer.cpp
@@ -9,17 +9,16 @@
 #include "nsImageRenderer.h"
 
 #include "mozilla/webrender/WebRenderAPI.h"
 
 #include "gfxContext.h"
 #include "gfxDrawable.h"
 #include "ImageOps.h"
 #include "ImageRegion.h"
-#include "mozilla/image/WebRenderImageProvider.h"
 #include "mozilla/layers/RenderRootStateManager.h"
 #include "mozilla/layers/StackingContextHelper.h"
 #include "mozilla/layers/WebRenderLayerManager.h"
 #include "nsContentUtils.h"
 #include "nsCSSRendering.h"
 #include "nsCSSRenderingGradients.h"
 #include "nsDeviceContext.h"
 #include "nsIFrame.h"
@@ -617,39 +616,49 @@ ImgDrawResult nsImageRenderer::BuildWebR
           LayoutDeviceRect::FromAppUnits(aFill, appUnitsPerDevPixel);
       auto stretchSize = wr::ToLayoutSize(destRect.Size());
 
       gfx::IntSize decodeSize =
           nsLayoutUtils::ComputeImageContainerDrawingParameters(
               mImageContainer, mForFrame, destRect, clipRect, aSc,
               containerFlags, svgContext, region);
 
-      RefPtr<image::WebRenderImageProvider> provider;
-      drawResult = mImageContainer->GetImageProvider(
+      if (extendMode != ExtendMode::CLAMP) {
+        region = Nothing();
+      }
+
+      RefPtr<layers::ImageContainer> container;
+      drawResult = mImageContainer->GetImageContainerAtSize(
           aManager->LayerManager(), decodeSize, svgContext, region,
-          containerFlags, getter_AddRefs(provider));
-      if (!provider) {
+          containerFlags, getter_AddRefs(container));
+      if (!container) {
         NS_WARNING("Failed to get image container");
         break;
       }
 
-      Maybe<wr::ImageKey> key =
-          aManager->CommandBuilder().CreateImageProviderKey(aItem, provider,
-                                                            aResources);
-      if (key.isNothing()) {
+      if (containerFlags & imgIContainer::FLAG_RECORD_BLOB) {
+        MOZ_ASSERT(extendMode == ExtendMode::CLAMP);
+        aManager->CommandBuilder().PushBlobImage(
+            aItem, container, aBuilder, aResources, clipRect, clipRect);
         break;
       }
 
       auto rendering =
           wr::ToImageRendering(aItem->Frame()->UsedImageRendering());
-      wr::LayoutRect clip = wr::ToLayoutRect(clipRect);
+      gfx::IntSize size;
+      Maybe<wr::ImageKey> key = aManager->CommandBuilder().CreateImageKey(
+          aItem, container, aBuilder, aResources, rendering, aSc, size,
+          Nothing());
 
-      // If we provided a region to the provider, then it already took the
-      // dest rect into account when it did the recording.
-      wr::LayoutRect dest = region ? clip : wr::ToLayoutRect(destRect);
+      if (key.isNothing()) {
+        break;
+      }
+
+      wr::LayoutRect dest = wr::ToLayoutRect(destRect);
+      wr::LayoutRect clip = wr::ToLayoutRect(clipRect);
 
       if (extendMode == ExtendMode::CLAMP) {
         // The image is not repeating. Just push as a regular image.
         aBuilder.PushImage(dest, clip, !aItem->BackfaceIsHidden(), rendering,
                            key.value(), true,
                            wr::ColorF{1.0f, 1.0f, 1.0f, aOpacity});
       } else {
         nsPoint firstTilePos = nsLayoutUtils::GetBackgroundFirstTilePos(
--- a/layout/style/ImageLoader.cpp
+++ b/layout/style/ImageLoader.cpp
@@ -535,19 +535,19 @@ static void InvalidateImages(nsIFrame* a
           }
           // XXX: handle Blob data
           invalidateFrame = true;
           break;
         case layers::WebRenderUserData::UserDataType::eMask:
           static_cast<layers::WebRenderMaskData*>(data.get())->Invalidate();
           invalidateFrame = true;
           break;
-        case layers::WebRenderUserData::UserDataType::eImageProvider:
-          if (static_cast<layers::WebRenderImageProviderData*>(data.get())
-                  ->Invalidate(aRequest->GetProviderId())) {
+        case layers::WebRenderUserData::UserDataType::eImage:
+          if (static_cast<layers::WebRenderImageData*>(data.get())
+                  ->UsingSharedSurface(aRequest->GetProducerId())) {
             break;
           }
           [[fallthrough]];
         default:
           invalidateFrame = true;
           break;
       }
     }
--- a/layout/svg/SVGImageFrame.cpp
+++ b/layout/svg/SVGImageFrame.cpp
@@ -5,17 +5,16 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "SVGImageFrame.h"
 
 // Keep in (case-insensitive) order:
 #include "gfxContext.h"
 #include "gfxPlatform.h"
 #include "mozilla/gfx/2D.h"
-#include "mozilla/image/WebRenderImageProvider.h"
 #include "mozilla/layers/RenderRootStateManager.h"
 #include "mozilla/layers/WebRenderLayerManager.h"
 #include "imgIContainer.h"
 #include "ImageRegion.h"
 #include "nsContainerFrame.h"
 #include "nsIImageLoadingContent.h"
 #include "nsLayoutUtils.h"
 #include "imgINotificationObserver.h"
@@ -616,20 +615,20 @@ bool SVGImageFrame::CreateWebRenderComma
                        Some(imgElem->mPreserveAspectRatio.GetAnimValue()));
   }
 
   Maybe<ImageIntRegion> region;
   IntSize decodeSize = nsLayoutUtils::ComputeImageContainerDrawingParameters(
       mImageContainer, this, destRect, clipRect, aSc, flags, svgContext,
       region);
 
-  RefPtr<image::WebRenderImageProvider> provider;
-  ImgDrawResult drawResult = mImageContainer->GetImageProvider(
+  RefPtr<layers::ImageContainer> container;
+  ImgDrawResult drawResult = mImageContainer->GetImageContainerAtSize(
       aManager->LayerManager(), decodeSize, svgContext, region, flags,
-      getter_AddRefs(provider));
+      getter_AddRefs(container));
 
   // While we got a container, it may not contain a fully decoded surface. If
   // that is the case, and we have an image we were previously displaying which
   // has a fully decoded surface, then we should prefer the previous image.
   switch (drawResult) {
     case ImgDrawResult::NOT_READY:
     case ImgDrawResult::TEMPORARY_ERROR:
       // nothing to draw (yet)
@@ -643,19 +642,24 @@ bool SVGImageFrame::CreateWebRenderComma
   }
 
   // Don't do any actual mutations to state if we're doing a dry run
   // (used to decide if we're making this into an active layer)
   if (!aDryRun) {
     // If the image container is empty, we don't want to fallback. Any other
     // failure will be due to resource constraints and fallback is unlikely to
     // help us. Hence we can ignore the return value from PushImage.
-    if (provider) {
-      aManager->CommandBuilder().PushImageProvider(
-          aItem, provider, aBuilder, aResources, destRect, clipRect);
+    if (container) {
+      if (flags & imgIContainer::FLAG_RECORD_BLOB) {
+        aManager->CommandBuilder().PushBlobImage(
+            aItem, container, aBuilder, aResources, destRect, clipRect);
+      } else {
+        aManager->CommandBuilder().PushImage(
+            aItem, container, aBuilder, aResources, aSc, destRect, clipRect);
+      }
     }
 
     nsDisplayItemGenericImageGeometry::UpdateDrawResult(aItem, drawResult);
   }
 
   return true;
 }
 
--- a/layout/xul/nsImageBoxFrame.cpp
+++ b/layout/xul/nsImageBoxFrame.cpp
@@ -46,17 +46,16 @@
 
 #include "mozilla/BasicEvents.h"
 #include "mozilla/EventDispatcher.h"
 #include "mozilla/Maybe.h"
 #include "mozilla/PresShell.h"
 #include "mozilla/StaticPrefs_image.h"
 #include "mozilla/SVGImageContext.h"
 #include "Units.h"
-#include "mozilla/image/WebRenderImageProvider.h"
 #include "mozilla/layers/RenderRootStateManager.h"
 #include "mozilla/layers/WebRenderLayerManager.h"
 #include "mozilla/dom/ImageTracker.h"
 
 #if defined(XP_WIN)
 // Undefine LoadImage to prevent naming conflict with Windows.
 #  undef LoadImage
 #endif
@@ -424,35 +423,49 @@ ImgDrawResult nsImageBoxFrame::CreateWeb
 
   Maybe<SVGImageContext> svgContext;
   Maybe<ImageIntRegion> region;
   gfx::IntSize decodeSize =
       nsLayoutUtils::ComputeImageContainerDrawingParameters(
           imgCon, aItem->Frame(), fillRect, fillRect, aSc, aFlags, svgContext,
           region);
 
-  RefPtr<image::WebRenderImageProvider> provider;
-  result =
-      imgCon->GetImageProvider(aManager->LayerManager(), decodeSize, svgContext,
-                               region, aFlags, getter_AddRefs(provider));
-  if (!provider) {
-    NS_WARNING("Failed to get image provider");
+  RefPtr<layers::ImageContainer> container;
+  result = imgCon->GetImageContainerAtSize(aManager->LayerManager(), decodeSize,
+                                           svgContext, region, aFlags,
+                                           getter_AddRefs(container));
+  if (!container) {
+    NS_WARNING("Failed to get image container");
     return result;
   }
 
   auto rendering = wr::ToImageRendering(aItem->Frame()->UsedImageRendering());
   wr::LayoutRect fill = wr::ToLayoutRect(fillRect);
 
-  Maybe<wr::ImageKey> key = aManager->CommandBuilder().CreateImageProviderKey(
-      aItem, provider, aResources);
+  if (aFlags & imgIContainer::FLAG_RECORD_BLOB) {
+    Maybe<wr::BlobImageKey> key = aManager->CommandBuilder().CreateBlobImageKey(
+        aItem, container, aResources);
+    if (key.isNothing()) {
+      return result;
+    }
+
+    aBuilder.PushImage(fill, fill, !BackfaceIsHidden(), rendering,
+                       wr::AsImageKey(key.value()));
+    return result;
+  }
+
+  gfx::IntSize size;
+  Maybe<wr::ImageKey> key = aManager->CommandBuilder().CreateImageKey(
+      aItem, container, aBuilder, aResources, rendering, aSc, size, Nothing());
   if (key.isNothing()) {
     return result;
   }
 
   aBuilder.PushImage(fill, fill, !BackfaceIsHidden(), rendering, key.value());
+
   return result;
 }
 
 nsRect nsImageBoxFrame::GetDestRect(const nsPoint& aOffset,
                                     Maybe<nsPoint>& aAnchorPoint) {
   nsCOMPtr<imgIContainer> imgCon;
   mImageRequest->GetImage(getter_AddRefs(imgCon));
   MOZ_ASSERT(imgCon);
@@ -823,18 +836,18 @@ void nsImageBoxFrame::OnImageIsAnimated(
 void nsImageBoxFrame::OnFrameUpdate(imgIRequest* aRequest) {
   if ((0 == mRect.width) || (0 == mRect.height)) {
     return;
   }
 
   // Check if WebRender has interacted with this frame. If it has
   // we need to let it know that things have changed.
   const auto type = DisplayItemType::TYPE_XUL_IMAGE;
-  const auto providerId = aRequest->GetProviderId();
-  if (WebRenderUserData::ProcessInvalidateForImage(this, type, providerId)) {
+  const auto producerId = aRequest->GetProducerId();
+  if (WebRenderUserData::ProcessInvalidateForImage(this, type, producerId)) {
     return;
   }
 
   InvalidateLayer(type);
 }
 
 NS_IMPL_ISUPPORTS(nsImageBoxListener, imgINotificationObserver)