Bug 1282327 - Store ISurfaceProviders in the ImageLib SurfaceCache. r=dholbert
authorSeth Fowler <mark.seth.fowler@gmail.com>
Sun, 26 Jun 2016 15:38:41 -0700
changeset 303215 6ba925dbd2334e4071d374a93e4aa8ca532d404f
parent 303214 626bc70dd8f972f8498d732f0f4c8c4343dc41d3
child 303216 c607a8652548294fab5cffa926a81e0e7952744f
push id30382
push usercbook@mozilla.com
push dateThu, 30 Jun 2016 10:34:10 +0000
treeherdermozilla-central@bcf4ff0c3eef [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdholbert
bugs1282327
milestone50.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1282327 - Store ISurfaceProviders in the ImageLib SurfaceCache. r=dholbert
image/Decoder.cpp
image/ISurfaceProvider.h
image/SurfaceCache.cpp
image/SurfaceCache.h
image/VectorImage.cpp
--- a/image/Decoder.cpp
+++ b/image/Decoder.cpp
@@ -5,17 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "Decoder.h"
 
 #include "mozilla/gfx/2D.h"
 #include "DecodePool.h"
 #include "GeckoProfiler.h"
 #include "IDecodingTask.h"
-#include "imgIContainer.h"
+#include "ISurfaceProvider.h"
 #include "nsProxyRelease.h"
 #include "nsServiceManagerUtils.h"
 #include "nsComponentManagerUtils.h"
 #include "mozilla/Telemetry.h"
 
 using mozilla::gfx::IntSize;
 using mozilla::gfx::SurfaceFormat;
 
@@ -296,33 +296,35 @@ Decoder::AllocateFrameInternal(uint32_t 
 
   const uint32_t bytesPerPixel = aPaletteDepth == 0 ? 4 : 1;
   if (ShouldUseSurfaceCache() &&
       !SurfaceCache::CanHold(aFrameRect.Size(), bytesPerPixel)) {
     NS_WARNING("Trying to add frame that's too large for the SurfaceCache");
     return RawAccessFrameRef();
   }
 
-  RefPtr<imgFrame> frame = new imgFrame();
+  NotNull<RefPtr<imgFrame>> frame = WrapNotNull(new imgFrame());
   bool nonPremult = bool(mSurfaceFlags & SurfaceFlags::NO_PREMULTIPLY_ALPHA);
   if (NS_FAILED(frame->InitForDecoder(aTargetSize, aFrameRect, aFormat,
                                       aPaletteDepth, nonPremult))) {
     NS_WARNING("imgFrame::Init should succeed");
     return RawAccessFrameRef();
   }
 
   RawAccessFrameRef ref = frame->RawAccessRef();
   if (!ref) {
     frame->Abort();
     return RawAccessFrameRef();
   }
 
   if (ShouldUseSurfaceCache()) {
+    NotNull<RefPtr<ISurfaceProvider>> provider =
+      WrapNotNull(new SimpleSurfaceProvider(frame));
     InsertOutcome outcome =
-      SurfaceCache::Insert(frame, ImageKey(mImage.get()),
+      SurfaceCache::Insert(provider, ImageKey(mImage.get()),
                            RasterSurfaceKey(aTargetSize,
                                             mSurfaceFlags,
                                             aFrameNum));
     if (outcome == InsertOutcome::FAILURE) {
       // We couldn't insert the surface, almost certainly due to low memory. We
       // treat this as a permanent error to help the system recover; otherwise,
       // we might just end up attempting to decode this image again immediately.
       ref->Abort();
new file mode 100644
--- /dev/null
+++ b/image/ISurfaceProvider.h
@@ -0,0 +1,109 @@
+/* -*- 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/. */
+
+/**
+ * An interface for objects which can either store a surface or dynamically
+ * generate one, and various implementations.
+ */
+
+#ifndef mozilla_image_ISurfaceProvider_h
+#define mozilla_image_ISurfaceProvider_h
+
+#include "mozilla/Maybe.h"
+#include "mozilla/NotNull.h"
+#include "mozilla/TimeStamp.h"
+#include "mozilla/Variant.h"
+#include "mozilla/gfx/2D.h"
+
+#include "imgFrame.h"
+
+namespace mozilla {
+namespace image {
+
+/**
+ * An interface for objects which can either store a surface or dynamically
+ * generate one.
+ */
+class ISurfaceProvider
+{
+public:
+  // Subclasses may or may not be XPCOM classes, so we just require that they
+  // implement AddRef and Release.
+  NS_IMETHOD_(MozExternalRefCountType) AddRef(void) = 0;
+  NS_IMETHOD_(MozExternalRefCountType) Release(void) = 0;
+
+  /// @return a drawable reference to a surface.
+  virtual DrawableFrameRef DrawableRef() = 0;
+
+  /// @return true if this ISurfaceProvider is acting as a placeholder, which is
+  /// to say that it doesn't have a surface and hence can't return a
+  /// DrawableFrameRef yet, but it will be able to in the future.
+  virtual bool IsPlaceholder() const = 0;
+
+  /// @return if DrawableRef() will return a completely decoded surface.
+  virtual bool IsFinished() const = 0;
+
+  /// @return true if this ISurfaceProvider is locked. (@see SetLocked())
+  virtual bool IsLocked() const = 0;
+
+  /// If @aLocked is true, hint that this ISurfaceProvider is in use and it
+  /// should avoid releasing its resources.
+  virtual void SetLocked(bool aLocked) = 0;
+
+  /// @return the number of bytes of memory this ISurfaceProvider is expected to
+  /// require. Optimizations may result in lower real memory usage. Trivial
+  /// overhead is ignored.
+  virtual size_t LogicalSizeInBytes() const = 0;
+
+protected:
+  virtual ~ISurfaceProvider() { }
+};
+
+/**
+ * An ISurfaceProvider that stores a single surface.
+ */
+class SimpleSurfaceProvider final : public ISurfaceProvider
+{
+public:
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SimpleSurfaceProvider, override)
+
+  explicit SimpleSurfaceProvider(NotNull<imgFrame*> aSurface)
+    : mSurface(aSurface)
+  { }
+
+  DrawableFrameRef DrawableRef() override { return mSurface->DrawableRef(); }
+  bool IsPlaceholder() const override { return false; }
+  bool IsFinished() const override { return mSurface->IsFinished(); }
+  bool IsLocked() const override { return bool(mLockRef); }
+
+  void SetLocked(bool aLocked) override
+  {
+    if (aLocked == IsLocked()) {
+      return;  // Nothing changed.
+    }
+
+    // If we're locked, hold a DrawableFrameRef to |mSurface|, which will keep
+    // any volatile buffer it owns in memory.
+    mLockRef = aLocked ? mSurface->DrawableRef()
+                       : DrawableFrameRef();
+  }
+
+  size_t LogicalSizeInBytes() const override
+  {
+    gfx::IntSize size = mSurface->GetSize();
+    return size.width * size.height * mSurface->GetBytesPerPixel();
+  }
+
+private:
+  virtual ~SimpleSurfaceProvider() { }
+
+  NotNull<RefPtr<imgFrame>> mSurface;
+  DrawableFrameRef mLockRef;
+};
+
+} // namespace image
+} // namespace mozilla
+
+#endif // mozilla_image_ISurfaceProvider_h
--- a/image/SurfaceCache.cpp
+++ b/image/SurfaceCache.cpp
@@ -22,16 +22,17 @@
 #include "mozilla/Tuple.h"
 #include "nsIMemoryReporter.h"
 #include "gfx2DGlue.h"
 #include "gfxPattern.h"  // Workaround for flaw in bug 921753 part 2.
 #include "gfxPlatform.h"
 #include "gfxPrefs.h"
 #include "imgFrame.h"
 #include "Image.h"
+#include "ISurfaceProvider.h"
 #include "LookupResult.h"
 #include "nsExpirationTracker.h"
 #include "nsHashKeys.h"
 #include "nsRefPtrHashtable.h"
 #include "nsSize.h"
 #include "nsTArray.h"
 #include "prsystem.h"
 #include "ShutdownTracker.h"
@@ -125,67 +126,60 @@ private:
  */
 class CachedSurface
 {
   ~CachedSurface() { }
 public:
   MOZ_DECLARE_REFCOUNTED_TYPENAME(CachedSurface)
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CachedSurface)
 
-  CachedSurface(imgFrame*          aSurface,
+  CachedSurface(ISurfaceProvider*  aProvider,
                 const Cost         aCost,
                 const ImageKey     aImageKey,
                 const SurfaceKey&  aSurfaceKey)
-    : mSurface(aSurface)
+    : mProvider(aProvider)
     , mCost(aCost)
     , mImageKey(aImageKey)
     , mSurfaceKey(aSurfaceKey)
   {
     MOZ_ASSERT(!IsPlaceholder() || mCost == sPlaceholderCost,
                "Placeholder should have trivial cost");
     MOZ_ASSERT(mImageKey, "Must have a valid image key");
   }
 
   DrawableFrameRef DrawableRef() const
   {
     if (MOZ_UNLIKELY(IsPlaceholder())) {
       MOZ_ASSERT_UNREACHABLE("Shouldn't call DrawableRef() on a placeholder");
       return DrawableFrameRef();
     }
 
-    return mSurface->DrawableRef();
+    return mProvider->DrawableRef();
   }
 
   void SetLocked(bool aLocked)
   {
     if (IsPlaceholder()) {
       return;  // Can't lock a placeholder.
     }
 
-    if (aLocked) {
-      // This may fail, and that's OK. We make no guarantees about whether
-      // locking is successful if you call SurfaceCache::LockImage() after
-      // SurfaceCache::Insert().
-      mDrawableRef = mSurface->DrawableRef();
-    } else {
-      mDrawableRef.reset();
-    }
+    mProvider->SetLocked(aLocked);
   }
 
-  bool IsPlaceholder() const { return !bool(mSurface); }
-  bool IsLocked() const { return bool(mDrawableRef); }
+  bool IsPlaceholder() const { return !mProvider || mProvider->IsPlaceholder(); }
+  bool IsLocked() const { return !IsPlaceholder() && mProvider->IsLocked(); }
 
   ImageKey GetImageKey() const { return mImageKey; }
   SurfaceKey GetSurfaceKey() const { return mSurfaceKey; }
   CostEntry GetCostEntry() { return image::CostEntry(this, mCost); }
   nsExpirationState* GetExpirationState() { return &mExpirationState; }
 
   bool IsDecoded() const
   {
-    return !IsPlaceholder() && mSurface->IsFinished();
+    return !IsPlaceholder() && mProvider->IsFinished();
   }
 
   // A helper type used by SurfaceCacheImpl::CollectSizeOfSurfaces.
   struct MOZ_STACK_CLASS SurfaceMemoryReport
   {
     SurfaceMemoryReport(nsTArray<SurfaceMemoryCounter>& aCounters,
                         MallocSizeOf                    aMallocSizeOf)
       : mCounters(aCounters)
@@ -194,37 +188,37 @@ public:
 
     void Add(CachedSurface* aCachedSurface)
     {
       MOZ_ASSERT(aCachedSurface, "Should have a CachedSurface");
 
       SurfaceMemoryCounter counter(aCachedSurface->GetSurfaceKey(),
                                    aCachedSurface->IsLocked());
 
-      if (aCachedSurface->mSurface) {
-        counter.SubframeSize() = Some(aCachedSurface->mSurface->GetSize());
+      DrawableFrameRef surfaceRef = aCachedSurface->DrawableRef();
+      if (surfaceRef) {
+        counter.SubframeSize() = Some(surfaceRef->GetSize());
 
         size_t heap = 0, nonHeap = 0;
-        aCachedSurface->mSurface->AddSizeOfExcludingThis(mMallocSizeOf,
-                                                         heap, nonHeap);
+        surfaceRef->AddSizeOfExcludingThis(mMallocSizeOf, heap, nonHeap);
         counter.Values().SetDecodedHeap(heap);
         counter.Values().SetDecodedNonHeap(nonHeap);
       }
 
       mCounters.AppendElement(counter);
     }
 
   private:
     nsTArray<SurfaceMemoryCounter>& mCounters;
     MallocSizeOf                    mMallocSizeOf;
   };
 
 private:
   nsExpirationState  mExpirationState;
-  RefPtr<imgFrame> mSurface;
+  RefPtr<ISurfaceProvider> mProvider;
   DrawableFrameRef   mDrawableRef;
   const Cost         mCost;
   const ImageKey     mImageKey;
   const SurfaceKey   mSurfaceKey;
 };
 
 static int64_t
 AreaOfIntSize(const IntSize& aSize) {
@@ -430,17 +424,17 @@ private:
     UnregisterWeakMemoryReporter(this);
   }
 
 public:
   void InitMemoryReporter() { RegisterWeakMemoryReporter(this); }
 
   Mutex& GetMutex() { return mMutex; }
 
-  InsertOutcome Insert(imgFrame*         aSurface,
+  InsertOutcome Insert(ISurfaceProvider* aProvider,
                        const Cost        aCost,
                        const ImageKey    aImageKey,
                        const SurfaceKey& aSurfaceKey)
   {
     // If this is a duplicate surface, refuse to replace the original.
     // XXX(seth): Calling Lookup() and then RemoveSurface() does the lookup
     // twice. We'll make this more efficient in bug 1185137.
     LookupResult result = Lookup(aImageKey, aSurfaceKey, /* aMarkUsed = */ false);
@@ -475,17 +469,17 @@ public:
     // for this image, create it.
     RefPtr<ImageSurfaceCache> cache = GetImageCache(aImageKey);
     if (!cache) {
       cache = new ImageSurfaceCache;
       mImageCaches.Put(aImageKey, cache);
     }
 
     RefPtr<CachedSurface> surface =
-      new CachedSurface(aSurface, aCost, aImageKey, aSurfaceKey);
+      new CachedSurface(aProvider, aCost, aImageKey, aSurfaceKey);
 
     // We require that locking succeed if the image is locked and we're not
     // inserting a placeholder; the caller may need to know this to handle
     // errors correctly.
     if (cache->IsLocked() && !surface->IsPlaceholder()) {
       surface->SetLocked(true);
       if (!surface->IsLocked()) {
         return InsertOutcome::FAILURE;
@@ -1021,33 +1015,27 @@ SurfaceCache::LookupBestMatch(const Imag
     return LookupResult(MatchType::NOT_FOUND);
   }
 
   MutexAutoLock lock(sInstance->GetMutex());
   return sInstance->LookupBestMatch(aImageKey, aSurfaceKey);
 }
 
 /* static */ InsertOutcome
-SurfaceCache::Insert(imgFrame*         aSurface,
-                     const ImageKey    aImageKey,
-                     const SurfaceKey& aSurfaceKey)
+SurfaceCache::Insert(NotNull<ISurfaceProvider*> aProvider,
+                     const ImageKey             aImageKey,
+                     const SurfaceKey&          aSurfaceKey)
 {
   if (!sInstance) {
     return InsertOutcome::FAILURE;
   }
 
-  // Refuse null surfaces.
-  if (!aSurface) {
-    gfxDevCrash(LogReason::InvalidCacheSurface) << "Null surface in SurfaceCache::Insert";
-    return InsertOutcome::FAILURE;
-  }
-
   MutexAutoLock lock(sInstance->GetMutex());
-  Cost cost = ComputeCost(aSurface->GetSize(), aSurface->GetBytesPerPixel());
-  return sInstance->Insert(aSurface, cost, aImageKey, aSurfaceKey);
+  Cost cost = aProvider->LogicalSizeInBytes();
+  return sInstance->Insert(aProvider.get(), cost, aImageKey, aSurfaceKey);
 }
 
 /* static */ InsertOutcome
 SurfaceCache::InsertPlaceholder(const ImageKey    aImageKey,
                                 const SurfaceKey& aSurfaceKey)
 {
   if (!sInstance) {
     return InsertOutcome::FAILURE;
--- a/image/SurfaceCache.h
+++ b/image/SurfaceCache.h
@@ -7,31 +7,32 @@
  * SurfaceCache is a service for caching temporary surfaces and decoded image
  * data in imagelib.
  */
 
 #ifndef mozilla_image_SurfaceCache_h
 #define mozilla_image_SurfaceCache_h
 
 #include "mozilla/Maybe.h"           // for Maybe
+#include "mozilla/NotNull.h"
 #include "mozilla/MemoryReporting.h" // for MallocSizeOf
 #include "mozilla/HashFunctions.h"   // for HashGeneric and AddToHash
 #include "gfx2DGlue.h"
 #include "gfxPoint.h"                // for gfxSize
 #include "nsCOMPtr.h"                // for already_AddRefed
 #include "mozilla/gfx/Point.h"       // for mozilla::gfx::IntSize
 #include "mozilla/gfx/2D.h"          // for SourceSurface
 #include "SurfaceFlags.h"
 #include "SVGImageContext.h"         // for SVGImageContext
 
 namespace mozilla {
 namespace image {
 
 class Image;
-class imgFrame;
+class ISurfaceProvider;
 class LookupResult;
 struct SurfaceMemoryCounter;
 
 /*
  * ImageKey contains the information we need to look up all cached surfaces for
  * a particular image.
  */
 typedef Image* ImageKey;
@@ -122,20 +123,19 @@ enum class InsertOutcome : uint8_t {
   FAILURE_ALREADY_PRESENT  // A surface with the same key is already present.
 };
 
 /**
  * SurfaceCache is an imagelib-global service that allows caching of temporary
  * surfaces. Surfaces normally expire from the cache automatically if they go
  * too long without being accessed.
  *
- * SurfaceCache does not hold surfaces directly; instead, it holds imgFrame
- * objects, which hold surfaces but also layer on additional features specific
- * to imagelib's needs like animation, padding support, and transparent support
- * for volatile buffers.
+ * SurfaceCache does not hold surfaces directly; instead, it holds
+ * ISurfaceProvider objects, which usually hold surfaces but may sometimes
+ * generate them on the fly when requested.
  *
  * Sometime it's useful to temporarily prevent surfaces from expiring from the
  * cache. This is most often because losing the data could harm the user
  * experience (for example, we often don't want to allow surfaces that are
  * currently visible to expire) or because it's not possible to rematerialize
  * the surface. SurfaceCache supports this through the use of image locking; see
  * the comments for Insert() and LockImage() for more details.
  *
@@ -153,23 +153,23 @@ struct SurfaceCache
   static void Initialize();
 
   /**
    * Release static data. Called during imagelib module shutdown.
    */
   static void Shutdown();
 
   /**
-   * Look up the imgFrame containing a surface in the cache and returns a
-   * drawable reference to that imgFrame.
+   * Looks up the requested surface in the cache and returns a drawable
+   * reference to it.
    *
    * If the image associated with the surface is locked, then the surface will
    * be locked before it is returned.
    *
-   * If the imgFrame was found in the cache, but had stored its surface in a
+   * If the surface was found in the cache, but had stored its surface in a
    * volatile buffer which was discarded by the OS, then it is automatically
    * removed from the cache and an empty LookupResult is returned. Note that
    * this will never happen to surfaces associated with a locked image; the
    * cache keeps a strong reference to such surfaces internally.
    *
    * @param aImageKey       Key data identifying which image the surface belongs
    *                        to.
    *
@@ -180,17 +180,17 @@ struct SurfaceCache
    *                        DrawableFrameRef to the requested surface, or an
    *                        empty DrawableFrameRef if the surface was not found.
    */
   static LookupResult Lookup(const ImageKey    aImageKey,
                              const SurfaceKey& aSurfaceKey);
 
   /**
    * Looks up the best matching surface in the cache and returns a drawable
-   * reference to the imgFrame containing it.
+   * reference to it.
    *
    * Returned surfaces may vary from the requested surface only in terms of
    * size.
    *
    * @param aImageKey    Key data identifying which image the surface belongs
    *                     to.
    *
    * @param aSurfaceKey  Key data which identifies the ideal surface to return.
@@ -201,19 +201,20 @@ struct SurfaceCache
    *                        the surface was not found. Callers can use
    *                        LookupResult::IsExactMatch() to check whether the
    *                        returned surface exactly matches @aSurfaceKey.
    */
   static LookupResult LookupBestMatch(const ImageKey    aImageKey,
                                       const SurfaceKey& aSurfaceKey);
 
   /**
-   * Insert a surface into the cache. If a surface with the same ImageKey and
-   * SurfaceKey is already in the cache, Insert returns FAILURE_ALREADY_PRESENT.
-   * If a matching placeholder is already present, the placeholder is removed.
+   * Insert a SurfaceProvider into the cache. If a surface with the same
+   * ImageKey and SurfaceKey is already in the cache, Insert returns
+   * FAILURE_ALREADY_PRESENT. If a matching placeholder is already present, the
+   * placeholder is removed.
    *
    * Surfaces will never expire as long as they remain locked, but if they
    * become unlocked, they can expire either because the SurfaceCache runs out
    * of capacity or because they've gone too long without being used.  When it
    * is first inserted, a surface is locked if its associated image is locked.
    * When that image is later unlocked, the surface becomes unlocked too. To
    * become locked again at that point, two things must happen: the image must
    * become locked again (via LockImage()), and the surface must be touched
@@ -230,31 +231,31 @@ struct SurfaceCache
    * it was inserted into the cache successfully. Insert() returns FAILURE if it
    * failed to insert the surface, which could happen because of capacity
    * reasons, or because it was already freed by the OS. If the surface isn't
    * associated with a locked image, checking for SUCCESS or FAILURE is useless:
    * the surface might expire immediately after being inserted, even though
    * Insert() returned SUCCESS. Thus, many callers do not need to check the
    * result of Insert() at all.
    *
-   * @param aTarget      The new surface (wrapped in an imgFrame) to insert into
-   *                     the cache.
+   * @param aProvider    The new surface (wrapped in an ISurfaceProvider) to
+   *                     insert into the cache.
    * @param aImageKey    Key data identifying which image the surface belongs
    *                     to.
    * @param aSurfaceKey  Key data which uniquely identifies the requested
    *                     surface.
    * @return SUCCESS if the surface was inserted successfully. (But see above
    *           for more information about when you should check this.)
    *         FAILURE if the surface could not be inserted, e.g. for capacity
    *           reasons. (But see above for more information about when you
    *           should check this.)
    *         FAILURE_ALREADY_PRESENT if a surface with the same ImageKey and
    *           SurfaceKey already exists in the cache.
    */
-  static InsertOutcome Insert(imgFrame*         aSurface,
+  static InsertOutcome Insert(NotNull<ISurfaceProvider*> aProvider,
                               const ImageKey    aImageKey,
                               const SurfaceKey& aSurfaceKey);
 
   /**
    * Insert a placeholder for a surface into the cache. If a surface with the
    * same ImageKey and SurfaceKey is already in the cache, InsertPlaceholder()
    * returns FAILURE_ALREADY_PRESENT.
    *
--- a/image/VectorImage.cpp
+++ b/image/VectorImage.cpp
@@ -918,17 +918,17 @@ VectorImage::CreateSurfaceAndShow(const 
   // 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.
   SurfaceCache::UnlockSurfaces(ImageKey(this));
 
   // 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.)
-  RefPtr<imgFrame> frame = new imgFrame;
+  NotNull<RefPtr<imgFrame>> frame = WrapNotNull(new imgFrame);
   nsresult rv =
     frame->InitWithDrawable(svgDrawable, aParams.size,
                             SurfaceFormat::B8G8R8A8,
                             SamplingFilter::POINT, aParams.flags);
 
   // 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.
@@ -939,17 +939,19 @@ VectorImage::CreateSurfaceAndShow(const 
   // Take a strong reference to the frame's surface and make sure it hasn't
   // already been purged by the operating system.
   RefPtr<SourceSurface> surface = frame->GetSurface();
   if (!surface) {
     return Show(svgDrawable, aParams);
   }
 
   // Attempt to cache the frame.
-  SurfaceCache::Insert(frame, ImageKey(this),
+  NotNull<RefPtr<ISurfaceProvider>> provider =
+    WrapNotNull(new SimpleSurfaceProvider(frame));
+  SurfaceCache::Insert(provider, ImageKey(this),
                        VectorSurfaceKey(aParams.size,
                                         aParams.svgContext,
                                         aParams.animationTime));
 
   // Draw.
   RefPtr<gfxDrawable> drawable =
     new gfxSurfaceDrawable(surface, aParams.size);
   Show(drawable, aParams);