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 382687 6ba925dbd2334e4071d374a93e4aa8ca532d404f
parent 382686 626bc70dd8f972f8498d732f0f4c8c4343dc41d3
child 382688 c607a8652548294fab5cffa926a81e0e7952744f
push id21799
push userbmo:joliu@mozilla.com
push dateThu, 30 Jun 2016 06:04:37 +0000
reviewersdholbert
bugs1282327
milestone50.0a1
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);