Bug 1139641 - Return more information from SurfaceCache::Lookup and SurfaceCache::LookupBestMatch. r=dholbert
☠☠ backed out by 9ad61b614c2d ☠ ☠
authorSeth Fowler <mark.seth.fowler@gmail.com>
Tue, 02 Jun 2015 23:30:14 -0700
changeset 246894 dd25b4d148af466d9f26930b6e30989fafaa9273
parent 246893 908408ee27c7b384df4c98b5981f97bcbf501574
child 246895 9ad61b614c2df283ccc9aff28207199bf515c212
push id13322
push usercbook@mozilla.com
push dateWed, 03 Jun 2015 12:01:38 +0000
treeherderfx-team@3f10ceab96e7 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdholbert
bugs1139641
milestone41.0a1
Bug 1139641 - Return more information from SurfaceCache::Lookup and SurfaceCache::LookupBestMatch. r=dholbert
image/FrameAnimator.cpp
image/FrameAnimator.h
image/LookupResult.h
image/RasterImage.cpp
image/RasterImage.h
image/SurfaceCache.cpp
image/SurfaceCache.h
image/VectorImage.cpp
--- a/image/FrameAnimator.cpp
+++ b/image/FrameAnimator.cpp
@@ -3,16 +3,17 @@
  * 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 "FrameAnimator.h"
 
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/Move.h"
 #include "imgIContainer.h"
+#include "LookupResult.h"
 #include "MainThreadUtils.h"
 #include "RasterImage.h"
 
 #include "pixman.h"
 
 namespace mozilla {
 
 using namespace gfx;
@@ -260,35 +261,37 @@ FrameAnimator::GetCurrentAnimationFrameI
 }
 
 nsIntRect
 FrameAnimator::GetFirstFrameRefreshArea() const
 {
   return mFirstFrameRefreshArea;
 }
 
-DrawableFrameRef
+LookupResult
 FrameAnimator::GetCompositedFrame(uint32_t aFrameNum)
 {
   MOZ_ASSERT(aFrameNum != 0, "First frame is never composited");
 
   // If we have a composited version of this frame, return that.
   if (mLastCompositedFrameIndex == int32_t(aFrameNum)) {
-    return mCompositingFrame->DrawableRef();
+    return LookupResult(mCompositingFrame->DrawableRef(),
+                        /* aIsExactMatch = */ true);
   }
 
   // Otherwise return the raw frame. DoBlend is required to ensure that we only
   // hit this case if the frame is not paletted and doesn't require compositing.
-  DrawableFrameRef ref =
+  LookupResult result =
     SurfaceCache::Lookup(ImageKey(mImage),
                          RasterSurfaceKey(mSize,
                                           0,  // Default decode flags.
                                           aFrameNum));
-  MOZ_ASSERT(!ref || !ref->GetIsPaletted(), "About to return a paletted frame");
-  return ref;
+  MOZ_ASSERT(!result || !result.DrawableRef()->GetIsPaletted(),
+             "About to return a paletted frame");
+  return result;
 }
 
 int32_t
 FrameAnimator::GetTimeoutForFrame(uint32_t aFrameNum) const
 {
   RawAccessFrameRef frame = GetRawFrame(aFrameNum);
   if (!frame) {
     NS_WARNING("No frame; called GetTimeoutForFrame too early?");
@@ -362,23 +365,23 @@ FrameAnimator::CollectSizeOfCompositingS
                                        aCounters,
                                        aMallocSizeOf);
   }
 }
 
 RawAccessFrameRef
 FrameAnimator::GetRawFrame(uint32_t aFrameNum) const
 {
-  DrawableFrameRef ref =
+  LookupResult result =
     SurfaceCache::Lookup(ImageKey(mImage),
                          RasterSurfaceKey(mSize,
                                           0,  // Default decode flags.
                                           aFrameNum));
-  return ref ? ref->RawAccessRef()
-             : RawAccessFrameRef();
+  return result ? result.DrawableRef()->RawAccessRef()
+                : RawAccessFrameRef();
 }
 
 //******************************************************************************
 // DoBlend gets called when the timer for animation get fired and we have to
 // update the composited frame of the animation.
 bool
 FrameAnimator::DoBlend(nsIntRect* aDirtyRect,
                        uint32_t aPrevFrameIndex,
--- a/image/FrameAnimator.h
+++ b/image/FrameAnimator.h
@@ -123,20 +123,20 @@ public:
 
   /**
    * Get the area we refresh when we loop around to the first frame.
    */
   nsIntRect GetFirstFrameRefreshArea() const;
 
   /**
    * If we have a composited frame for @aFrameNum, returns it. Otherwise,
-   * returns an empty DrawableFrameRef. It is an error to call this method with
+   * returns an empty LookupResult. It is an error to call this method with
    * aFrameNum == 0, because the first frame is never composited.
    */
-  DrawableFrameRef GetCompositedFrame(uint32_t aFrameNum);
+  LookupResult GetCompositedFrame(uint32_t aFrameNum);
 
   /*
    * Returns the frame's adjusted timeout. If the animation loops and the
    * timeout falls in between a certain range then the timeout is adjusted so
    * that it's never 0. If the animation does not loop then no adjustments are
    * made.
    */
   int32_t GetTimeoutForFrame(uint32_t aFrameNum) const;
new file mode 100644
--- /dev/null
+++ b/image/LookupResult.h
@@ -0,0 +1,54 @@
+/* -*- 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/. */
+
+/**
+ * LookupResult is the return type of SurfaceCache's Lookup*() functions. It
+ * combines a surface with relevant metadata tracked by SurfaceCache.
+ */
+
+#ifndef mozilla_image_LookupResult_h
+#define mozilla_image_LookupResult_h
+
+#include "mozilla/Attributes.h"
+#include "mozilla/Move.h"
+#include "imgFrame.h"
+
+namespace mozilla {
+namespace image {
+
+/**
+ * LookupResult is the return type of SurfaceCache's Lookup*() functions. It
+ * combines a surface with relevant metadata tracked by SurfaceCache.
+ */
+class MOZ_STACK_CLASS LookupResult
+{
+public:
+  LookupResult()
+    : mIsExactMatch(false)
+  { }
+
+  LookupResult(DrawableFrameRef&& aDrawableRef, bool aIsExactMatch)
+    : mDrawableRef(Move(aDrawableRef))
+    , mIsExactMatch(aIsExactMatch)
+  { }
+
+  DrawableFrameRef& DrawableRef() { return mDrawableRef; }
+  const DrawableFrameRef& DrawableRef() const { return mDrawableRef; }
+
+  /// @return true if this LookupResult contains a surface.
+  explicit operator bool() const { return bool(mDrawableRef); }
+
+  /// @return true if the surface is an exact match for the Lookup*() arguments.
+  bool IsExactMatch() const { return mIsExactMatch; }
+
+private:
+  DrawableFrameRef mDrawableRef;
+  bool mIsExactMatch;
+};
+
+} // namespace image
+} // namespace mozilla
+
+#endif // mozilla_image_LookupResult_h
--- a/image/RasterImage.cpp
+++ b/image/RasterImage.cpp
@@ -15,16 +15,17 @@
 #include "nsError.h"
 #include "Decoder.h"
 #include "nsAutoPtr.h"
 #include "prenv.h"
 #include "prsystem.h"
 #include "ImageContainer.h"
 #include "ImageRegion.h"
 #include "Layers.h"
+#include "LookupResult.h"
 #include "nsPresContext.h"
 #include "SourceBuffer.h"
 #include "SurfaceCache.h"
 #include "FrameAnimator.h"
 
 #include "nsPNGDecoder.h"
 #include "nsGIFDecoder2.h"
 #include "nsJPEGDecoder.h"
@@ -451,17 +452,17 @@ NS_IMETHODIMP
 RasterImage::GetType(uint16_t* aType)
 {
   NS_ENSURE_ARG_POINTER(aType);
 
   *aType = imgIContainer::TYPE_RASTER;
   return NS_OK;
 }
 
-DrawableFrameRef
+LookupResult
 RasterImage::LookupFrameInternal(uint32_t aFrameNum,
                                  const IntSize& aSize,
                                  uint32_t aFlags)
 {
   if (!mAnim) {
     NS_ASSERTION(aFrameNum == 0,
                  "Don't ask for a frame > 0 if we're not animated!");
     aFrameNum = 0;
@@ -504,54 +505,55 @@ RasterImage::LookupFrame(uint32_t aFrame
                          const IntSize& aSize,
                          uint32_t aFlags)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   IntSize requestedSize = CanDownscaleDuringDecode(aSize, aFlags)
                         ? aSize : mSize;
 
-  DrawableFrameRef ref = LookupFrameInternal(aFrameNum, requestedSize, aFlags);
+  LookupResult result = LookupFrameInternal(aFrameNum, requestedSize, aFlags);
 
-  if (!ref && !mHasSize) {
+  if (!result && !mHasSize) {
     // We can't request a decode without knowing our intrinsic size. Give up.
     return DrawableFrameRef();
   }
 
-  if (!ref || ref->GetImageSize() != requestedSize) {
+  if (!result || !result.IsExactMatch()) {
     // The OS threw this frame away. We need to redecode if we can.
     MOZ_ASSERT(!mAnim, "Animated frames should be locked");
 
     Decode(Some(requestedSize), aFlags);
 
     // If we can sync decode, we should already have the frame.
     if (aFlags & FLAG_SYNC_DECODE) {
-      ref = LookupFrameInternal(aFrameNum, requestedSize, aFlags);
+      result = LookupFrameInternal(aFrameNum, requestedSize, aFlags);
     }
   }
 
-  if (!ref) {
+  if (!result) {
     // We still weren't able to get a frame. Give up.
     return DrawableFrameRef();
   }
 
-  if (ref->GetCompositingFailed()) {
+  if (result.DrawableRef()->GetCompositingFailed()) {
     return DrawableFrameRef();
   }
 
-  MOZ_ASSERT(!ref || !ref->GetIsPaletted(), "Should not have paletted frame");
+  MOZ_ASSERT(!result.DrawableRef()->GetIsPaletted(),
+             "Should not have a paletted frame");
 
   // Sync decoding guarantees that we got the frame, but if it's owned by an
   // async decoder that's currently running, the contents of the frame may not
   // be available yet. Make sure we get everything.
-  if (ref && mHasSourceData && (aFlags & FLAG_SYNC_DECODE)) {
-    ref->WaitUntilComplete();
+  if (mHasSourceData && (aFlags & FLAG_SYNC_DECODE)) {
+    result.DrawableRef()->WaitUntilComplete();
   }
 
-  return ref;
+  return Move(result.DrawableRef());
 }
 
 uint32_t
 RasterImage::GetCurrentFrameIndex() const
 {
   if (mAnim) {
     return mAnim->GetCurrentAnimationFrameIndex();
   }
@@ -1746,29 +1748,29 @@ RasterImage::DrawWithPreDownscaleIfNeede
                                           const IntSize& aSize,
                                           const ImageRegion& aRegion,
                                           GraphicsFilter aFilter,
                                           uint32_t aFlags)
 {
   DrawableFrameRef frameRef;
 
   if (CanScale(aFilter, aSize, aFlags)) {
-    frameRef =
+    LookupResult result =
       SurfaceCache::Lookup(ImageKey(this),
                            RasterSurfaceKey(aSize,
                                             DecodeFlags(aFlags),
                                             0));
-    if (!frameRef) {
+    if (!result) {
       // We either didn't have a matching scaled frame or the OS threw it away.
       // Request a new one so we'll be ready next time. For now, we'll fall back
       // to aFrameRef below.
       RequestScale(aFrameRef.get(), aFlags, aSize);
     }
-    if (frameRef && !frameRef->IsImageComplete()) {
-      frameRef.reset();  // We're still scaling, so we can't use this yet.
+    if (result && result.DrawableRef()->IsImageComplete()) {
+      frameRef = Move(result.DrawableRef());  // The scaled version is ready.
     }
   }
 
   gfxContextMatrixAutoSaveRestore saveMatrix(aContext);
   ImageRegion region(aRegion);
   bool frameIsComplete = true;  // We already checked HQ scaled frames.
   if (!frameRef) {
     // There's no HQ scaled frame available, so we'll have to use the frame
@@ -2151,31 +2153,31 @@ RasterImage::OptimalImageSizeForDest(con
   }
 
   IntSize destSize(ceil(aDest.width), ceil(aDest.height));
 
   if (aFilter == GraphicsFilter::FILTER_GOOD &&
       CanDownscaleDuringDecode(destSize, aFlags)) {
     return destSize;
   } else if (CanScale(aFilter, destSize, aFlags)) {
-    DrawableFrameRef frameRef =
+    LookupResult result =
       SurfaceCache::Lookup(ImageKey(this),
                            RasterSurfaceKey(destSize,
                                             DecodeFlags(aFlags),
                                             0));
 
-    if (frameRef && frameRef->IsImageComplete()) {
-        return destSize;  // We have an existing HQ scale for this size.
+    if (result && result.DrawableRef()->IsImageComplete()) {
+      return destSize;  // We have an existing HQ scale for this size.
     }
-    if (!frameRef) {
+    if (!result) {
       // We could HQ scale to this size, but we haven't. Request a scale now.
-      frameRef = LookupFrame(GetRequestedFrameIndex(aWhichFrame),
-                             mSize, aFlags);
-      if (frameRef) {
-        RequestScale(frameRef.get(), aFlags, destSize);
+      DrawableFrameRef ref = LookupFrame(GetRequestedFrameIndex(aWhichFrame),
+                                         mSize, aFlags);
+      if (ref) {
+        RequestScale(ref.get(), aFlags, destSize);
       }
     }
   }
 
   // We either can't HQ scale to this size or the scaled version isn't ready
   // yet. Use our intrinsic size for now.
   return mSize;
 }
--- a/image/RasterImage.h
+++ b/image/RasterImage.h
@@ -18,16 +18,17 @@
 #define mozilla_image_RasterImage_h
 
 #include "Image.h"
 #include "nsCOMPtr.h"
 #include "imgIContainer.h"
 #include "nsIProperties.h"
 #include "nsTArray.h"
 #include "imgFrame.h"
+#include "LookupResult.h"
 #include "nsThreadUtils.h"
 #include "DecodePool.h"
 #include "Orientation.h"
 #include "nsIObserver.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/Maybe.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/Pair.h"
@@ -295,19 +296,19 @@ private:
                                           uint32_t aFlags);
 
   TemporaryRef<gfx::SourceSurface> CopyFrame(uint32_t aWhichFrame,
                                              uint32_t aFlags);
 
   Pair<DrawResult, RefPtr<gfx::SourceSurface>>
     GetFrameInternal(uint32_t aWhichFrame, uint32_t aFlags);
 
-  DrawableFrameRef LookupFrameInternal(uint32_t aFrameNum,
-                                       const gfx::IntSize& aSize,
-                                       uint32_t aFlags);
+  LookupResult LookupFrameInternal(uint32_t aFrameNum,
+                                   const gfx::IntSize& aSize,
+                                   uint32_t aFlags);
   DrawableFrameRef LookupFrame(uint32_t aFrameNum,
                                const nsIntSize& aSize,
                                uint32_t aFlags);
   uint32_t GetCurrentFrameIndex() const;
   uint32_t GetRequestedFrameIndex(uint32_t aWhichFrame) const;
 
   nsIntRect GetFirstFrameRect();
 
--- a/image/SurfaceCache.cpp
+++ b/image/SurfaceCache.cpp
@@ -20,16 +20,17 @@
 #include "mozilla/StaticPtr.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 "LookupResult.h"
 #include "nsAutoPtr.h"
 #include "nsExpirationTracker.h"
 #include "nsHashKeys.h"
 #include "nsRefPtrHashtable.h"
 #include "nsSize.h"
 #include "nsTArray.h"
 #include "prsystem.h"
 #include "ShutdownTracker.h"
@@ -538,67 +539,67 @@ public:
       MOZ_ASSERT(foundInCosts, "Lost track of costs for this surface");
     }
 
     mAvailableCost += costEntry.GetCost();
     MOZ_ASSERT(mAvailableCost <= mMaxCost,
                "More available cost than we started with");
   }
 
-  DrawableFrameRef Lookup(const ImageKey    aImageKey,
-                          const SurfaceKey& aSurfaceKey)
+  LookupResult Lookup(const ImageKey    aImageKey,
+                      const SurfaceKey& aSurfaceKey)
   {
     nsRefPtr<ImageSurfaceCache> cache = GetImageCache(aImageKey);
     if (!cache) {
-      return DrawableFrameRef();  // No cached surfaces for this image.
+      return LookupResult();  // No cached surfaces for this image.
     }
 
     nsRefPtr<CachedSurface> surface = cache->Lookup(aSurfaceKey);
     if (!surface) {
-      return DrawableFrameRef();  // Lookup in the per-image cache missed.
+      return LookupResult();  // Lookup in the per-image cache missed.
     }
 
     DrawableFrameRef ref = surface->DrawableRef();
     if (!ref) {
       // The surface was released by the operating system. Remove the cache
       // entry as well.
       Remove(surface);
-      return DrawableFrameRef();
+      return LookupResult();
     }
 
     if (cache->IsLocked()) {
       LockSurface(surface);
     } else {
       mExpirationTracker.MarkUsed(surface);
     }
 
-    return ref;
+    return LookupResult(Move(ref), /* aIsExactMatch = */ true);
   }
 
-  DrawableFrameRef LookupBestMatch(const ImageKey         aImageKey,
-                                   const SurfaceKey&      aSurfaceKey,
-                                   const Maybe<uint32_t>& aAlternateFlags)
+  LookupResult LookupBestMatch(const ImageKey         aImageKey,
+                               const SurfaceKey&      aSurfaceKey,
+                               const Maybe<uint32_t>& aAlternateFlags)
   {
     nsRefPtr<ImageSurfaceCache> cache = GetImageCache(aImageKey);
     if (!cache) {
-      return DrawableFrameRef();  // No cached surfaces for this image.
+      return LookupResult();  // No cached surfaces for this image.
     }
 
     // Repeatedly look up the best match, trying again if the resulting surface
     // has been freed by the operating system, until we can either lock a
     // surface for drawing or there are no matching surfaces left.
     // XXX(seth): This is O(N^2), but N is expected to be very small. If we
     // encounter a performance problem here we can revisit this.
 
     nsRefPtr<CachedSurface> surface;
     DrawableFrameRef ref;
     while (true) {
       surface = cache->LookupBestMatch(aSurfaceKey, aAlternateFlags);
       if (!surface) {
-        return DrawableFrameRef();  // Lookup in the per-image cache missed.
+        return LookupResult();  // Lookup in the per-image cache missed.
       }
 
       ref = surface->DrawableRef();
       if (ref) {
         break;
       }
 
       // The surface was released by the operating system. Remove the cache
@@ -607,17 +608,25 @@ public:
     }
 
     if (cache->IsLocked()) {
       LockSurface(surface);
     } else {
       mExpirationTracker.MarkUsed(surface);
     }
 
-    return ref;
+    SurfaceKey key = surface->GetSurfaceKey();
+    const bool isExactMatch = key.Size() == aSurfaceKey.Size();
+
+    MOZ_ASSERT(isExactMatch ==
+      (key == aSurfaceKey ||
+         (aAlternateFlags && key == aSurfaceKey.WithNewFlags(*aAlternateFlags))),
+      "Result differs in a way other than size or alternate flags");
+
+    return LookupResult(Move(ref), isExactMatch);
   }
 
   void RemoveSurface(const ImageKey    aImageKey,
                      const SurfaceKey& aSurfaceKey)
   {
     nsRefPtr<ImageSurfaceCache> cache = GetImageCache(aImageKey);
     if (!cache) {
       return;  // No cached surfaces for this image.
@@ -965,44 +974,44 @@ SurfaceCache::Initialize()
 /* static */ void
 SurfaceCache::Shutdown()
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(sInstance, "No singleton - was Shutdown() called twice?");
   sInstance = nullptr;
 }
 
-/* static */ DrawableFrameRef
+/* static */ LookupResult
 SurfaceCache::Lookup(const ImageKey         aImageKey,
                      const SurfaceKey&      aSurfaceKey,
                      const Maybe<uint32_t>& aAlternateFlags /* = Nothing() */)
 {
   if (!sInstance) {
-    return DrawableFrameRef();
+    return LookupResult();
   }
 
   MutexAutoLock lock(sInstance->GetMutex());
 
-  DrawableFrameRef ref = sInstance->Lookup(aImageKey, aSurfaceKey);
-  if (!ref && aAlternateFlags) {
-    ref = sInstance->Lookup(aImageKey,
-                            aSurfaceKey.WithNewFlags(*aAlternateFlags));
+  LookupResult result = sInstance->Lookup(aImageKey, aSurfaceKey);
+  if (!result && aAlternateFlags) {
+    result = sInstance->Lookup(aImageKey,
+                               aSurfaceKey.WithNewFlags(*aAlternateFlags));
   }
 
-  return ref;
+  return result;
 }
 
-/* static */ DrawableFrameRef
+/* static */ LookupResult
 SurfaceCache::LookupBestMatch(const ImageKey         aImageKey,
                               const SurfaceKey&      aSurfaceKey,
                               const Maybe<uint32_t>& aAlternateFlags
                                 /* = Nothing() */)
 {
   if (!sInstance) {
-    return DrawableFrameRef();
+    return LookupResult();
   }
 
   MutexAutoLock lock(sInstance->GetMutex());
   return sInstance->LookupBestMatch(aImageKey, aSurfaceKey, aAlternateFlags);
 }
 
 /* static */ InsertOutcome
 SurfaceCache::Insert(imgFrame*         aSurface,
--- a/image/SurfaceCache.h
+++ b/image/SurfaceCache.h
@@ -19,19 +19,19 @@
 #include "nsCOMPtr.h"                // for already_AddRefed
 #include "mozilla/gfx/Point.h"       // for mozilla::gfx::IntSize
 #include "mozilla/gfx/2D.h"          // for SourceSurface
 #include "SVGImageContext.h"         // for SVGImageContext
 
 namespace mozilla {
 namespace image {
 
-class DrawableFrameRef;
 class Image;
 class imgFrame;
+class LookupResult;
 struct SurfaceMemoryCounter;
 
 /*
  * ImageKey contains the information we need to look up all cached surfaces for
  * a particular image.
  */
 typedef Image* ImageKey;
 
@@ -169,40 +169,39 @@ struct SurfaceCache
    * Look up the imgFrame containing a surface in the cache and returns a
    * drawable reference to that imgFrame.
    *
    * 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
    * volatile buffer which was discarded by the OS, then it is automatically
-   * removed from the cache and an empty DrawableFrameRef is returned. Note that
+   * removed from the cache and an empty LookupResult is returned. Note that
    * this will never happen to persistent 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.
    *
    * @param aSurfaceKey     Key data which uniquely identifies the requested
    *                        surface.
    *
    * @param aAlternateFlags If not Nothing(), a different set of flags than the
    *                        ones specified in @aSurfaceKey which are also
    *                        acceptable to the caller. This is more efficient
    *                        than calling Lookup() twice, which requires taking a
    *                        lock each time.
    *
-   * @return                a DrawableFrameRef to the imgFrame wrapping the
-   *                        requested surface, or an empty DrawableFrameRef if
-   *                        not found.
+   * @return                a LookupResult, which will either contain a
+   *                        DrawableFrameRef to the requested surface, or an
+   *                        empty DrawableFrameRef if the surface was not found.
    */
-  static DrawableFrameRef Lookup(const ImageKey    aImageKey,
-                                 const SurfaceKey& aSurfaceKey,
-                                 const Maybe<uint32_t>& aAlternateFlags
-                                   = Nothing());
+  static LookupResult Lookup(const ImageKey    aImageKey,
+                             const SurfaceKey& aSurfaceKey,
+                             const Maybe<uint32_t>& aAlternateFlags = Nothing());
 
   /**
    * Looks up the best matching surface in the cache and returns a drawable
    * reference to the imgFrame containing it.
    *
    * Returned surfaces may vary from the requested surface only in terms of
    * size, unless @aAlternateFlags is specified.
    *
@@ -211,23 +210,27 @@ struct SurfaceCache
    *
    * @param aSurfaceKey  Key data which identifies the ideal surface to return.
    *
    * @param aAlternateFlags If not Nothing(), a different set of flags than the
    *                        ones specified in @aSurfaceKey which are also
    *                        acceptable to the caller. This is much more
    *                        efficient than calling LookupBestMatch() twice.
    *
-   * @return a DrawableFrameRef to the imgFrame wrapping a surface similar to
-   *         the requested surface, or an empty DrawableFrameRef if not found.
+   * @return                a LookupResult, which will either contain a
+   *                        DrawableFrameRef to a surface similar to the
+   *                        requested surface, or an empty DrawableFrameRef if
+   *                        the surface was not found. Callers can use
+   *                        LookupResult::IsExactMatch() to check whether the
+   *                        returned surface exactly matches @aSurfaceKey.
    */
-  static DrawableFrameRef LookupBestMatch(const ImageKey    aImageKey,
-                                          const SurfaceKey& aSurfaceKey,
-                                          const Maybe<uint32_t>& aAlternateFlags
-                                            = Nothing());
+  static LookupResult LookupBestMatch(const ImageKey    aImageKey,
+                                      const SurfaceKey& aSurfaceKey,
+                                      const Maybe<uint32_t>& aAlternateFlags
+                                        = Nothing());
 
   /**
    * 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.
    *
    * Each surface in the cache has a lifetime, either Transient or Persistent.
    * Transient surfaces can expire from the cache at any time. Persistent
    * surfaces, on the other hand, will never expire as long as they remain
--- a/image/VectorImage.cpp
+++ b/image/VectorImage.cpp
@@ -22,16 +22,17 @@
 #include "nsMimeTypes.h"
 #include "nsPresContext.h"
 #include "nsRect.h"
 #include "nsString.h"
 #include "nsStubDocumentObserver.h"
 #include "nsSVGEffects.h" // for nsSVGRenderingObserver
 #include "nsWindowMemoryReporter.h"
 #include "ImageRegion.h"
+#include "LookupResult.h"
 #include "Orientation.h"
 #include "SVGDocumentWrapper.h"
 #include "nsIDOMEventListener.h"
 #include "SurfaceCache.h"
 
 // undef the GetCurrentTime macro defined in WinBase.h from the MS Platform SDK
 #undef GetCurrentTime
 
@@ -819,28 +820,28 @@ VectorImage::Draw(gfxContext* aContext,
   SVGDrawingParameters params(aContext, aSize, aRegion, aFilter,
                               aSVGContext, animTime, aFlags);
 
   if (aFlags & FLAG_BYPASS_SURFACE_CACHE) {
     CreateSurfaceAndShow(params);
     return DrawResult::SUCCESS;
   }
 
-  DrawableFrameRef frameRef =
+  LookupResult result =
     SurfaceCache::Lookup(ImageKey(this),
                          VectorSurfaceKey(params.size,
                                           params.svgContext,
                                           params.animationTime));
 
   // Draw.
-  if (frameRef) {
-    RefPtr<SourceSurface> surface = frameRef->GetSurface();
+  if (result) {
+    RefPtr<SourceSurface> surface = result.DrawableRef()->GetSurface();
     if (surface) {
       nsRefPtr<gfxDrawable> svgDrawable =
-        new gfxSurfaceDrawable(surface, frameRef->GetSize());
+        new gfxSurfaceDrawable(surface, result.DrawableRef()->GetSize());
       Show(svgDrawable, params);
       return DrawResult::SUCCESS;
     }
 
     // We lost our surface due to some catastrophic event.
     RecoverFromLossOfSurfaces();
   }