Backout 051cf1c1449c:521215a7b32e (bug 826093) for Android reftest failure
authorPhil Ringnalda <philringnalda@gmail.com>
Thu, 04 Apr 2013 21:06:58 -0700
changeset 127756 4c45dbd81a3224768c250fd2bac7d0dbc53d98e6
parent 127755 91ac668eeace1af9bf22b24e2d4ea3bd8574427b
child 127757 8396d7543197fafcb70ab42b7d55d6d00e0e360d
push id24512
push userryanvm@gmail.com
push dateFri, 05 Apr 2013 20:13:49 +0000
treeherdermozilla-central@139b6ba547fa [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs826093
milestone23.0a1
backs out051cf1c1449c2e45f776e701a33d05ea29acf2c1
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
Backout 051cf1c1449c:521215a7b32e (bug 826093) for Android reftest failure
image/public/imgIContainer.idl
image/src/ClippedImage.cpp
image/src/ClippedImage.h
image/src/FrozenImage.cpp
image/src/FrozenImage.h
image/src/ImageFactory.cpp
image/src/ImageFactory.h
image/src/ImageOps.cpp
image/src/ImageOps.h
image/src/ImageWrapper.cpp
image/src/Makefile.in
image/src/RasterImage.cpp
image/src/VectorImage.cpp
image/src/VectorImage.h
image/src/imgRequestProxy.cpp
layout/base/nsCSSRendering.cpp
--- a/image/public/imgIContainer.idl
+++ b/image/public/imgIContainer.idl
@@ -47,22 +47,23 @@ native nsSize(nsSize);
 [ptr] native ImageContainer(mozilla::layers::ImageContainer);
 [ptr] native LayerManager(mozilla::layers::LayerManager);
 [ref] native TimeStamp(mozilla::TimeStamp);
 [ptr] native SVGImageContext(mozilla::SVGImageContext);
 
 
 /**
  * imgIContainer is the interface that represents an image. It allows
- * access to frames as Thebes surfaces. It also allows drawing of images
- * onto Thebes contexts.
+ * access to frames as Thebes surfaces, and permits users to extract subregions
+ * as other imgIContainers. It also allows drawing of images on to Thebes
+ * contexts.
  *
  * Internally, imgIContainer also manages animation of images.
  */
-[scriptable, builtinclass, uuid(0c1caf24-bce7-4db5-971d-8e1b6ed07540)]
+[scriptable, builtinclass, uuid(01c4f92f-f883-4837-a127-d8f30920e374)]
 interface imgIContainer : nsISupports
 {
   /**
    * The width of the container rectangle.  In the case of any error,
    * zero is returned, and an exception will be thrown.
    */
   readonly attribute int32_t width;
 
@@ -173,16 +174,29 @@ interface imgIContainer : nsISupports
 
   /**
    * Attempts to create an ImageContainer (and Image) containing the current
    * frame. Only valid for RASTER type images.
    */
   [noscript] ImageContainer getImageContainer(in LayerManager aManager);
 
   /**
+   * Create a new imgContainer that contains only a single frame, which itself
+   * contains a subregion of the given frame.
+   *
+   * @param aWhichFrame Frame specifier of the FRAME_* variety.
+   * @param aRect the area of the current frame to be duplicated in the
+   *              returned imgContainer's frame.
+   * @param aFlags Flags of the FLAG_* variety
+   */
+  [noscript] imgIContainer extractFrame(in uint32_t aWhichFrame,
+                                        [const] in nsIntRect aRect,
+                                        in uint32_t aFlags);
+
+  /**
    * Draw a frame onto the context specified.
    *
    * @param aContext The Thebes context to draw the image to.
    * @param aFilter The filter to be used if we're scaling the image.
    * @param aUserSpaceToImageSpace The transformation from user space (e.g.,
    *                               appunits) to image space.
    * @param aFill The area in the context to draw pixels to. When aFlags includes
    *              FLAG_CLAMP, the image will be extended to this area by clampling
@@ -210,20 +224,21 @@ interface imgIContainer : nsISupports
                        [const] in nsIntRect aSubimage,
                        [const] in nsIntSize aViewportSize,
                        [const] in SVGImageContext aSVGContext,
                        in uint32_t aWhichFrame,
                        in uint32_t aFlags);
 
   /*
    * Ensures that an image is decoding. Calling this function guarantees that
-   * the image will at some point fire off decode notifications. Calling draw()
-   * or getFrame() triggers the same mechanism internally. Thus, if you want to
-   * be sure that the image will be decoded but don't want to access it until
-   * then, you must call requestDecode().
+   * the image will at some point fire off decode notifications. Calling draw(),
+   * getFrame(), copyFrame(), or extractCurrentFrame() triggers the same
+   * mechanism internally. Thus, if you want to be sure that the image will be
+   * decoded but don't want to access it until then, you must call
+   * requestDecode().
    */
   void requestDecode();
 
   /*
    * This is equivalent to requestDecode() but it also decodes some of the image.
    */
   [noscript] void startDecoding();
 
deleted file mode 100644
--- a/image/src/ClippedImage.cpp
+++ /dev/null
@@ -1,324 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "gfxDrawable.h"
-#include "gfxPlatform.h"
-#include "gfxUtils.h"
-
-#include "ClippedImage.h"
-
-using mozilla::layers::LayerManager;
-using mozilla::layers::ImageContainer;
-
-namespace mozilla {
-namespace image {
-
-class DrawSingleTileCallback : public gfxDrawingCallback
-{
-public:
-  DrawSingleTileCallback(ClippedImage* aImage,
-                         const nsIntRect& aClip,
-                         const nsIntSize& aViewportSize,
-                         const SVGImageContext* aSVGContext,
-                         uint32_t aWhichFrame,
-                         uint32_t aFlags)
-    : mImage(aImage)
-    , mClip(aClip)
-    , mViewportSize(aViewportSize)
-    , mSVGContext(aSVGContext)
-    , mWhichFrame(aWhichFrame)
-    , mFlags(aFlags)
-  {
-    MOZ_ASSERT(mImage, "Must have an image to clip");
-  }
-
-  virtual bool operator()(gfxContext* aContext,
-                          const gfxRect& aFillRect,
-                          const gfxPattern::GraphicsFilter& aFilter,
-                          const gfxMatrix& aTransform)
-  {
-    // Draw the image. |gfxCallbackDrawable| always calls this function with
-    // arguments that guarantee we never tile.
-    mImage->DrawSingleTile(aContext, aFilter, aTransform, aFillRect, mClip,
-                           mViewportSize, mSVGContext, mWhichFrame, mFlags);
-
-    return true;
-  }
-
-private:
-  nsRefPtr<ClippedImage> mImage;
-  const nsIntRect        mClip;
-  const nsIntSize        mViewportSize;
-  const SVGImageContext* mSVGContext;
-  const uint32_t         mWhichFrame;
-  const uint32_t         mFlags;
-};
-
-ClippedImage::ClippedImage(Image* aImage,
-                           nsIntRect aClip)
-  : ImageWrapper(aImage)
-  , mClip(aClip)
-{
-  MOZ_ASSERT(aImage != nullptr, "ClippedImage requires an existing Image");
-}
-
-bool
-ClippedImage::ShouldClip()
-{
-  // We need to evaluate the clipping region against the image's width and height
-  // once they're available to determine if it's valid and whether we actually
-  // need to do any work. We may fail if the image's width and height aren't
-  // available yet, in which case we'll try again later.
-  if (mShouldClip.empty()) {
-    int32_t width, height;
-    if (InnerImage()->HasError()) {
-      // If there's a problem with the inner image we'll let it handle everything.
-      mShouldClip.construct(false);
-    } else if (NS_SUCCEEDED(InnerImage()->GetWidth(&width)) && width > 0 &&
-               NS_SUCCEEDED(InnerImage()->GetHeight(&height)) && height > 0) {
-      // Clamp the clipping region to the size of the underlying image.
-      mClip = mClip.Intersect(nsIntRect(0, 0, width, height));
-
-      // If the clipping region is the same size as the underlying image we
-      // don't have to do anything.
-      mShouldClip.construct(!mClip.IsEqualInterior(nsIntRect(0, 0, width, height)));
-    } else if (InnerImage()->GetStatusTracker().IsLoading()) {
-      // The image just hasn't finished loading yet. We don't yet know whether
-      // clipping with be needed or not for now. Just return without memoizing
-      // anything.
-      return false;
-    } else {
-      // We have a fully loaded image without a clearly defined width and
-      // height. This can happen with SVG images.
-      mShouldClip.construct(false);
-    }
-  }
-
-  MOZ_ASSERT(!mShouldClip.empty(), "Should have computed a result");
-  return mShouldClip.ref();
-}
-
-NS_IMPL_ISUPPORTS1(ClippedImage, imgIContainer)
-
-nsIntRect
-ClippedImage::FrameRect(uint32_t aWhichFrame)
-{
-  if (!ShouldClip()) {
-    return InnerImage()->FrameRect(aWhichFrame);
-  }
-
-  return nsIntRect(0, 0, mClip.width, mClip.height);
-}
-
-NS_IMETHODIMP
-ClippedImage::GetWidth(int32_t* aWidth)
-{
-  if (!ShouldClip()) {
-    return InnerImage()->GetWidth(aWidth);
-  }
-
-  *aWidth = mClip.width;
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-ClippedImage::GetHeight(int32_t* aHeight)
-{
-  if (!ShouldClip()) {
-    return InnerImage()->GetHeight(aHeight);
-  }
-
-  *aHeight = mClip.height;
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-ClippedImage::GetIntrinsicSize(nsSize* aSize)
-{
-  if (!ShouldClip()) {
-    return InnerImage()->GetIntrinsicSize(aSize);
-  }
-
-  *aSize = nsSize(mClip.width, mClip.height);
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-ClippedImage::GetIntrinsicRatio(nsSize* aRatio)
-{
-  if (!ShouldClip()) {
-    return InnerImage()->GetIntrinsicRatio(aRatio);
-  }
-
-  *aRatio = nsSize(mClip.width, mClip.height);
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-ClippedImage::GetFrame(uint32_t aWhichFrame,
-                       uint32_t aFlags,
-                       gfxASurface** _retval)
-{
-  if (!ShouldClip()) {
-    return InnerImage()->GetFrame(aWhichFrame, aFlags, _retval);
-  }
-
-  // Create a surface to draw into.
-  gfxImageSurface::gfxImageFormat format = gfxASurface::ImageFormatARGB32;
-  nsRefPtr<gfxASurface> surface = gfxPlatform::GetPlatform()
-    ->CreateOffscreenSurface(gfxIntSize(mClip.width, mClip.height),
-                             gfxImageSurface::ContentFromFormat(format));
-  // Create our callback.
-  nsRefPtr<gfxDrawingCallback> drawTileCallback =
-    new DrawSingleTileCallback(this, mClip, mClip.Size(), nullptr, aWhichFrame, aFlags);
-  nsRefPtr<gfxDrawable> drawable =
-    new gfxCallbackDrawable(drawTileCallback, mClip.Size());
-
-  // Actually draw. The callback will end up invoking DrawSingleTile.
-  nsRefPtr<gfxContext> ctx = new gfxContext(surface);
-  gfxRect imageRect(0, 0, mClip.width, mClip.height);
-  gfxUtils::DrawPixelSnapped(ctx, drawable, gfxMatrix(),
-                             imageRect, imageRect, imageRect, imageRect,
-                             gfxASurface::ImageFormatARGB32, gfxPattern::FILTER_FAST);
-
-  *_retval = surface.forget().get();
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-ClippedImage::GetImageContainer(LayerManager* aManager, ImageContainer** _retval)
-{
-  // 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.
-
-  *_retval = nullptr;
-  return NS_OK;
-}
-
-bool
-ClippedImage::MustCreateSurface(gfxContext* aContext,
-                                const gfxMatrix& aTransform,
-                                const gfxRect& aSourceRect,
-                                const nsIntRect& aSubimage,
-                                const uint32_t aFlags) const
-{
-  gfxRect gfxImageRect(0, 0, mClip.width, mClip.height);
-  nsIntRect intImageRect(0, 0, mClip.width, mClip.height);
-  bool willTile = !gfxImageRect.Contains(aSourceRect) &&
-                  !(aFlags & imgIContainer::FLAG_CLAMP);
-  bool willResample = (aContext->CurrentMatrix().HasNonIntegerTranslation() ||
-                       aTransform.HasNonIntegerTranslation()) &&
-                      (willTile || !aSubimage.Contains(intImageRect));
-  return willTile || willResample;
-}
-
-NS_IMETHODIMP
-ClippedImage::Draw(gfxContext* aContext,
-                   gfxPattern::GraphicsFilter aFilter,
-                   const gfxMatrix& aUserSpaceToImageSpace,
-                   const gfxRect& aFill,
-                   const nsIntRect& aSubimage,
-                   const nsIntSize& aViewportSize,
-                   const SVGImageContext* aSVGContext,
-                   uint32_t aWhichFrame,
-                   uint32_t aFlags)
-{
-  if (!ShouldClip()) {
-    return InnerImage()->Draw(aContext, aFilter, aUserSpaceToImageSpace,
-                              aFill, aSubimage, aViewportSize, aSVGContext,
-                              aWhichFrame, aFlags);
-  }
-
-  // Check for tiling. If we need to tile then we need to create a
-  // gfxCallbackDrawable to handle drawing for us.
-  gfxRect sourceRect = aUserSpaceToImageSpace.Transform(aFill);
-  if (MustCreateSurface(aContext, aUserSpaceToImageSpace, sourceRect, aSubimage, aFlags)) {
-    // Create a temporary surface containing a single tile of this image.
-    // GetFrame will call DrawSingleTile internally.
-    nsRefPtr<gfxASurface> surface;
-    GetFrame(aWhichFrame, aFlags, getter_AddRefs(surface));
-    NS_ENSURE_TRUE(surface, NS_ERROR_FAILURE);
-
-    // Create a drawable from that surface.
-    nsRefPtr<gfxSurfaceDrawable> drawable =
-      new gfxSurfaceDrawable(surface, gfxIntSize(mClip.width, mClip.height));
-
-    // Draw.
-    gfxRect imageRect(0, 0, mClip.width, mClip.height);
-    gfxRect subimage(aSubimage.x, aSubimage.y, aSubimage.width, aSubimage.height);
-    gfxUtils::DrawPixelSnapped(aContext, drawable, aUserSpaceToImageSpace,
-                               subimage, sourceRect, imageRect, aFill,
-                               gfxASurface::ImageFormatARGB32, aFilter);
-
-    return NS_OK;
-  }
-
-  // Determine the appropriate subimage for the inner image.
-  nsIntRect innerSubimage(aSubimage);
-  innerSubimage.MoveBy(mClip.x, mClip.y);
-  innerSubimage.Intersect(mClip);
-
-  return DrawSingleTile(aContext, aFilter, aUserSpaceToImageSpace, aFill, innerSubimage,
-                        aViewportSize, aSVGContext, aWhichFrame, aFlags);
-}
-
-gfxFloat
-ClippedImage::ClampFactor(const gfxFloat aToClamp, const int aReference) const
-{
-  return aToClamp > aReference ? aReference / aToClamp
-                               : 1.0;
-}
-
-nsresult
-ClippedImage::DrawSingleTile(gfxContext* aContext,
-                             gfxPattern::GraphicsFilter aFilter,
-                             const gfxMatrix& aUserSpaceToImageSpace,
-                             const gfxRect& aFill,
-                             const nsIntRect& aSubimage,
-                             const nsIntSize& aViewportSize,
-                             const SVGImageContext* aSVGContext,
-                             uint32_t aWhichFrame,
-                             uint32_t aFlags)
-{
-  MOZ_ASSERT(!MustCreateSurface(aContext, aUserSpaceToImageSpace,
-                                aUserSpaceToImageSpace.Transform(aFill),
-                                aSubimage - nsIntPoint(mClip.x, mClip.y), aFlags),
-             "DrawSingleTile shouldn't need to create a surface");
-
-  // Make the viewport reflect the original image's size.
-  nsIntSize viewportSize(aViewportSize);
-  int32_t imgWidth, imgHeight;
-  if (NS_SUCCEEDED(InnerImage()->GetWidth(&imgWidth)) &&
-      NS_SUCCEEDED(InnerImage()->GetHeight(&imgHeight))) {
-    viewportSize = nsIntSize(imgWidth, imgHeight);
-  } else {
-    MOZ_ASSERT(false, "If ShouldClip() led us to draw then we should never get here");
-  }
-
-  // Add a translation to the transform to reflect the clipping region.
-  gfxMatrix transform(aUserSpaceToImageSpace);
-  transform.Multiply(gfxMatrix().Translate(gfxPoint(mClip.x, mClip.y)));
-
-  // "Clamp the source rectangle" to the clipping region's width and height.
-  // Really, this means modifying the transform to get the results we want.
-  gfxRect sourceRect = transform.Transform(aFill);
-  if (sourceRect.width > mClip.width || sourceRect.height > mClip.height) {
-    gfxMatrix clampSource;
-    clampSource.Translate(gfxPoint(sourceRect.x, sourceRect.y));
-    clampSource.Scale(ClampFactor(sourceRect.width, mClip.width),
-                      ClampFactor(sourceRect.height, mClip.height));
-    clampSource.Translate(gfxPoint(-sourceRect.x, -sourceRect.y));
-    transform.Multiply(clampSource);
-  }
-
-  return InnerImage()->Draw(aContext, aFilter, transform, aFill, aSubimage,
-                            viewportSize, aSVGContext, aWhichFrame, aFlags);
-}
-
-} // namespace image
-} // namespace mozilla
deleted file mode 100644
--- a/image/src/ClippedImage.h
+++ /dev/null
@@ -1,82 +0,0 @@
-/* -*- 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/. */
-
-#ifndef MOZILLA_IMAGELIB_CLIPPEDIMAGE_H_
-#define MOZILLA_IMAGELIB_CLIPPEDIMAGE_H_
-
-#include "ImageWrapper.h"
-
-namespace mozilla {
-namespace image {
-
-class DrawSingleTileCallback;
-
-/**
- * An Image wrapper that clips an image against a rectangle. Right now only
- * absolute coordinates in pixels are supported.
- *
- * XXX(seth): There a known (performance, not correctness) issue with
- * GetImageContainer. See the comments for that method for more information.
- */
-class ClippedImage : public ImageWrapper
-{
-public:
-  NS_DECL_ISUPPORTS
-
-  virtual ~ClippedImage() { }
-
-  virtual nsIntRect FrameRect(uint32_t aWhichFrame) MOZ_OVERRIDE;
-
-  NS_IMETHOD GetWidth(int32_t* aWidth) MOZ_OVERRIDE;
-  NS_IMETHOD GetHeight(int32_t* aHeight) MOZ_OVERRIDE;
-  NS_IMETHOD GetIntrinsicSize(nsSize* aSize) MOZ_OVERRIDE;
-  NS_IMETHOD GetIntrinsicRatio(nsSize* aRatio) MOZ_OVERRIDE;
-  NS_IMETHOD GetFrame(uint32_t aWhichFrame,
-                      uint32_t aFlags,
-                      gfxASurface** _retval) MOZ_OVERRIDE;
-  NS_IMETHOD GetImageContainer(mozilla::layers::LayerManager* aManager,
-                               mozilla::layers::ImageContainer** _retval) MOZ_OVERRIDE;
-  NS_IMETHOD Draw(gfxContext* aContext,
-                  gfxPattern::GraphicsFilter aFilter,
-                  const gfxMatrix& aUserSpaceToImageSpace,
-                  const gfxRect& aFill,
-                  const nsIntRect& aSubimage,
-                  const nsIntSize& aViewportSize,
-                  const SVGImageContext* aSVGContext,
-                  uint32_t aWhichFrame,
-                  uint32_t aFlags) MOZ_OVERRIDE;
-
-protected:
-  ClippedImage(Image* aImage, nsIntRect aClip);
-
-private:
-  bool ShouldClip();
-  bool MustCreateSurface(gfxContext* aContext,
-                         const gfxMatrix& aTransform,
-                         const gfxRect& aSourceRect,
-                         const nsIntRect& aSubimage,
-                         const uint32_t aFlags) const;
-  gfxFloat ClampFactor(const gfxFloat aToClamp, const int aReference) const;
-  nsresult DrawSingleTile(gfxContext* aContext,
-                          gfxPattern::GraphicsFilter aFilter,
-                          const gfxMatrix& aUserSpaceToImageSpace,
-                          const gfxRect& aFill,
-                          const nsIntRect& aSubimage,
-                          const nsIntSize& aViewportSize,
-                          const SVGImageContext* aSVGContext,
-                          uint32_t aWhichFrame,
-                          uint32_t aFlags);
-
-  nsIntRect   mClip;              // The region to clip to.
-  Maybe<bool> mShouldClip;        // Memoized ShouldClip() if present.
-
-  friend class DrawSingleTileCallback;
-  friend class ImageOps;
-};
-
-} // namespace image
-} // namespace mozilla
-
-#endif // MOZILLA_IMAGELIB_CLIPPEDIMAGE_H_
--- a/image/src/FrozenImage.cpp
+++ b/image/src/FrozenImage.cpp
@@ -64,16 +64,25 @@ FrozenImage::GetImageContainer(layers::L
   // benefit from GetImageContainer, it would be a good idea to fix that method
   // for performance reasons.
 
   *_retval = nullptr;
   return NS_OK;
 }
 
 NS_IMETHODIMP
+FrozenImage::ExtractFrame(uint32_t aWhichFrame,
+                          const nsIntRect& aRegion,
+                          uint32_t aFlags,
+                          imgIContainer** _retval)
+{
+  return InnerImage()->ExtractFrame(FRAME_FIRST, aRegion, aFlags, _retval);
+}
+
+NS_IMETHODIMP
 FrozenImage::Draw(gfxContext* aContext,
                   gfxPattern::GraphicsFilter aFilter,
                   const gfxMatrix& aUserSpaceToImageSpace,
                   const gfxRect& aFill,
                   const nsIntRect& aSubimage,
                   const nsIntSize& aViewportSize,
                   const SVGImageContext* aSVGContext,
                   uint32_t /* aWhichFrame - ignored */,
--- a/image/src/FrozenImage.h
+++ b/image/src/FrozenImage.h
@@ -35,16 +35,20 @@ public:
 
   NS_IMETHOD GetAnimated(bool* aAnimated) MOZ_OVERRIDE;
   NS_IMETHOD GetFrame(uint32_t aWhichFrame,
                       uint32_t aFlags,
                       gfxASurface** _retval) MOZ_OVERRIDE;
   NS_IMETHOD_(bool) FrameIsOpaque(uint32_t aWhichFrame) MOZ_OVERRIDE;
   NS_IMETHOD GetImageContainer(layers::LayerManager* aManager,
                                layers::ImageContainer** _retval) MOZ_OVERRIDE;
+  NS_IMETHOD ExtractFrame(uint32_t aWhichFrame,
+                          const nsIntRect& aRegion,
+                          uint32_t aFlags,
+                          imgIContainer** _retval) MOZ_OVERRIDE;
   NS_IMETHOD Draw(gfxContext* aContext,
                   gfxPattern::GraphicsFilter aFilter,
                   const gfxMatrix& aUserSpaceToImageSpace,
                   const gfxRect& aFill,
                   const nsIntRect& aSubimage,
                   const nsIntSize& aViewportSize,
                   const SVGImageContext* aSVGContext,
                   uint32_t aWhichFrame,
@@ -53,15 +57,15 @@ public:
   NS_IMETHOD GetAnimationMode(uint16_t* aAnimationMode) MOZ_OVERRIDE;
   NS_IMETHOD SetAnimationMode(uint16_t aAnimationMode) MOZ_OVERRIDE;
   NS_IMETHOD ResetAnimation() MOZ_OVERRIDE;
 
 protected:
   FrozenImage(Image* aImage) : ImageWrapper(aImage) { }
 
 private:
-  friend class ImageOps;
+  friend class ImageFactory;
 };
 
 } // namespace image
 } // namespace mozilla
 
 #endif // MOZILLA_IMAGELIB_FROZENIMAGE_H_
--- a/image/src/ImageFactory.cpp
+++ b/image/src/ImageFactory.cpp
@@ -16,16 +16,17 @@
 #include "nsMimeTypes.h"
 #include "nsIURI.h"
 #include "nsIRequest.h"
 
 #include "imgIContainer.h"
 #include "imgStatusTracker.h"
 #include "RasterImage.h"
 #include "VectorImage.h"
+#include "FrozenImage.h"
 #include "Image.h"
 #include "nsMediaFragmentURIParser.h"
 
 #include "ImageFactory.h"
 
 namespace mozilla {
 namespace image {
 
@@ -170,16 +171,23 @@ GetContentSize(nsIRequest* aRequest)
     }
   }
 
   // Fallback - neither http nor file. We'll use dynamic allocation.
   return 0;
 }
 
 /* static */ already_AddRefed<Image>
+ImageFactory::Freeze(Image* aImage)
+{
+  nsRefPtr<Image> frozenImage = new FrozenImage(aImage);
+  return frozenImage.forget();
+}
+
+/* static */ already_AddRefed<Image>
 ImageFactory::CreateRasterImage(nsIRequest* aRequest,
                                 imgStatusTracker* aStatusTracker,
                                 const nsCString& aMimeType,
                                 nsIURI* aURI,
                                 uint32_t aImageFlags,
                                 uint32_t aInnerWindowId)
 {
   nsresult rv;
--- a/image/src/ImageFactory.h
+++ b/image/src/ImageFactory.h
@@ -41,16 +41,24 @@ public:
   /**
    * Creates a new image which isn't associated with a URI or loaded through
    * the usual image loading mechanism.
    *
    * @param aMimeType      The mimetype of the image.
    */
   static already_AddRefed<Image> CreateAnonymousImage(const nsCString& aMimeType);
 
+  /**
+   * Creates a version of an existing image which does not animate and is frozen
+   * at the first frame.
+   *
+   * @param aImage         The existing image.
+   */
+  static already_AddRefed<Image> Freeze(Image* aImage);
+
 private:
   // Factory functions that create specific types of image containers.
   static already_AddRefed<Image> CreateRasterImage(nsIRequest* aRequest,
                                                    imgStatusTracker* aStatusTracker,
                                                    const nsCString& aMimeType,
                                                    nsIURI* aURI,
                                                    uint32_t aImageFlags,
                                                    uint32_t aInnerWindowId);
deleted file mode 100644
--- a/image/src/ImageOps.cpp
+++ /dev/null
@@ -1,48 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "imgIContainer.h"
-#include "ClippedImage.h"
-#include "FrozenImage.h"
-#include "Image.h"
-
-#include "ImageOps.h"
-
-namespace mozilla {
-namespace image {
-
-/* static */ already_AddRefed<Image>
-ImageOps::Freeze(Image* aImage)
-{
-  nsRefPtr<Image> frozenImage = new FrozenImage(aImage);
-  return frozenImage.forget();
-}
-
-/* static */ already_AddRefed<imgIContainer>
-ImageOps::Freeze(imgIContainer* aImage)
-{
-  nsCOMPtr<imgIContainer> frozenImage =
-    new FrozenImage(static_cast<Image*>(aImage));
-  return frozenImage.forget();
-}
-
-/* static */ already_AddRefed<Image>
-ImageOps::Clip(Image* aImage, nsIntRect aClip)
-{
-  nsRefPtr<Image> clippedImage = new ClippedImage(aImage, aClip);
-  return clippedImage.forget();
-}
-
-/* static */ already_AddRefed<imgIContainer>
-ImageOps::Clip(imgIContainer* aImage, nsIntRect aClip)
-{
-  nsCOMPtr<imgIContainer> clippedImage =
-    new ClippedImage(static_cast<Image*>(aImage), aClip);
-  return clippedImage.forget();
-}
-
-} // namespace image
-} // namespace mozilla
deleted file mode 100644
--- a/image/src/ImageOps.h
+++ /dev/null
@@ -1,49 +0,0 @@
-/* -*- 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/. */
-
-#ifndef MOZILLA_IMAGELIB_IMAGEOPS_H_
-#define MOZILLA_IMAGELIB_IMAGEOPS_H_
-
-#include "nsCOMPtr.h"
-#include "nsRect.h"
-
-class imgIContainer;
-
-namespace mozilla {
-namespace image {
-
-class Image;
-
-class ImageOps
-{
-public:
-  /**
-   * Creates a version of an existing image which does not animate and is frozen
-   * at the first frame.
-   *
-   * @param aImage         The existing image.
-   */
-  static already_AddRefed<Image> Freeze(Image* aImage);
-  static already_AddRefed<imgIContainer> Freeze(imgIContainer* aImage);
-
-  /**
-   * Creates a clipped version of an existing image. Animation is unaffected.
-   *
-   * @param aImage         The existing image.
-   * @param aClip          The rectangle to clip the image against.
-   */
-  static already_AddRefed<Image> Clip(Image* aImage, nsIntRect aClip);
-  static already_AddRefed<imgIContainer> Clip(imgIContainer* aImage, nsIntRect aClip);
-
-private:
-  // This is a static utility class, so disallow instantiation.
-  virtual ~ImageOps() = 0;
-};
-
-} // namespace image
-} // namespace mozilla
-
-#endif // MOZILLA_IMAGELIB_IMAGEOPS_H_
--- a/image/src/ImageWrapper.cpp
+++ b/image/src/ImageWrapper.cpp
@@ -199,16 +199,25 @@ ImageWrapper::FrameIsOpaque(uint32_t aWh
 
 NS_IMETHODIMP
 ImageWrapper::GetImageContainer(LayerManager* aManager, ImageContainer** _retval)
 {
   return mInnerImage->GetImageContainer(aManager, _retval);
 }
 
 NS_IMETHODIMP
+ImageWrapper::ExtractFrame(uint32_t aWhichFrame,
+                           const nsIntRect& aRegion,
+                           uint32_t aFlags,
+                           imgIContainer** _retval)
+{
+  return mInnerImage->ExtractFrame(aWhichFrame, aRegion, aFlags, _retval);
+}
+
+NS_IMETHODIMP
 ImageWrapper::Draw(gfxContext* aContext,
                    gfxPattern::GraphicsFilter aFilter,
                    const gfxMatrix& aUserSpaceToImageSpace,
                    const gfxRect& aFill,
                    const nsIntRect& aSubimage,
                    const nsIntSize& aViewportSize,
                    const SVGImageContext* aSVGContext,
                    uint32_t aWhichFrame,
--- a/image/src/Makefile.in
+++ b/image/src/Makefile.in
@@ -15,26 +15,23 @@ FORCE_STATIC_LIB = 1
 MODULE_NAME	= nsImageLib2Module
 LIBXUL_LIBRARY  = 1
 FAIL_ON_WARNINGS = 1
 
 
 EXPORTS		=  imgLoader.h \
 		   imgRequest.h \
 		   imgRequestProxy.h \
-		   ImageOps.h \
 		   $(NULL)
 
 CPPSRCS		= \
 			Image.cpp \
 			ImageFactory.cpp \
                         ImageMetadata.cpp \
-			ImageOps.cpp \
 			ImageWrapper.cpp \
-			ClippedImage.cpp \
 			Decoder.cpp \
 			DiscardTracker.cpp \
 			FrozenImage.cpp \
 			RasterImage.cpp \
 			ScriptedNotificationObserver.cpp \
 			SVGDocumentWrapper.cpp \
 			VectorImage.cpp \
 			imgFrame.cpp \
--- a/image/src/RasterImage.cpp
+++ b/image/src/RasterImage.cpp
@@ -508,16 +508,24 @@ RasterImage::Init(const char* aMimeType,
   mMultipart = !!(aFlags & INIT_FLAG_MULTIPART);
 
   // Statistics
   if (mDiscardable) {
     num_discardable_containers++;
     discardable_source_bytes += mSourceData.Length();
   }
 
+  // If we're being called from ExtractFrame (used by borderimage),
+  // we don't actually do any decoding. Bail early.
+  // XXX - This should be removed when we fix borderimage
+  if (mSourceDataMimeType.Length() == 0) {
+    mInitialized = true;
+    return NS_OK;
+  }
+
   // Instantiate the decoder
   nsresult rv = InitDecoder(/* aDoSizeDecode = */ true);
   CONTAINER_ENSURE_SUCCESS(rv);
 
   // If we aren't storing source data, we want to switch from a size decode to
   // a full decode as soon as possible.
   if (!StoringSourceData()) {
     mWantFullDecode = true;
@@ -682,16 +690,95 @@ RasterImage::RequestRefresh(const mozill
     // Explicitly call this on mStatusTracker so we're sure to not interfere
     // with the decoding process
     if (mStatusTracker)
       mStatusTracker->FrameChanged(&dirtyRect);
   }
 }
 
 //******************************************************************************
+/* [noscript] imgIContainer extractFrame(uint32_t aWhichFrame,
+ *                                       [const] in nsIntRect aRegion,
+ *                                       in uint32_t aFlags); */
+NS_IMETHODIMP
+RasterImage::ExtractFrame(uint32_t aWhichFrame,
+                          const nsIntRect &aRegion,
+                          uint32_t aFlags,
+                          imgIContainer **_retval)
+{
+  NS_ENSURE_ARG_POINTER(_retval);
+
+  nsresult rv;
+
+  if (aWhichFrame > FRAME_MAX_VALUE)
+    return NS_ERROR_INVALID_ARG;
+
+  if (mError)
+    return NS_ERROR_FAILURE;
+
+  // Disallowed in the API
+  if (mInDecoder && (aFlags & imgIContainer::FLAG_SYNC_DECODE))
+    return NS_ERROR_FAILURE;
+
+  // Make a new container. This should switch to another class with bug 505959.
+  nsRefPtr<RasterImage> img(new RasterImage());
+
+  // We don't actually have a mimetype in this case. The empty string tells the
+  // init routine not to try to instantiate a decoder. This should be fixed in
+  // bug 505959.
+  img->Init("", INIT_FLAG_NONE);
+  img->SetSize(aRegion.width, aRegion.height);
+  img->mDecoded = true; // Also, we need to mark the image as decoded
+  img->mHasBeenDecoded = true;
+  img->mFrameDecodeFlags = aFlags & DECODE_FLAGS_MASK;
+
+  if (!ApplyDecodeFlags(aFlags))
+    return NS_ERROR_NOT_AVAILABLE;
+
+  // If a synchronous decode was requested, do it
+  if (aFlags & FLAG_SYNC_DECODE) {
+    rv = SyncDecode();
+    CONTAINER_ENSURE_SUCCESS(rv);
+  }
+
+  // Get the frame. If it's not there, it's probably the caller's fault for
+  // not waiting for the data to be loaded from the network or not passing
+  // FLAG_SYNC_DECODE
+  uint32_t frameIndex = (aWhichFrame == FRAME_FIRST) ?
+                        0 : GetCurrentImgFrameIndex();
+  imgFrame *frame = GetDrawableImgFrame(frameIndex);
+  if (!frame) {
+    *_retval = nullptr;
+    return NS_ERROR_FAILURE;
+  }
+
+  // The frame can be smaller than the image. We want to extract only the part
+  // of the frame that actually exists.
+  nsIntRect framerect = frame->GetRect();
+  framerect.IntersectRect(framerect, aRegion);
+
+  if (framerect.IsEmpty())
+    return NS_ERROR_NOT_AVAILABLE;
+
+  nsAutoPtr<imgFrame> subframe;
+  rv = frame->Extract(framerect, getter_Transfers(subframe));
+  if (NS_FAILED(rv))
+    return rv;
+
+  img->mFrames.AppendElement(subframe.forget());
+
+  img->mStatusTracker->RecordLoaded();
+  img->mStatusTracker->RecordDecoded();
+
+  *_retval = img.forget().get();
+
+  return NS_OK;
+}
+
+//******************************************************************************
 /* readonly attribute int32_t width; */
 NS_IMETHODIMP
 RasterImage::GetWidth(int32_t *aWidth)
 {
   NS_ENSURE_ARG_POINTER(aWidth);
 
   if (mError) {
     *aWidth = 0;
@@ -2682,20 +2769,21 @@ RasterImage::WriteToDecoder(const char *
   // Keep track of the total number of bytes written over the lifetime of the
   // decoder
   mBytesDecoded += aCount;
 
   return NS_OK;
 }
 
 // This function is called in situations where it's clear that we want the
-// frames in decoded form (Draw, GetFrame, etc).  If we're completely decoded,
-// this method resets the discard timer (if we're discardable), since wanting
-// the frames now is a good indicator of wanting them again soon. If we're not
-// decoded, this method kicks off asynchronous decoding to generate the frames.
+// frames in decoded form (Draw, GetFrame, CopyFrame, ExtractFrame, etc).
+// If we're completely decoded, this method resets the discard timer (if
+// we're discardable), since wanting the frames now is a good indicator of
+// wanting them again soon. If we're not decoded, this method kicks off
+// asynchronous decoding to generate the frames.
 nsresult
 RasterImage::WantDecodedFrames()
 {
   nsresult rv;
 
   // If we can discard, the clock should be running. Reset it.
   if (CanDiscard()) {
     NS_ABORT_IF_FALSE(DiscardingActive(),
--- a/image/src/VectorImage.cpp
+++ b/image/src/VectorImage.cpp
@@ -296,20 +296,22 @@ NS_IMPL_ISUPPORTS3(VectorImage,
                    nsIRequestObserver)
 
 //------------------------------------------------------------------------------
 // Constructor / Destructor
 
 VectorImage::VectorImage(imgStatusTracker* aStatusTracker,
                          nsIURI* aURI /* = nullptr */) :
   ImageResource(aStatusTracker, aURI), // invoke superclass's constructor
+  mRestrictedRegion(0, 0, 0, 0),
   mIsInitialized(false),
   mIsFullyLoaded(false),
   mIsDrawing(false),
-  mHaveAnimations(false)
+  mHaveAnimations(false),
+  mHaveRestrictedRegion(false)
 {
 }
 
 VectorImage::~VectorImage()
 {
   CancelAllListeners();
 }
 
@@ -319,17 +321,18 @@ VectorImage::~VectorImage()
 nsresult
 VectorImage::Init(const char* aMimeType,
                   uint32_t aFlags)
 {
   // We don't support re-initialization
   if (mIsInitialized)
     return NS_ERROR_ILLEGAL_VALUE;
 
-  MOZ_ASSERT(!mIsFullyLoaded && !mHaveAnimations && !mError,
+  MOZ_ASSERT(!mIsFullyLoaded && !mHaveAnimations &&
+             !mHaveRestrictedRegion && !mError,
              "Flags unexpectedly set before initialization");
   MOZ_ASSERT(!strcmp(aMimeType, IMAGE_SVG_XML), "Unexpected mimetype");
 
   mIsInitialized = true;
   return NS_OK;
 }
 
 nsIntRect
@@ -587,17 +590,24 @@ VectorImage::GetFrame(uint32_t aWhichFra
     // We'll get here if our SVG doc has a percent-valued width or height.
     return NS_ERROR_FAILURE;
   }
 
   // Create a surface that we'll ultimately return
   // ---------------------------------------------
   // Make our surface the size of what will ultimately be drawn to it.
   // (either the full image size, or the restricted region)
-  gfxIntSize surfaceSize(imageIntSize.width, imageIntSize.height);
+  gfxIntSize surfaceSize;
+  if (mHaveRestrictedRegion) {
+    surfaceSize.width = mRestrictedRegion.width;
+    surfaceSize.height = mRestrictedRegion.height;
+  } else {
+    surfaceSize.width = imageIntSize.width;
+    surfaceSize.height = imageIntSize.height;
+  }
 
   nsRefPtr<gfxImageSurface> surface =
     new gfxImageSurface(surfaceSize, gfxASurface::ImageFormatARGB32);
   nsRefPtr<gfxContext> context = new gfxContext(surface);
 
   // Draw to our surface!
   // --------------------
   nsresult rv = Draw(context, gfxPattern::FILTER_NEAREST, gfxMatrix(),
@@ -617,16 +627,64 @@ NS_IMETHODIMP
 VectorImage::GetImageContainer(LayerManager* aManager,
                                mozilla::layers::ImageContainer** _retval)
 {
   *_retval = nullptr;
   return NS_OK;
 }
 
 //******************************************************************************
+/* [noscript] imgIContainer extractFrame(uint32_t aWhichFrame,
+ *                                       [const] in nsIntRect aRegion,
+ *                                       in uint32_t aFlags); */
+NS_IMETHODIMP
+VectorImage::ExtractFrame(uint32_t aWhichFrame,
+                          const nsIntRect& aRegion,
+                          uint32_t aFlags,
+                          imgIContainer** _retval)
+{
+  NS_ENSURE_ARG_POINTER(_retval);
+  if (mError || !mIsFullyLoaded)
+    return NS_ERROR_FAILURE;
+
+  // XXXdholbert NOTE: This method assumes FRAME_CURRENT (not FRAME_FIRST)
+  // right now, because mozilla doesn't actually contain any clients of this
+  // method that use FRAME_FIRST.  If it's needed, we *could* handle
+  // FRAME_FIRST by saving the helper-doc's current SMIL time, seeking it to
+  // time 0, rendering to a RasterImage, and then restoring our saved time.
+  if (aWhichFrame != FRAME_CURRENT) {
+    NS_WARNING("VectorImage::ExtractFrame with something other than "
+               "FRAME_CURRENT isn't supported yet. Assuming FRAME_CURRENT.");
+  }
+
+  // XXXdholbert This method also doesn't actually freeze animation in the
+  // returned imgIContainer, because it shares our helper-document. To
+  // get a true snapshot, we need to clone the document - see bug 590792.
+
+  // Make a new container with same SVG document.
+  nsRefPtr<VectorImage> extractedImg = new VectorImage();
+  extractedImg->mSVGDocumentWrapper = mSVGDocumentWrapper;
+  extractedImg->mAnimationMode = kDontAnimMode;
+
+  extractedImg->mRestrictedRegion.x = aRegion.x;
+  extractedImg->mRestrictedRegion.y = aRegion.y;
+
+  // (disallow negative width/height on our restricted region)
+  extractedImg->mRestrictedRegion.width  = std::max(aRegion.width,  0);
+  extractedImg->mRestrictedRegion.height = std::max(aRegion.height, 0);
+
+  extractedImg->mIsInitialized = true;
+  extractedImg->mIsFullyLoaded = true;
+  extractedImg->mHaveRestrictedRegion = true;
+
+  *_retval = extractedImg.forget().get();
+  return NS_OK;
+}
+
+//******************************************************************************
 /* [noscript] void draw(in gfxContext aContext,
  *                      in gfxGraphicsFilter aFilter,
  *                      [const] in gfxMatrix aUserSpaceToImageSpace,
  *                      [const] in gfxRect aFill,
  *                      [const] in nsIntRect aSubimage,
  *                      [const] in nsIntSize aViewportSize,
  *                      [const] in SVGImageContext aSVGContext,
  *                      in uint32_t aWhichFrame,
@@ -659,42 +717,50 @@ VectorImage::Draw(gfxContext* aContext,
   float time = aWhichFrame == FRAME_FIRST ? 0.0f
                                           : mSVGDocumentWrapper->GetCurrentTime();
   AutoSVGRenderingState autoSVGState(aSVGContext,
                                      time,
                                      mSVGDocumentWrapper->GetRootSVGElem());
   mSVGDocumentWrapper->UpdateViewportBounds(aViewportSize);
   mSVGDocumentWrapper->FlushImageTransformInvalidation();
 
+  nsIntSize imageSize = mHaveRestrictedRegion ?
+    mRestrictedRegion.Size() : aViewportSize;
+
   // XXXdholbert Do we need to convert image size from
   // CSS pixels to dev pixels here? (is gfxCallbackDrawable's 2nd arg in dev
   // pixels?)
-  gfxIntSize imageSizeGfx(aViewportSize.width, aViewportSize.height);
+  gfxIntSize imageSizeGfx(imageSize.width, imageSize.height);
 
   // Based on imgFrame::Draw
   gfxRect sourceRect = aUserSpaceToImageSpace.Transform(aFill);
-  gfxRect imageRect(0, 0, aViewportSize.width, aViewportSize.height);
+  gfxRect imageRect(0, 0, imageSize.width, imageSize.height);
   gfxRect subimage(aSubimage.x, aSubimage.y, aSubimage.width, aSubimage.height);
 
 
   nsRefPtr<gfxDrawingCallback> cb =
     new SVGDrawingCallback(mSVGDocumentWrapper,
+                           mHaveRestrictedRegion ?
+                           mRestrictedRegion :
                            nsIntRect(nsIntPoint(0, 0), aViewportSize),
                            aFlags);
 
   nsRefPtr<gfxDrawable> drawable = new gfxCallbackDrawable(cb, imageSizeGfx);
 
   gfxUtils::DrawPixelSnapped(aContext, drawable,
                              aUserSpaceToImageSpace,
                              subimage, sourceRect, imageRect, aFill,
                              gfxASurface::ImageFormatARGB32, aFilter);
 
-  // Allow ourselves to fire FrameChanged and OnStopFrame again.
-  MOZ_ASSERT(mRenderingObserver, "Should have a rendering observer by now");
-  mRenderingObserver->ResumeListening();
+  MOZ_ASSERT(mRenderingObserver || mHaveRestrictedRegion,
+      "Should have a rendering observer by now unless ExtractFrame created us");
+  if (mRenderingObserver) {
+    // Allow ourselves to fire FrameChanged and OnStopFrame again.
+    mRenderingObserver->ResumeListening();
+  }
 
   return NS_OK;
 }
 
 //******************************************************************************
 /* void requestDecode() */
 NS_IMETHODIMP
 VectorImage::RequestDecode()
--- a/image/src/VectorImage.h
+++ b/image/src/VectorImage.h
@@ -79,21 +79,28 @@ protected:
 private:
   void CancelAllListeners();
 
   nsRefPtr<SVGDocumentWrapper>       mSVGDocumentWrapper;
   nsRefPtr<SVGRootRenderingObserver> mRenderingObserver;
   nsRefPtr<SVGLoadEventListener>     mLoadEventListener;
   nsRefPtr<SVGParseCompleteListener> mParseCompleteListener;
 
+  nsIntRect      mRestrictedRegion;       // If we were created by
+                                          // ExtractFrame, this is the region
+                                          // that we're restricted to using.
+                                          // Otherwise, this is ignored.
+
   bool           mIsInitialized;          // Have we been initalized?
   bool           mIsFullyLoaded;          // Has the SVG document finished loading?
   bool           mIsDrawing;              // Are we currently drawing?
   bool           mHaveAnimations;         // Is our SVG content SMIL-animated?
                                           // (Only set after mIsFullyLoaded.)
+  bool           mHaveRestrictedRegion;   // Are we a restricted-region clone
+                                          // created via ExtractFrame?
 
   friend class ImageFactory;
 };
 
 inline NS_IMETHODIMP VectorImage::GetAnimationMode(uint16_t *aAnimationMode) {
   return GetAnimationModeInternal(aAnimationMode);
 }
 
--- a/image/src/imgRequestProxy.cpp
+++ b/image/src/imgRequestProxy.cpp
@@ -13,17 +13,17 @@
 #include "nsIMultiPartChannel.h"
 
 #include "nsString.h"
 #include "nsXPIDLString.h"
 #include "nsReadableUtils.h"
 #include "nsCRT.h"
 
 #include "Image.h"
-#include "ImageOps.h"
+#include "ImageFactory.h"
 #include "nsError.h"
 #include "ImageLogging.h"
 
 #include "nspr.h"
 
 using namespace mozilla::image;
 
 // The split of imgRequestProxy and imgRequestProxyStatic means that
@@ -910,17 +910,17 @@ imgRequestProxy::GetStaticRequest(imgReq
   // Check for errors in the image. Callers code rely on GetStaticRequest
   // failing in this case, though with FrozenImage there's no technical reason
   // for it anymore.
   if (image->HasError()) {
     return NS_ERROR_FAILURE;
   }
 
   // We are animated. We need to create a frozen version of this image.
-  nsRefPtr<Image> frozenImage = ImageOps::Freeze(image);
+  nsRefPtr<Image> frozenImage = ImageFactory::Freeze(image);
 
   // Create a static imgRequestProxy with our new extracted frame.
   nsCOMPtr<nsIPrincipal> currentPrincipal;
   GetImagePrincipal(getter_AddRefs(currentPrincipal));
   nsRefPtr<imgRequestProxy> req = new imgRequestProxyStatic(frozenImage,
                                                             currentPrincipal);
   req->Init(nullptr, nullptr, mURI, nullptr);
 
--- a/layout/base/nsCSSRendering.cpp
+++ b/layout/base/nsCSSRendering.cpp
@@ -25,17 +25,16 @@
 #include "nsGkAtoms.h"
 #include "nsCSSAnonBoxes.h"
 #include "nsTransform2D.h"
 #include "nsIContent.h"
 #include "nsIDocumentInlines.h"
 #include "nsIScrollableFrame.h"
 #include "imgIRequest.h"
 #include "imgIContainer.h"
-#include "ImageOps.h"
 #include "nsCSSRendering.h"
 #include "nsCSSColorUtils.h"
 #include "nsITheme.h"
 #include "nsThemeConstants.h"
 #include "nsIServiceManager.h"
 #include "nsLayoutUtils.h"
 #include "nsINameSpaceManager.h"
 #include "nsBlockFrame.h"
@@ -56,17 +55,16 @@
 #include "mozilla/css/ImageLoader.h"
 #include "ImageContainer.h"
 #include "mozilla/Telemetry.h"
 #include "gfxUtils.h"
 #include <algorithm>
 
 using namespace mozilla;
 using namespace mozilla::css;
-using mozilla::image::ImageOps;
 
 static int gFrameTreeLockCount = 0;
 
 // To avoid storing this data on nsInlineFrame (bloat) and to avoid
 // recalculating this for each frame in a continuation (perf), hold
 // a cache of various coordinate information that we need in order
 // to paint inline backgrounds.
 struct InlineBackgroundData
@@ -3311,20 +3309,30 @@ DrawBorderImageComponent(nsRenderingCont
                          uint8_t              aVFill,
                          const nsSize&        aUnitSize,
                          const nsStyleBorder& aStyleBorder,
                          uint8_t              aIndex)
 {
   if (aFill.IsEmpty() || aSrc.IsEmpty())
     return;
 
+  // Don't bother trying to cache sub images if the border image is animated
+  // We can only sucessfully call GetAnimated() if we are fully decoded, so default to true
+  bool animated = true;
+  aImage->GetAnimated(&animated);
+
   nsCOMPtr<imgIContainer> subImage;
-  if ((subImage = aStyleBorder.GetSubImage(aIndex)) == nullptr) {
-    subImage = ImageOps::Clip(aImage, aSrc);
-    aStyleBorder.SetSubImage(aIndex, subImage);
+  if (animated || (subImage = aStyleBorder.GetSubImage(aIndex)) == 0) {
+    if (NS_FAILED(aImage->ExtractFrame(imgIContainer::FRAME_CURRENT, aSrc,
+                                       imgIContainer::FLAG_SYNC_DECODE,
+                                       getter_AddRefs(subImage))))
+      return;
+
+    if (!animated)
+      aStyleBorder.SetSubImage(aIndex, subImage);
   }
 
   gfxPattern::GraphicsFilter graphicsFilter =
     nsLayoutUtils::GetGraphicsFilterForFrame(aForFrame);
 
   // If we have no tiling in either direction, we can skip the intermediate
   // scaling step.
   if ((aHFill == NS_STYLE_BORDER_IMAGE_REPEAT_STRETCH &&
@@ -4280,17 +4288,28 @@ nsImageRenderer::PrepareImage()
         if (!success || actualCropRect.IsEmpty()) {
           // The cropped image has zero size
           return false;
         }
         if (isEntireImage) {
           // The cropped image is identical to the source image
           mImageContainer.swap(srcImage);
         } else {
-          nsCOMPtr<imgIContainer> subImage = ImageOps::Clip(srcImage, actualCropRect);
+          nsCOMPtr<imgIContainer> subImage;
+          uint32_t aExtractFlags = (mFlags & FLAG_SYNC_DECODE_IMAGES)
+                                     ? (uint32_t) imgIContainer::FLAG_SYNC_DECODE
+                                     : (uint32_t) imgIContainer::FLAG_NONE;
+          nsresult rv = srcImage->ExtractFrame(imgIContainer::FRAME_CURRENT,
+                                               actualCropRect, aExtractFlags,
+                                               getter_AddRefs(subImage));
+          if (NS_FAILED(rv)) {
+            NS_WARNING("The cropped image contains no pixels to draw; "
+                       "maybe the crop rect is outside the image frame rect");
+            return false;
+          }
           mImageContainer.swap(subImage);
         }
       }
       mIsReady = true;
       break;
     }
     case eStyleImageType_Gradient:
       mGradientData = mImage->GetGradientData();