Bug 1446309 - Part 1. Return draw result from imgIContainer::GetImageContainerAtSize. r=tnikkel
☠☠ backed out by 5589d9ec31aa ☠ ☠
authorAndrew Osmond <aosmond@mozilla.com>
Tue, 14 Aug 2018 11:35:40 -0400
changeset 431442 69a4c2a0aac6e8bfc041a82b634611e1432af36b
parent 431441 45dd8818d922150a499cf1477455679b9819e475
child 431443 45e3f5d8e29402e8fda2fea772dc5d08d8866fd7
push id34443
push usercsabou@mozilla.com
push dateWed, 15 Aug 2018 00:53:32 +0000
treeherdermozilla-central@b80906e2fbc9 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstnikkel
bugs1446309
milestone63.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 1446309 - Part 1. Return draw result from imgIContainer::GetImageContainerAtSize. r=tnikkel In addition to the image container, the draw result can also be useful for callers to know whether or not the surface(s) in the container are fully decoded or not. This is used in subsequent parts to avoid flickering in some cases.
image/ClippedImage.cpp
image/ClippedImage.h
image/DynamicImage.cpp
image/FrozenImage.cpp
image/FrozenImage.h
image/Image.cpp
image/Image.h
image/ImageWrapper.cpp
image/ImgDrawResult.h
image/OrientedImage.cpp
image/OrientedImage.h
image/RasterImage.cpp
image/RasterImage.h
image/VectorImage.cpp
image/VectorImage.h
image/imgIContainer.idl
image/test/gtest/TestContainers.cpp
layout/generic/nsBulletFrame.cpp
layout/generic/nsImageFrame.cpp
layout/painting/nsCSSRenderingBorders.cpp
layout/painting/nsImageRenderer.cpp
layout/xul/nsImageBoxFrame.cpp
widget/nsBaseDragService.cpp
--- a/image/ClippedImage.cpp
+++ b/image/ClippedImage.cpp
@@ -360,34 +360,35 @@ ClippedImage::IsImageContainerAvailableA
                                               uint32_t aFlags)
 {
   if (!ShouldClip()) {
     return InnerImage()->IsImageContainerAvailableAtSize(aManager, aSize, aFlags);
   }
   return false;
 }
 
-NS_IMETHODIMP_(already_AddRefed<ImageContainer>)
-ClippedImage::GetImageContainerAtSize(LayerManager* aManager,
-                                      const IntSize& aSize,
+NS_IMETHODIMP_(ImgDrawResult)
+ClippedImage::GetImageContainerAtSize(layers::LayerManager* aManager,
+                                      const gfx::IntSize& aSize,
                                       const Maybe<SVGImageContext>& aSVGContext,
-                                      uint32_t aFlags)
+                                      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()->GetImageContainerAtSize(aManager, aSize,
-                                                 aSVGContext, aFlags);
+    return InnerImage()->GetImageContainerAtSize(aManager, aSize, aSVGContext,
+                                                 aFlags, aOutContainer);
   }
 
-  return nullptr;
+  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
@@ -46,21 +46,22 @@ public:
                                               uint32_t aFlags) override;
   NS_IMETHOD_(already_AddRefed<layers::ImageContainer>)
     GetImageContainer(layers::LayerManager* aManager,
                       uint32_t aFlags) override;
   NS_IMETHOD_(bool)
     IsImageContainerAvailableAtSize(layers::LayerManager* aManager,
                                     const gfx::IntSize& aSize,
                                     uint32_t aFlags) override;
-  NS_IMETHOD_(already_AddRefed<layers::ImageContainer>)
+  NS_IMETHOD_(ImgDrawResult)
     GetImageContainerAtSize(layers::LayerManager* aManager,
                             const gfx::IntSize& aSize,
                             const Maybe<SVGImageContext>& aSVGContext,
-                            uint32_t aFlags) override;
+                            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;
--- a/image/DynamicImage.cpp
+++ b/image/DynamicImage.cpp
@@ -229,23 +229,24 @@ DynamicImage::GetImageContainer(LayerMan
 NS_IMETHODIMP_(bool)
 DynamicImage::IsImageContainerAvailableAtSize(LayerManager* aManager,
                                               const IntSize& aSize,
                                               uint32_t aFlags)
 {
   return false;
 }
 
-NS_IMETHODIMP_(already_AddRefed<ImageContainer>)
-DynamicImage::GetImageContainerAtSize(LayerManager* aManager,
-                                      const IntSize& aSize,
-                                      const Maybe<SVGImageContext>& aSVGContext,
-                                      uint32_t aFlags)
+NS_IMETHODIMP_(ImgDrawResult)
+DynamicImage::GetImageContainerAtSize(layers::LayerManager* aManager,
+                                     const gfx::IntSize& aSize,
+                                     const Maybe<SVGImageContext>& aSVGContext,
+                                     uint32_t aFlags,
+                                     layers::ImageContainer** aContainer)
 {
-  return nullptr;
+  return ImgDrawResult::NOT_SUPPORTED;
 }
 
 NS_IMETHODIMP_(ImgDrawResult)
 DynamicImage::Draw(gfxContext* aContext,
                    const nsIntSize& aSize,
                    const ImageRegion& aRegion,
                    uint32_t aWhichFrame,
                    SamplingFilter aSamplingFilter,
--- a/image/FrozenImage.cpp
+++ b/image/FrozenImage.cpp
@@ -72,28 +72,29 @@ FrozenImage::GetImageContainer(layers::L
 NS_IMETHODIMP_(bool)
 FrozenImage::IsImageContainerAvailableAtSize(LayerManager* aManager,
                                              const IntSize& aSize,
                                              uint32_t aFlags)
 {
   return false;
 }
 
-NS_IMETHODIMP_(already_AddRefed<ImageContainer>)
+NS_IMETHODIMP_(ImgDrawResult)
 FrozenImage::GetImageContainerAtSize(layers::LayerManager* aManager,
-                                     const IntSize& aSize,
+                                     const gfx::IntSize& aSize,
                                      const Maybe<SVGImageContext>& aSVGContext,
-                                     uint32_t aFlags)
+                                     uint32_t aFlags,
+                                     layers::ImageContainer** 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 nullptr;
+  return ImgDrawResult::NOT_SUPPORTED;
 }
 
 NS_IMETHODIMP_(ImgDrawResult)
 FrozenImage::Draw(gfxContext* aContext,
                   const nsIntSize& aSize,
                   const ImageRegion& aRegion,
                   uint32_t /* aWhichFrame - ignored */,
                   SamplingFilter aSamplingFilter,
--- a/image/FrozenImage.h
+++ b/image/FrozenImage.h
@@ -45,21 +45,22 @@ public:
                                               uint32_t aFlags) override;
   NS_IMETHOD_(already_AddRefed<layers::ImageContainer>)
     GetImageContainer(layers::LayerManager* aManager,
                       uint32_t aFlags) override;
   NS_IMETHOD_(bool)
     IsImageContainerAvailableAtSize(layers::LayerManager* aManager,
                                     const gfx::IntSize& aSize,
                                     uint32_t aFlags) override;
-  NS_IMETHOD_(already_AddRefed<layers::ImageContainer>)
+  NS_IMETHOD_(ImgDrawResult)
     GetImageContainerAtSize(layers::LayerManager* aManager,
                             const gfx::IntSize& aSize,
                             const Maybe<SVGImageContext>& aSVGContext,
-                            uint32_t aFlags) override;
+                            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;
--- a/image/Image.cpp
+++ b/image/Image.cpp
@@ -99,36 +99,41 @@ ImageResource::SetCurrentImage(ImageCont
 
   if (aInTransaction) {
     aContainer->SetCurrentImagesInTransaction(imageList);
   } else {
     aContainer->SetCurrentImages(imageList);
   }
 }
 
-already_AddRefed<ImageContainer>
+ImgDrawResult
 ImageResource::GetImageContainerImpl(LayerManager* aManager,
                                      const IntSize& aSize,
                                      const Maybe<SVGImageContext>& aSVGContext,
-                                     uint32_t aFlags)
+                                     uint32_t aFlags,
+                                     ImageContainer** aOutContainer)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aManager);
   MOZ_ASSERT((aFlags & ~(FLAG_SYNC_DECODE |
                          FLAG_SYNC_DECODE_IF_FAST |
                          FLAG_ASYNC_NOTIFY |
                          FLAG_HIGH_QUALITY_SCALING))
                == FLAG_NONE,
              "Unsupported flag passed to GetImageContainer");
 
-  IntSize size = GetImageContainerSize(aManager, aSize, aFlags);
-  if (size.IsEmpty()) {
-    return nullptr;
+  ImgDrawResult drawResult;
+  IntSize size;
+  Tie(drawResult, size) = GetImageContainerSize(aManager, 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;
@@ -150,35 +155,36 @@ ImageResource::GetImageContainerImpl(Lay
     }
   }
 
   if (container) {
     switch (entry->mLastDrawResult) {
       case ImgDrawResult::SUCCESS:
       case ImgDrawResult::BAD_IMAGE:
       case ImgDrawResult::BAD_ARGS:
-        return container.forget();
+        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!");
-        return container.forget();
+        container.forget(aOutContainer);
+        return entry->mLastDrawResult;
     }
   }
 
 #ifdef DEBUG
   NotifyDrawingObservers();
 #endif
 
-  ImgDrawResult drawResult;
   IntSize bestSize;
   RefPtr<SourceSurface> surface;
   Tie(drawResult, bestSize, surface) =
     GetFrameInternal(size, aSVGContext, 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
@@ -207,28 +213,30 @@ ImageResource::GetImageContainerImpl(Lay
       if (bestSize == entry->mSize && flags == entry->mFlags &&
           aSVGContext == entry->mSVGContext) {
         container = entry->mContainer.get();
         if (container) {
           switch (entry->mLastDrawResult) {
             case ImgDrawResult::SUCCESS:
             case ImgDrawResult::BAD_IMAGE:
             case ImgDrawResult::BAD_ARGS:
-              return container.forget();
+              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!");
-              return container.forget();
+              container.forget(aOutContainer);
+              return entry->mLastDrawResult;
           }
         }
         break;
       }
     }
   }
 
   if (!container) {
@@ -240,17 +248,18 @@ ImageResource::GetImageContainerImpl(Lay
     } else {
       entry = mImageContainers.AppendElement(
         ImageContainerEntry(bestSize, aSVGContext, container.get(), flags));
     }
   }
 
   SetCurrentImage(container, surface, true);
   entry->mLastDrawResult = drawResult;
-  return container.forget();
+  container.forget(aOutContainer);
+  return drawResult;
 }
 
 void
 ImageResource::UpdateImageContainer()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   for (int i = mImageContainers.Length() - 1; i >= 0; --i) {
--- a/image/Image.h
+++ b/image/Image.h
@@ -348,28 +348,29 @@ protected:
   }
 
   /**
    * 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 gfx::IntSize GetImageContainerSize(layers::LayerManager* aManager,
-                                             const gfx::IntSize& aSize,
-                                             uint32_t aFlags)
+  virtual Tuple<ImgDrawResult, gfx::IntSize>
+    GetImageContainerSize(layers::LayerManager* aManager,
+                          const gfx::IntSize& aSize,
+                          uint32_t aFlags)
   {
-    return gfx::IntSize(0, 0);
+    return MakeTuple(ImgDrawResult::NOT_SUPPORTED, gfx::IntSize(0, 0));
   }
 
-  already_AddRefed<layers::ImageContainer>
-    GetImageContainerImpl(layers::LayerManager* aManager,
-                          const gfx::IntSize& aSize,
-                          const Maybe<SVGImageContext>& aSVGContext,
-                          uint32_t aFlags);
+  ImgDrawResult GetImageContainerImpl(layers::LayerManager* aManager,
+                                      const gfx::IntSize& aSize,
+                                      const Maybe<SVGImageContext>& aSVGContext,
+                                      uint32_t aFlags,
+                                      layers::ImageContainer** aContainer);
 
   void UpdateImageContainer();
 
   void ReleaseImageContainer();
 
 private:
   void SetCurrentImage(layers::ImageContainer* aContainer,
                        gfx::SourceSurface* aSurface,
--- a/image/ImageWrapper.cpp
+++ b/image/ImageWrapper.cpp
@@ -217,24 +217,25 @@ ImageWrapper::GetImageContainer(LayerMan
 NS_IMETHODIMP_(bool)
 ImageWrapper::IsImageContainerAvailableAtSize(LayerManager* aManager,
                                               const IntSize& aSize,
                                               uint32_t aFlags)
 {
   return mInnerImage->IsImageContainerAvailableAtSize(aManager, aSize, aFlags);
 }
 
-NS_IMETHODIMP_(already_AddRefed<ImageContainer>)
-ImageWrapper::GetImageContainerAtSize(LayerManager* aManager,
-                                      const IntSize& aSize,
+NS_IMETHODIMP_(ImgDrawResult)
+ImageWrapper::GetImageContainerAtSize(layers::LayerManager* aManager,
+                                      const gfx::IntSize& aSize,
                                       const Maybe<SVGImageContext>& aSVGContext,
-                                      uint32_t aFlags)
+                                      uint32_t aFlags,
+                                      layers::ImageContainer** aOutContainer)
 {
-  return mInnerImage->GetImageContainerAtSize(aManager, aSize,
-                                              aSVGContext, aFlags);
+  return mInnerImage->GetImageContainerAtSize(aManager, aSize, aSVGContext,
+                                              aFlags, aOutContainer);
 }
 
 NS_IMETHODIMP_(ImgDrawResult)
 ImageWrapper::Draw(gfxContext* aContext,
                    const nsIntSize& aSize,
                    const ImageRegion& aRegion,
                    uint32_t aWhichFrame,
                    SamplingFilter aSamplingFilter,
--- a/image/ImgDrawResult.h
+++ b/image/ImgDrawResult.h
@@ -40,26 +40,30 @@ namespace image {
  *
  * TEMPORARY_ERROR: We failed to draw due to a temporary error. Drawing may
  * succeed at a later time.
  *
  * BAD_IMAGE: We failed to draw because the image has an error. This is a
  * permanent condition.
  *
  * BAD_ARGS: We failed to draw because bad arguments were passed to draw().
+ *
+ * NOT_SUPPORTED: The requested operation is not supported, but the image is
+ *                otherwise valid.
  */
 enum class MOZ_MUST_USE_TYPE ImgDrawResult : uint8_t
 {
   SUCCESS,
   INCOMPLETE,
   WRONG_SIZE,
   NOT_READY,
   TEMPORARY_ERROR,
   BAD_IMAGE,
-  BAD_ARGS
+  BAD_ARGS,
+  NOT_SUPPORTED
 };
 
 /**
  * You can combine ImgDrawResults with &. By analogy to bitwise-&, the result is
  * ImgDrawResult::SUCCESS only if both operands are ImgDrawResult::SUCCESS. Otherwise,
  * a failing ImgDrawResult is returned; we favor the left operand's failure when
  * deciding which failure to return, with the exception that we always prefer
  * any other kind of failure over ImgDrawResult::BAD_IMAGE, since other failures
--- a/image/OrientedImage.cpp
+++ b/image/OrientedImage.cpp
@@ -177,34 +177,35 @@ OrientedImage::IsImageContainerAvailable
                                                uint32_t aFlags)
 {
   if (mOrientation.IsIdentity()) {
     return InnerImage()->IsImageContainerAvailableAtSize(aManager, aSize, aFlags);
   }
   return false;
 }
 
-NS_IMETHODIMP_(already_AddRefed<ImageContainer>)
-OrientedImage::GetImageContainerAtSize(LayerManager* aManager,
-                                       const IntSize& aSize,
+NS_IMETHODIMP_(ImgDrawResult)
+OrientedImage::GetImageContainerAtSize(layers::LayerManager* aManager,
+                                       const gfx::IntSize& aSize,
                                        const Maybe<SVGImageContext>& aSVGContext,
-                                       uint32_t aFlags)
+                                       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()->GetImageContainerAtSize(aManager, aSize,
-                                                 aSVGContext, aFlags);
+    return InnerImage()->GetImageContainerAtSize(aManager, aSize, aSVGContext,
+                                                 aFlags, aOutContainer);
   }
 
-  return nullptr;
+  return ImgDrawResult::NOT_SUPPORTED;
 }
 
 struct MatrixBuilder
 {
   explicit MatrixBuilder(bool aInvert) : mInvert(aInvert) { }
 
   gfxMatrix Build() { return mMatrix; }
 
--- a/image/OrientedImage.h
+++ b/image/OrientedImage.h
@@ -43,21 +43,22 @@ public:
                                               uint32_t aFlags) override;
   NS_IMETHOD_(already_AddRefed<layers::ImageContainer>)
     GetImageContainer(layers::LayerManager* aManager,
                       uint32_t aFlags) override;
   NS_IMETHOD_(bool)
     IsImageContainerAvailableAtSize(layers::LayerManager* aManager,
                                     const gfx::IntSize& aSize,
                                     uint32_t aFlags) override;
-  NS_IMETHOD_(already_AddRefed<layers::ImageContainer>)
+  NS_IMETHOD_(ImgDrawResult)
     GetImageContainerAtSize(layers::LayerManager* aManager,
                             const gfx::IntSize& aSize,
                             const Maybe<SVGImageContext>& aSVGContext,
-                            uint32_t aFlags) override;
+                            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;
--- a/image/RasterImage.cpp
+++ b/image/RasterImage.cpp
@@ -622,42 +622,59 @@ RasterImage::GetFrameInternal(const IntS
   RefPtr<SourceSurface> surface = result.Surface()->GetSourceSurface();
   if (!result.Surface()->IsFinished()) {
     return MakeTuple(ImgDrawResult::INCOMPLETE, suggestedSize, std::move(surface));
   }
 
   return MakeTuple(ImgDrawResult::SUCCESS, suggestedSize, std::move(surface));
 }
 
-IntSize
+Tuple<ImgDrawResult, IntSize>
 RasterImage::GetImageContainerSize(LayerManager* aManager,
                                    const IntSize& aSize,
                                    uint32_t aFlags)
 {
-  if (!IsImageContainerAvailableAtSize(aManager, aSize, aFlags)) {
-    return IntSize(0, 0);
+  if (!mHasSize) {
+    return MakeTuple(ImgDrawResult::NOT_READY, IntSize(0, 0));
+  }
+
+  if (aSize.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 aSize > mSize, we will never give a larger surface
+  // than mSize. If mSize > aSize, and mSize > maxTextureSize, we still want to
+  // use image containers if aSize <= maxTextureSize.
+  int32_t maxTextureSize = aManager->GetMaxTextureSize();
+  if (min(mSize.width, aSize.width) > maxTextureSize ||
+      min(mSize.height, aSize.height) > maxTextureSize) {
+    return MakeTuple(ImgDrawResult::NOT_SUPPORTED, IntSize(0, 0));
   }
 
   if (!CanDownscaleDuringDecode(aSize, aFlags)) {
-    return mSize;
+    return MakeTuple(ImgDrawResult::SUCCESS, mSize);
   }
 
-  return aSize;
+  return MakeTuple(ImgDrawResult::SUCCESS, aSize);
 }
 
 NS_IMETHODIMP_(bool)
 RasterImage::IsImageContainerAvailable(LayerManager* aManager, uint32_t aFlags)
 {
   return IsImageContainerAvailableAtSize(aManager, mSize, aFlags);
 }
 
 NS_IMETHODIMP_(already_AddRefed<ImageContainer>)
 RasterImage::GetImageContainer(LayerManager* aManager, uint32_t aFlags)
 {
-  return GetImageContainerImpl(aManager, mSize, Nothing(), aFlags);
+  RefPtr<ImageContainer> container;
+  GetImageContainerImpl(aManager, mSize, Nothing(), aFlags,
+                        getter_AddRefs(container));
+  return container.forget();
 }
 
 NS_IMETHODIMP_(bool)
 RasterImage::IsImageContainerAvailableAtSize(LayerManager* aManager,
                                              const IntSize& aSize,
                                              uint32_t aFlags)
 {
   // We check the minimum size because while we support downscaling, we do not
@@ -669,26 +686,28 @@ RasterImage::IsImageContainerAvailableAt
       min(mSize.width, aSize.width) > maxTextureSize ||
       min(mSize.height, aSize.height) > maxTextureSize) {
     return false;
   }
 
   return true;
 }
 
-NS_IMETHODIMP_(already_AddRefed<ImageContainer>)
-RasterImage::GetImageContainerAtSize(LayerManager* aManager,
-                                     const IntSize& aSize,
+NS_IMETHODIMP_(ImgDrawResult)
+RasterImage::GetImageContainerAtSize(layers::LayerManager* aManager,
+                                     const gfx::IntSize& aSize,
                                      const Maybe<SVGImageContext>& aSVGContext,
-                                     uint32_t aFlags)
+                                     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(aManager, aSize, Nothing(), aFlags);
+  return GetImageContainerImpl(aManager, aSize, Nothing(),
+                               aFlags, aOutContainer);
 }
 
 size_t
 RasterImage::SizeOfSourceWithComputedFallback(SizeOfState& aState) const
 {
   return mSourceBuffer->SizeOfIncludingThisWithComputedFallback(
     aState.mMallocSizeOf);
 }
--- a/image/RasterImage.h
+++ b/image/RasterImage.h
@@ -307,19 +307,20 @@ private:
                           float aOpacity);
 
   Tuple<ImgDrawResult, gfx::IntSize, RefPtr<gfx::SourceSurface>>
     GetFrameInternal(const gfx::IntSize& aSize,
                      const Maybe<SVGImageContext>& aSVGContext,
                      uint32_t aWhichFrame,
                      uint32_t aFlags) override;
 
-  gfx::IntSize GetImageContainerSize(layers::LayerManager* aManager,
-                                     const gfx::IntSize& aSize,
-                                     uint32_t aFlags) override;
+  Tuple<ImgDrawResult, gfx::IntSize>
+    GetImageContainerSize(layers::LayerManager* aManager,
+                          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
--- a/image/VectorImage.cpp
+++ b/image/VectorImage.cpp
@@ -844,26 +844,41 @@ VectorImage::GetFrameInternal(const IntS
                      RefPtr<SourceSurface>());
   }
 
   SendFrameComplete(didCache, params.flags);
   return MakeTuple(ImgDrawResult::SUCCESS, aSize, std::move(surface));
 }
 
 //******************************************************************************
-IntSize
+Tuple<ImgDrawResult, IntSize>
 VectorImage::GetImageContainerSize(LayerManager* aManager,
                                    const IntSize& aSize,
                                    uint32_t aFlags)
 {
-  if (!IsImageContainerAvailableAtSize(aManager, aSize, aFlags)) {
-    return IntSize(0, 0);
+  if (mError) {
+    return MakeTuple(ImgDrawResult::BAD_IMAGE, IntSize(0, 0));
+  }
+
+  if (!mIsFullyLoaded) {
+    return MakeTuple(ImgDrawResult::NOT_READY, IntSize(0, 0));
   }
 
-  return aSize;
+  if (mHaveAnimations ||
+      aManager->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(LayerManager* aManager, uint32_t aFlags)
 {
   if (mError || !mIsFullyLoaded || mHaveAnimations ||
       aManager->GetBackendType() != LayersBackend::LAYERS_WR) {
     return false;
@@ -888,34 +903,35 @@ VectorImage::IsImageContainerAvailableAt
                                              uint32_t aFlags)
 {
   // Since we only support image containers with WebRender, and it can handle
   // textures larger than the hw max texture size, we don't need to check aSize.
   return !aSize.IsEmpty() && IsImageContainerAvailable(aManager, aFlags);
 }
 
 //******************************************************************************
-NS_IMETHODIMP_(already_AddRefed<ImageContainer>)
-VectorImage::GetImageContainerAtSize(LayerManager* aManager,
-                                     const IntSize& aSize,
+NS_IMETHODIMP_(ImgDrawResult)
+VectorImage::GetImageContainerAtSize(layers::LayerManager* aManager,
+                                     const gfx::IntSize& aSize,
                                      const Maybe<SVGImageContext>& aSVGContext,
-                                     uint32_t aFlags)
+                                     uint32_t aFlags,
+                                     layers::ImageContainer** aOutContainer)
 {
   Maybe<SVGImageContext> newSVGContext;
   MaybeRestrictSVGContext(newSVGContext, aSVGContext, aFlags);
 
   // Since we do not support high quality scaling with SVG, we mask it off so
   // that container requests with and without it map to the same container.
   // Similarly the aspect ratio flag was already handled as part of the SVG
   // context restriction above.
   uint32_t flags = aFlags & ~(FLAG_HIGH_QUALITY_SCALING |
                               FLAG_FORCE_PRESERVEASPECTRATIO_NONE);
   return GetImageContainerImpl(aManager, aSize,
                                newSVGContext ? newSVGContext : aSVGContext,
-                               flags);
+                               flags, aOutContainer);
 }
 
 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
@@ -82,19 +82,20 @@ protected:
 
 private:
   Tuple<ImgDrawResult, IntSize, RefPtr<SourceSurface>>
     GetFrameInternal(const IntSize& aSize,
                      const Maybe<SVGImageContext>& aSVGContext,
                      uint32_t aWhichFrame,
                      uint32_t aFlags) override;
 
-  IntSize GetImageContainerSize(layers::LayerManager* aManager,
-                                const IntSize& aSize,
-                                uint32_t aFlags) override;
+  Tuple<ImgDrawResult, IntSize>
+    GetImageContainerSize(layers::LayerManager* aManager,
+                          const IntSize& aSize,
+                          uint32_t aFlags) override;
 
   /// Attempt to find a matching cached surface in the SurfaceCache.
   already_AddRefed<SourceSurface>
     LookupCachedSurface(const IntSize& aSize,
                         const Maybe<SVGImageContext>& aSVGContext,
                         uint32_t aFlags);
 
   bool MaybeRestrictSVGContext(Maybe<SVGImageContext>& aNewSVGContext,
--- a/image/imgIContainer.idl
+++ b/image/imgIContainer.idl
@@ -52,16 +52,17 @@ native ImgDrawResult(mozilla::image::Img
 [ref] native gfxSize(gfxSize);
 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);
 [ref] native ImageRegion(mozilla::image::ImageRegion);
 [ptr] native LayerManager(mozilla::layers::LayerManager);
 native Orientation(mozilla::image::Orientation);
 [ref] native TimeStamp(mozilla::TimeStamp);
 [ref] native MaybeSVGImageContext(mozilla::Maybe<mozilla::SVGImageContext>);
 native TempRefSourceSurface(already_AddRefed<mozilla::gfx::SourceSurface>);
 native TempRefImgIContainer(already_AddRefed<imgIContainer>);
 native nsIntSizeByVal(nsIntSize);
@@ -291,25 +292,16 @@ interface imgIContainer : nsISupports
    *               are supported.
    * @return An ImageContainer for the current frame, or nullptr if one could
    *         not be created.
    */
   [noscript, notxpcom] TempRefImageContainer getImageContainer(in LayerManager aManager,
                                                                in uint32_t aFlags);
 
   /**
-   * @return true if getImageContainer() is expected to return a valid
-   *         ImageContainer when passed the given @Manager, @Size and @Flags
-   *         parameters.
-   */
-  [noscript, notxpcom] boolean isImageContainerAvailableAtSize(in LayerManager aManager,
-                                                               [const] in nsIntSize aSize,
-                                                               in uint32_t aFlags);
-
-  /**
    * 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.
    *
@@ -317,23 +309,34 @@ interface imgIContainer : nsISupports
    *                 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.
-   * @return An ImageContainer for the current frame, or nullptr if one could
-   *         not be created.
+   * @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] TempRefImageContainer getImageContainerAtSize(in LayerManager aManager,
-                                                                     [const] in nsIntSize aSize,
-                                                                     [const] in MaybeSVGImageContext aSVGContext,
-                                                                     in uint32_t aFlags);
+  [noscript, notxpcom] ImgDrawResult getImageContainerAtSize(in LayerManager aManager,
+                                                             [const] in nsIntSize aSize,
+                                                             [const] in MaybeSVGImageContext aSVGContext,
+                                                             in uint32_t aFlags,
+                                                             out ImageContainer aOutContainer);
+
+  /**
+   * @return true if getImageContainer() is expected to return a valid
+   *         ImageContainer when passed the given @Manager, @Size and @Flags
+   *         parameters.
+   */
+  [noscript, notxpcom] boolean isImageContainerAvailableAtSize(in LayerManager aManager,
+                                                               [const] in nsIntSize aSize,
+                                                               in uint32_t aFlags);
 
   /**
    * 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/test/gtest/TestContainers.cpp
+++ b/image/test/gtest/TestContainers.cpp
@@ -65,41 +65,44 @@ TEST_F(ImageContainers, RasterImageConta
   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 =
-    image->GetImageContainerAtSize(layerManager,
-                                   requestedSize,
-                                   Nothing(),
-                                   imgIContainer::FLAG_SYNC_DECODE |
-                                   imgIContainer::FLAG_HIGH_QUALITY_SCALING);
+  RefPtr<layers::ImageContainer> upscaleContainer;
+  image->GetImageContainerAtSize(layerManager,
+                                 requestedSize,
+                                 Nothing(),
+                                 imgIContainer::FLAG_SYNC_DECODE |
+                                 imgIContainer::FLAG_HIGH_QUALITY_SCALING,
+                                 getter_AddRefs(upscaleContainer));
   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 =
-    image->GetImageContainerAtSize(layerManager,
-                                   requestedSize,
-                                   Nothing(),
-                                   imgIContainer::FLAG_SYNC_DECODE |
-                                   imgIContainer::FLAG_HIGH_QUALITY_SCALING);
+  RefPtr<layers::ImageContainer> downscaleContainer;
+  image->GetImageContainerAtSize(layerManager,
+                                 requestedSize,
+                                 Nothing(),
+                                 imgIContainer::FLAG_SYNC_DECODE |
+                                 imgIContainer::FLAG_HIGH_QUALITY_SCALING,
+                                 getter_AddRefs(downscaleContainer));
   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 =
-    image->GetImageContainerAtSize(layerManager,
-                                   testCase.mSize,
-                                   Nothing(),
-                                   imgIContainer::FLAG_SYNC_DECODE);
+  RefPtr<layers::ImageContainer> againContainer;
+  image->GetImageContainerAtSize(layerManager,
+                                 testCase.mSize,
+                                 Nothing(),
+                                 imgIContainer::FLAG_SYNC_DECODE,
+                                 getter_AddRefs(againContainer));
   ASSERT_EQ(nativeContainer.get(), againContainer.get());
 }
--- a/layout/generic/nsBulletFrame.cpp
+++ b/layout/generic/nsBulletFrame.cpp
@@ -474,18 +474,20 @@ BulletRenderer::CreateWebRenderCommandsF
   }
 
   const int32_t appUnitsPerDevPixel = aItem->Frame()->PresContext()->AppUnitsPerDevPixel();
   LayoutDeviceRect destRect = LayoutDeviceRect::FromAppUnits(mDest, appUnitsPerDevPixel);
   Maybe<SVGImageContext> svgContext;
   gfx::IntSize decodeSize =
     nsLayoutUtils::ComputeImageContainerDrawingParameters(mImage, aItem->Frame(), destRect,
                                                           aSc, flags, svgContext);
-  RefPtr<layers::ImageContainer> container =
-    mImage->GetImageContainerAtSize(aManager, decodeSize, svgContext, flags);
+
+  RefPtr<layers::ImageContainer> container;
+  mImage->GetImageContainerAtSize(aManager, decodeSize, svgContext,
+                                  flags, getter_AddRefs(container));
   if (!container) {
     return false;
   }
 
   gfx::IntSize size;
   Maybe<wr::ImageKey> key = aManager->CommandBuilder().CreateImageKey(aItem, container, aBuilder, aResources,
                                                                       aSc, size, Nothing());
   if (key.isNothing()) {
--- a/layout/generic/nsImageFrame.cpp
+++ b/layout/generic/nsImageFrame.cpp
@@ -1806,18 +1806,21 @@ nsDisplayImage::CreateWebRenderCommands(
 
   const int32_t factor = mFrame->PresContext()->AppUnitsPerDevPixel();
   const LayoutDeviceRect destRect(
     LayoutDeviceRect::FromAppUnits(GetDestRect(), factor));
   Maybe<SVGImageContext> svgContext;
   IntSize decodeSize =
     nsLayoutUtils::ComputeImageContainerDrawingParameters(mImage, mFrame, destRect,
                                                           aSc, flags, svgContext);
-  RefPtr<ImageContainer> container =
-    mImage->GetImageContainerAtSize(aManager, decodeSize, svgContext, flags);
+
+  RefPtr<layers::ImageContainer> container;
+  ImgDrawResult drawResult =
+    mImage->GetImageContainerAtSize(aManager, decodeSize, svgContext,
+                                    flags, getter_AddRefs(container));
   if (!container) {
     return false;
   }
 
   // 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.
   aManager->CommandBuilder().PushImage(this, container, aBuilder, aResources, aSc, destRect);
--- a/layout/painting/nsCSSRenderingBorders.cpp
+++ b/layout/painting/nsCSSRenderingBorders.cpp
@@ -3716,18 +3716,20 @@ nsCSSBorderImageRenderer::CreateWebRende
         flags |= imgIContainer::FLAG_SYNC_DECODE;
       }
 
       RefPtr<imgIContainer> img = mImageRenderer.GetImage();
       Maybe<SVGImageContext> svgContext;
       gfx::IntSize decodeSize =
         nsLayoutUtils::ComputeImageContainerDrawingParameters(img, aForFrame, destRect,
                                                               aSc, flags, svgContext);
-      RefPtr<layers::ImageContainer> container =
-        img->GetImageContainerAtSize(aManager, decodeSize, svgContext, flags);
+
+      RefPtr<layers::ImageContainer> container;
+      img->GetImageContainerAtSize(aManager, decodeSize, svgContext,
+                                   flags, getter_AddRefs(container));
       if (!container) {
         return;
       }
 
       gfx::IntSize size;
       Maybe<wr::ImageKey> key = aManager->CommandBuilder().CreateImageKey(aItem, container, aBuilder,
                                                                           aResources, aSc, size, Nothing());
       if (key.isNothing()) {
--- a/layout/painting/nsImageRenderer.cpp
+++ b/layout/painting/nsImageRenderer.cpp
@@ -613,18 +613,20 @@ nsImageRenderer::BuildWebRenderDisplayIt
       Maybe<SVGImageContext> svgContext(Some(SVGImageContext(Some(imageSize))));
 
       const int32_t appUnitsPerDevPixel = mForFrame->PresContext()->AppUnitsPerDevPixel();
       LayoutDeviceRect destRect = LayoutDeviceRect::FromAppUnits(
           aDest, appUnitsPerDevPixel);
       gfx::IntSize decodeSize =
         nsLayoutUtils::ComputeImageContainerDrawingParameters(mImageContainer, mForFrame, destRect,
                                                               aSc, containerFlags, svgContext);
-      RefPtr<layers::ImageContainer> container =
-        mImageContainer->GetImageContainerAtSize(aManager, decodeSize, svgContext, containerFlags);
+
+      RefPtr<layers::ImageContainer> container;
+      mImageContainer->GetImageContainerAtSize(aManager, decodeSize, svgContext,
+                                               containerFlags, getter_AddRefs(container));
       if (!container) {
         NS_WARNING("Failed to get image container");
         return ImgDrawResult::NOT_READY;
       }
 
       gfx::IntSize size;
       Maybe<wr::ImageKey> key = aManager->CommandBuilder().CreateImageKey(aItem, container, aBuilder,
                                                                           aResources, aSc, size, Nothing());
--- a/layout/xul/nsImageBoxFrame.cpp
+++ b/layout/xul/nsImageBoxFrame.cpp
@@ -447,18 +447,20 @@ nsImageBoxFrame::CreateWebRenderCommands
 
   const int32_t appUnitsPerDevPixel = PresContext()->AppUnitsPerDevPixel();
   LayoutDeviceRect fillRect = LayoutDeviceRect::FromAppUnits(dest,
                                                              appUnitsPerDevPixel);
   Maybe<SVGImageContext> svgContext;
   gfx::IntSize decodeSize =
     nsLayoutUtils::ComputeImageContainerDrawingParameters(imgCon, aItem->Frame(), fillRect,
                                                           aSc, containerFlags, svgContext);
-  RefPtr<layers::ImageContainer> container =
-    imgCon->GetImageContainerAtSize(aManager, decodeSize, svgContext, containerFlags);
+
+  RefPtr<layers::ImageContainer> container;
+  imgCon->GetImageContainerAtSize(aManager, decodeSize, svgContext,
+                                  containerFlags, getter_AddRefs(container));
   if (!container) {
     NS_WARNING("Failed to get image container");
     return Nothing();
   }
 
   gfx::IntSize size;
   Maybe<wr::ImageKey> key = aManager->CommandBuilder().CreateImageKey(aItem, container,
                                                                       aBuilder, aResources,
--- a/widget/nsBaseDragService.cpp
+++ b/widget/nsBaseDragService.cpp
@@ -784,17 +784,18 @@ nsBaseDragService::DrawDragForImage(nsPr
     if (!ctx)
       return NS_ERROR_FAILURE;
 
     ImgDrawResult res =
       imgContainer->Draw(ctx, destSize, ImageRegion::Create(destSize),
                          imgIContainer::FRAME_CURRENT,
                          SamplingFilter::GOOD, /* no SVGImageContext */ Nothing(),
                          imgIContainer::FLAG_SYNC_DECODE, 1.0);
-    if (res == ImgDrawResult::BAD_IMAGE || res == ImgDrawResult::BAD_ARGS) {
+    if (res == ImgDrawResult::BAD_IMAGE || res == ImgDrawResult::BAD_ARGS ||
+        res == ImgDrawResult::NOT_SUPPORTED) {
       return NS_ERROR_FAILURE;
     }
     *aSurface = dt->Snapshot();
   } else {
     *aSurface = aCanvas->GetSurfaceSnapshot();
   }
 
   return result;