Bug 1054076 - Make imgFrame reference counted. r=tn
authorSeth Fowler <seth@mozilla.com>
Fri, 22 Aug 2014 13:49:54 -0700
changeset 224167 99fd6c8e7d368635095859d568986c281f7b0415
parent 224166 d7e28d7d750565556009cc4129a5849eeaaabfcb
child 224168 d06c2e71aca26515557b4a77e0df73c910737644
push id583
push userbhearsum@mozilla.com
push dateMon, 24 Nov 2014 19:04:58 +0000
treeherdermozilla-release@c107e74250f4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstn
bugs1054076
milestone34.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 1054076 - Make imgFrame reference counted. r=tn
image/decoders/nsGIFDecoder2.cpp
image/decoders/nsPNGDecoder.cpp
image/src/Decoder.cpp
image/src/Decoder.h
image/src/FrameAnimator.cpp
image/src/FrameBlender.cpp
image/src/FrameBlender.h
image/src/FrameSequence.cpp
image/src/FrameSequence.h
image/src/RasterImage.cpp
image/src/RasterImage.h
image/src/imgFrame.cpp
image/src/imgFrame.h
--- a/image/decoders/nsGIFDecoder2.cpp
+++ b/image/decoders/nsGIFDecoder2.cpp
@@ -169,32 +169,34 @@ void nsGIFDecoder2::BeginImageFrame(uint
 
   // Use correct format, RGB for first frame, PAL for following frames
   // and include transparency to allow for optimization of opaque images
   if (mGIFStruct.images_decoded) {
     // Image data is stored with original depth and palette
     NeedNewFrame(mGIFStruct.images_decoded, mGIFStruct.x_offset,
                  mGIFStruct.y_offset, mGIFStruct.width, mGIFStruct.height,
                  format, aDepth);
-  }
+  } else {
+    nsRefPtr<imgFrame> currentFrame = GetCurrentFrame();
 
-  // Our first full frame is automatically created by the image decoding
-  // infrastructure. Just use it as long as it matches up.
-  else if (!GetCurrentFrame()->GetRect().IsEqualEdges(nsIntRect(mGIFStruct.x_offset,
-                                                                mGIFStruct.y_offset,
-                                                                mGIFStruct.width,
-                                                                mGIFStruct.height))) {
-    // Regardless of depth of input, image is decoded into 24bit RGB
-    NeedNewFrame(mGIFStruct.images_decoded, mGIFStruct.x_offset,
-                 mGIFStruct.y_offset, mGIFStruct.width, mGIFStruct.height,
-                 format);
-  } else {
-    // Our preallocated frame matches up, with the possible exception of alpha.
-    if (format == gfx::SurfaceFormat::B8G8R8X8) {
-      GetCurrentFrame()->SetHasNoAlpha();
+    // Our first full frame is automatically created by the image decoding
+    // infrastructure. Just use it as long as it matches up.
+    if (!currentFrame->GetRect().IsEqualEdges(nsIntRect(mGIFStruct.x_offset,
+                                                        mGIFStruct.y_offset,
+                                                        mGIFStruct.width,
+                                                        mGIFStruct.height))) {
+      // Regardless of depth of input, image is decoded into 24bit RGB
+      NeedNewFrame(mGIFStruct.images_decoded, mGIFStruct.x_offset,
+                   mGIFStruct.y_offset, mGIFStruct.width, mGIFStruct.height,
+                   format);
+    } else {
+      // Our preallocated frame matches up, with the possible exception of alpha.
+      if (format == gfx::SurfaceFormat::B8G8R8X8) {
+        currentFrame->SetHasNoAlpha();
+      }
     }
   }
 
   mCurrentFrameIndex = mGIFStruct.images_decoded;
 }
 
 
 //******************************************************************************
--- a/image/decoders/nsPNGDecoder.cpp
+++ b/image/decoders/nsPNGDecoder.cpp
@@ -140,39 +140,36 @@ nsPNGDecoder::~nsPNGDecoder()
 // CreateFrame() is used for both simple and animated images
 void nsPNGDecoder::CreateFrame(png_uint_32 x_offset, png_uint_32 y_offset,
                                int32_t width, int32_t height,
                                gfx::SurfaceFormat format)
 {
   // Our first full frame is automatically created by the image decoding
   // infrastructure. Just use it as long as it matches up.
   MOZ_ASSERT(HasSize());
-  if (mNumFrames != 0 ||
-      !GetCurrentFrame()->GetRect().IsEqualEdges(nsIntRect(x_offset, y_offset, width, height))) {
+  nsIntRect neededRect(x_offset, y_offset, width, height);
+  nsRefPtr<imgFrame> currentFrame = GetCurrentFrame();
+  if (mNumFrames != 0 || !currentFrame->GetRect().IsEqualEdges(neededRect)) {
     NeedNewFrame(mNumFrames, x_offset, y_offset, width, height, format);
   } else if (mNumFrames == 0) {
     // Our preallocated frame matches up, with the possible exception of alpha.
     if (format == gfx::SurfaceFormat::B8G8R8X8) {
-      GetCurrentFrame()->SetHasNoAlpha();
+      currentFrame->SetHasNoAlpha();
     }
   }
 
-  mFrameRect.x = x_offset;
-  mFrameRect.y = y_offset;
-  mFrameRect.width = width;
-  mFrameRect.height = height;
+  mFrameRect = neededRect;
+  mFrameHasNoAlpha = true;
 
   PR_LOG(GetPNGDecoderAccountingLog(), PR_LOG_DEBUG,
          ("PNGDecoderAccounting: nsPNGDecoder::CreateFrame -- created "
           "image frame with %dx%d pixels in container %p",
           width, height,
           &mImage));
 
-  mFrameHasNoAlpha = true;
-
 #ifdef PNG_APNG_SUPPORTED
   if (png_get_valid(mPNG, mInfo, PNG_INFO_acTL)) {
     mAnimInfo = AnimFrameInfo(mPNG, mInfo);
   }
 #endif
 }
 
 // set timeout and frame disposal method for the current frame
--- a/image/src/Decoder.cpp
+++ b/image/src/Decoder.cpp
@@ -199,29 +199,31 @@ Decoder::FinishSharedDecoder()
 
 nsresult
 Decoder::AllocateFrame()
 {
   MOZ_ASSERT(mNeedsNewFrame);
   MOZ_ASSERT(NS_IsMainThread());
 
   nsresult rv;
-  imgFrame* frame = nullptr;
+  nsRefPtr<imgFrame> frame;
   if (mNewFrameData.mPaletteDepth) {
     rv = mImage.EnsureFrame(mNewFrameData.mFrameNum, mNewFrameData.mOffsetX,
                             mNewFrameData.mOffsetY, mNewFrameData.mWidth,
                             mNewFrameData.mHeight, mNewFrameData.mFormat,
                             mNewFrameData.mPaletteDepth,
                             &mImageData, &mImageDataLength,
-                            &mColormap, &mColormapSize, &frame);
+                            &mColormap, &mColormapSize,
+                            getter_AddRefs(frame));
   } else {
     rv = mImage.EnsureFrame(mNewFrameData.mFrameNum, mNewFrameData.mOffsetX,
                             mNewFrameData.mOffsetY, mNewFrameData.mWidth,
                             mNewFrameData.mHeight, mNewFrameData.mFormat,
-                            &mImageData, &mImageDataLength, &frame);
+                            &mImageData, &mImageDataLength,
+                            getter_AddRefs(frame));
   }
 
   if (NS_SUCCEEDED(rv)) {
     mCurrentFrame = frame;
   } else {
     mCurrentFrame = nullptr;
   }
 
--- a/image/src/Decoder.h
+++ b/image/src/Decoder.h
@@ -166,17 +166,21 @@ public:
                     uint8_t palette_depth = 0);
 
   virtual bool NeedsNewFrame() const { return mNeedsNewFrame; }
 
   // Try to allocate a frame as described in mNewFrameData and return the
   // status code from that attempt. Clears mNewFrameData.
   virtual nsresult AllocateFrame();
 
-  imgFrame* GetCurrentFrame() const { return mCurrentFrame; }
+  already_AddRefed<imgFrame> GetCurrentFrame() const
+  {
+    nsRefPtr<imgFrame> frame = mCurrentFrame;
+    return frame.forget();
+  }
 
 protected:
 
   /*
    * Internal hooks. Decoder implementations may override these and
    * only these methods.
    */
   virtual void InitInternal();
@@ -225,17 +229,17 @@ protected:
   void PostDataError();
   void PostDecoderError(nsresult aFailCode);
 
   /*
    * Member variables.
    *
    */
   RasterImage &mImage;
-  imgFrame* mCurrentFrame;
+  nsRefPtr<imgFrame> mCurrentFrame;
   RefPtr<imgDecoderObserver> mObserver;
   ImageMetadata mImageMetadata;
 
   uint8_t* mImageData;       // Pointer to image data in either Cairo or 8bit format
   uint32_t mImageDataLength;
   uint32_t* mColormap;       // Current colormap to be used in Cairo format
   uint32_t mColormapSize;
 
--- a/image/src/FrameAnimator.cpp
+++ b/image/src/FrameAnimator.cpp
@@ -80,23 +80,22 @@ FrameAnimator::AdvanceFrame(TimeStamp aT
   NS_ASSERTION(aTime <= TimeStamp::Now(),
                "Given time appears to be in the future");
 
   uint32_t currentFrameIndex = mCurrentAnimationFrameIndex;
   uint32_t nextFrameIndex = currentFrameIndex + 1;
   int32_t timeout = 0;
 
   RefreshResult ret;
+  nsRefPtr<imgFrame> nextFrame = mFrameBlender.RawGetFrame(nextFrameIndex);
 
   // If we're done decoding, we know we've got everything we're going to get.
   // If we aren't, we only display fully-downloaded frames; everything else
   // gets delayed.
-  bool canDisplay = mDoneDecoding ||
-                    (mFrameBlender.RawGetFrame(nextFrameIndex) &&
-                     mFrameBlender.RawGetFrame(nextFrameIndex)->ImageComplete());
+  bool canDisplay = mDoneDecoding || (nextFrame && nextFrame->ImageComplete());
 
   if (!canDisplay) {
     // Uh oh, the frame we want to show is currently being decoded (partial)
     // Wait until the next refresh driver tick and try again
     return ret;
   }
 
   // If we're done decoding the next frame, go ahead and display it now and
@@ -133,28 +132,32 @@ FrameAnimator::AdvanceFrame(TimeStamp aT
     ret.animationFinished = true;
     ret.error = true;
   }
 
   if (nextFrameIndex == 0) {
     ret.dirtyRect = mFirstFrameRefreshArea;
   } else {
     // Change frame
+    if (nextFrameIndex != currentFrameIndex + 1) {
+      nextFrame = mFrameBlender.RawGetFrame(nextFrameIndex);
+    }
+
     if (!mFrameBlender.DoBlend(&ret.dirtyRect, currentFrameIndex, nextFrameIndex)) {
       // something went wrong, move on to next
       NS_WARNING("FrameAnimator::AdvanceFrame(): Compositing of frame failed");
-      mFrameBlender.RawGetFrame(nextFrameIndex)->SetCompositingFailed(true);
+      nextFrame->SetCompositingFailed(true);
       mCurrentAnimationFrameTime = GetCurrentImgFrameEndTime();
       mCurrentAnimationFrameIndex = nextFrameIndex;
 
       ret.error = true;
       return ret;
     }
 
-    mFrameBlender.RawGetFrame(nextFrameIndex)->SetCompositingFailed(false);
+    nextFrame->SetCompositingFailed(false);
   }
 
   mCurrentAnimationFrameTime = GetCurrentImgFrameEndTime();
 
   // If we can get closer to the current time by a multiple of the image's loop
   // time, we should.
   uint32_t loopTime = GetSingleLoopTime();
   if (loopTime > 0) {
--- a/image/src/FrameBlender.cpp
+++ b/image/src/FrameBlender.cpp
@@ -30,49 +30,50 @@ FrameBlender::~FrameBlender()
 
 already_AddRefed<FrameSequence>
 FrameBlender::GetFrameSequence()
 {
   nsRefPtr<FrameSequence> seq(mFrames);
   return seq.forget();
 }
 
-imgFrame*
+already_AddRefed<imgFrame>
 FrameBlender::GetFrame(uint32_t framenum) const
 {
   if (!mAnim) {
     NS_ASSERTION(framenum == 0, "Don't ask for a frame > 0 if we're not animated!");
-    return mFrames->GetFrame(0);
+    return mFrames->GetFrame(0).GetFrame();
   }
-  if (mAnim->lastCompositedFrameIndex == int32_t(framenum))
-    return mAnim->compositingFrame;
-  return mFrames->GetFrame(framenum);
+  if (mAnim->lastCompositedFrameIndex == int32_t(framenum)) {
+    return mAnim->compositingFrame.GetFrame();
+  }
+  return mFrames->GetFrame(framenum).GetFrame();
 }
 
-imgFrame*
+already_AddRefed<imgFrame>
 FrameBlender::RawGetFrame(uint32_t framenum) const
 {
   if (!mAnim) {
     NS_ASSERTION(framenum == 0, "Don't ask for a frame > 0 if we're not animated!");
-    return mFrames->GetFrame(0);
+    return mFrames->GetFrame(0).GetFrame();
   }
-
-  return mFrames->GetFrame(framenum);
+  return mFrames->GetFrame(framenum).GetFrame();
 }
 
 uint32_t
 FrameBlender::GetNumFrames() const
 {
   return mFrames->GetNumFrames();
 }
 
 int32_t
 FrameBlender::GetTimeoutForFrame(uint32_t framenum) const
 {
-  const int32_t timeout = RawGetFrame(framenum)->GetRawTimeout();
+  nsRefPtr<imgFrame> frame = RawGetFrame(framenum);
+  const int32_t timeout = frame->GetRawTimeout();
   // Ensure a minimal time between updates so we don't throttle the UI thread.
   // consider 0 == unspecified and make it fast but not too fast.  Unless we have
   // a single loop GIF. See bug 890743, bug 125137, bug 139677, and bug 207059.
   // The behavior of recent IE and Opera versions seems to be:
   // IE 6/Win:
   //   10 - 50ms go 100ms
   //   >50ms go correct speed
   // Opera 7 final/Win:
@@ -118,33 +119,33 @@ FrameBlender::InsertFrame(uint32_t frame
 {
   NS_ABORT_IF_FALSE(framenum <= GetNumFrames(), "Inserting invalid frame!");
   mFrames->InsertFrame(framenum, aFrame);
   if (GetNumFrames() > 1) {
     EnsureAnimExists();
   }
 }
 
-imgFrame*
+already_AddRefed<imgFrame>
 FrameBlender::SwapFrame(uint32_t framenum, imgFrame* aFrame)
 {
   NS_ABORT_IF_FALSE(framenum < GetNumFrames(), "Swapping invalid frame!");
 
-  imgFrame* ret;
+  nsRefPtr<imgFrame> ret;
 
   // Steal the imgFrame from wherever it's currently stored
   if (mAnim && mAnim->lastCompositedFrameIndex == int32_t(framenum)) {
     ret = mAnim->compositingFrame.Forget();
     mAnim->lastCompositedFrameIndex = -1;
-    nsAutoPtr<imgFrame> toDelete(mFrames->SwapFrame(framenum, aFrame));
+    nsRefPtr<imgFrame> toDelete(mFrames->SwapFrame(framenum, aFrame));
   } else {
     ret = mFrames->SwapFrame(framenum, aFrame);
   }
 
-  return ret;
+  return ret.forget();
 }
 
 void
 FrameBlender::EnsureAnimExists()
 {
   if (!mAnim) {
     // Create the animation context
     mAnim = new Anim();
@@ -302,84 +303,84 @@ FrameBlender::DoBlend(nsIntRect* aDirtyR
   if (doDisposal) {
     // Dispose of previous: clear, restore, or keep (copy)
     switch (prevFrameDisposalMethod) {
       case FrameBlender::kDisposeClear:
         if (needToBlankComposite) {
           // If we just created the composite, it could have anything in its
           // buffer. Clear whole frame
           ClearFrame(mAnim->compositingFrame.GetFrameData(),
-                     mAnim->compositingFrame.GetFrame()->GetRect());
+                     mAnim->compositingFrame->GetRect());
         } else {
           // Only blank out previous frame area (both color & Mask/Alpha)
           ClearFrame(mAnim->compositingFrame.GetFrameData(),
-                     mAnim->compositingFrame.GetFrame()->GetRect(),
+                     mAnim->compositingFrame->GetRect(),
                      prevFrameRect);
         }
         break;
 
       case FrameBlender::kDisposeClearAll:
         ClearFrame(mAnim->compositingFrame.GetFrameData(),
-                   mAnim->compositingFrame.GetFrame()->GetRect());
+                   mAnim->compositingFrame->GetRect());
         break;
 
       case FrameBlender::kDisposeRestorePrevious:
         // It would be better to copy only the area changed back to
         // compositingFrame.
         if (mAnim->compositingPrevFrame) {
           CopyFrameImage(mAnim->compositingPrevFrame.GetFrameData(),
-                         mAnim->compositingPrevFrame.GetFrame()->GetRect(),
+                         mAnim->compositingPrevFrame->GetRect(),
                          mAnim->compositingFrame.GetFrameData(),
-                         mAnim->compositingFrame.GetFrame()->GetRect());
+                         mAnim->compositingFrame->GetRect());
 
           // destroy only if we don't need it for this frame's disposal
           if (nextFrameDisposalMethod != FrameBlender::kDisposeRestorePrevious)
             mAnim->compositingPrevFrame.SetFrame(nullptr);
         } else {
           ClearFrame(mAnim->compositingFrame.GetFrameData(),
-                     mAnim->compositingFrame.GetFrame()->GetRect());
+                     mAnim->compositingFrame->GetRect());
         }
         break;
 
       default:
         // Copy previous frame into compositingFrame before we put the new frame on top
         // Assumes that the previous frame represents a full frame (it could be
         // smaller in size than the container, as long as the frame before it erased
         // itself)
         // Note: Frame 1 never gets into DoBlend(), so (aNextFrameIndex - 1) will
         // always be a valid frame number.
         if (mAnim->lastCompositedFrameIndex != int32_t(aNextFrameIndex - 1)) {
           if (isFullPrevFrame && !prevFrame->GetIsPaletted()) {
             // Just copy the bits
             CopyFrameImage(prevFrame.GetFrameData(),
-                           prevFrame.GetFrame()->GetRect(),
+                           prevFrame->GetRect(),
                            mAnim->compositingFrame.GetFrameData(),
-                           mAnim->compositingFrame.GetFrame()->GetRect());
+                           mAnim->compositingFrame->GetRect());
           } else {
             if (needToBlankComposite) {
               // Only blank composite when prev is transparent or not full.
               if (prevFrame->GetHasAlpha() || !isFullPrevFrame) {
                 ClearFrame(mAnim->compositingFrame.GetFrameData(),
-                           mAnim->compositingFrame.GetFrame()->GetRect());
+                           mAnim->compositingFrame->GetRect());
               }
             }
             DrawFrameTo(prevFrame.GetFrameData(), prevFrameRect,
-                        prevFrame.GetFrame()->PaletteDataLength(),
-                        prevFrame.GetFrame()->GetHasAlpha(),
+                        prevFrame->PaletteDataLength(),
+                        prevFrame->GetHasAlpha(),
                         mAnim->compositingFrame.GetFrameData(),
-                        mAnim->compositingFrame.GetFrame()->GetRect(),
-                        FrameBlendMethod(prevFrame.GetFrame()->GetBlendMethod()));
+                        mAnim->compositingFrame->GetRect(),
+                        FrameBlendMethod(prevFrame->GetBlendMethod()));
           }
         }
     }
   } else if (needToBlankComposite) {
     // If we just created the composite, it could have anything in it's
     // buffers. Clear them
     ClearFrame(mAnim->compositingFrame.GetFrameData(),
-               mAnim->compositingFrame.GetFrame()->GetRect());
+               mAnim->compositingFrame->GetRect());
   }
 
   // Check if the frame we are composing wants the previous image restored afer
   // it is done. Don't store it (again) if last frame wanted its image restored
   // too
   if ((nextFrameDisposalMethod == FrameBlender::kDisposeRestorePrevious) &&
       (prevFrameDisposalMethod != FrameBlender::kDisposeRestorePrevious)) {
     // We are storing the whole image.
@@ -393,36 +394,37 @@ FrameBlender::DoBlend(nsIntRect* aDirtyR
         mAnim->compositingPrevFrame.SetFrame(nullptr);
         return false;
       }
 
       mAnim->compositingPrevFrame.LockAndGetData();
     }
 
     CopyFrameImage(mAnim->compositingFrame.GetFrameData(),
-                   mAnim->compositingFrame.GetFrame()->GetRect(),
+                   mAnim->compositingFrame->GetRect(),
                    mAnim->compositingPrevFrame.GetFrameData(),
-                   mAnim->compositingPrevFrame.GetFrame()->GetRect());
+                   mAnim->compositingPrevFrame->GetRect());
   }
 
   // blit next frame into it's correct spot
   DrawFrameTo(nextFrame.GetFrameData(), nextFrameRect,
-              nextFrame.GetFrame()->PaletteDataLength(),
-              nextFrame.GetFrame()->GetHasAlpha(),
+              nextFrame->PaletteDataLength(),
+              nextFrame->GetHasAlpha(),
               mAnim->compositingFrame.GetFrameData(),
-              mAnim->compositingFrame.GetFrame()->GetRect(),
-              FrameBlendMethod(nextFrame.GetFrame()->GetBlendMethod()));
+              mAnim->compositingFrame->GetRect(),
+              FrameBlendMethod(nextFrame->GetBlendMethod()));
 
   // Set timeout of CompositeFrame to timeout of frame we just composed
   // Bug 177948
   int32_t timeout = nextFrame->GetRawTimeout();
   mAnim->compositingFrame->SetRawTimeout(timeout);
 
   // Tell the image that it is fully 'downloaded'.
-  nsresult rv = mAnim->compositingFrame->ImageUpdated(mAnim->compositingFrame->GetRect());
+  nsresult rv =
+    mAnim->compositingFrame->ImageUpdated(mAnim->compositingFrame->GetRect());
   if (NS_FAILED(rv)) {
     return false;
   }
 
   mAnim->lastCompositedFrameIndex = int32_t(aNextFrameIndex);
 
   return true;
 }
--- a/image/src/FrameBlender.h
+++ b/image/src/FrameBlender.h
@@ -39,26 +39,26 @@ public:
                uint32_t aNextFrameIndex);
 
   already_AddRefed<FrameSequence> GetFrameSequence();
 
   /**
    * Get the @aIndex-th frame, including (if applicable) any results of
    * blending.
    */
-  imgFrame* GetFrame(uint32_t aIndex) const;
+  already_AddRefed<imgFrame> GetFrame(uint32_t aIndex) const;
 
   /**
    * Get the @aIndex-th frame in the frame index, ignoring results of blending.
    */
-  imgFrame* RawGetFrame(uint32_t aIndex) const;
+  already_AddRefed<imgFrame> RawGetFrame(uint32_t aIndex) const;
 
   void InsertFrame(uint32_t framenum, imgFrame* aFrame);
   void RemoveFrame(uint32_t framenum);
-  imgFrame* SwapFrame(uint32_t framenum, imgFrame* aFrame);
+  already_AddRefed<imgFrame> SwapFrame(uint32_t framenum, imgFrame* aFrame);
   void ClearFrames();
 
   /* The total number of frames in this image. */
   uint32_t GetNumFrames() const;
 
   /*
    * Returns the frame's adjusted timeout. If the animation loops and the timeout
    * falls in between a certain range then the timeout is adjusted so that
--- a/image/src/FrameSequence.cpp
+++ b/image/src/FrameSequence.cpp
@@ -59,17 +59,17 @@ FrameSequence::InsertFrame(uint32_t fram
     }
 
     // Whenever we have more than one frame, we always lock *all* our frames
     // so we have all the image data pointers.
     mFrames[framenum].LockAndGetData();
   }
 }
 
-imgFrame*
+already_AddRefed<imgFrame>
 FrameSequence::SwapFrame(uint32_t framenum, imgFrame* aFrame)
 {
   NS_ABORT_IF_FALSE(framenum < mFrames.Length(), "Swapping invalid frame!");
 
   FrameDataPair ret;
 
   // Steal the imgFrame.
   if (framenum < mFrames.Length()) {
@@ -77,27 +77,27 @@ FrameSequence::SwapFrame(uint32_t framen
   }
 
   if (aFrame) {
     mFrames.ReplaceElementAt(framenum, aFrame);
   } else {
     mFrames.RemoveElementAt(framenum);
   }
 
-  return ret.Forget();
+  return ret.GetFrame();
 }
 
 size_t
 FrameSequence::SizeOfDecodedWithComputedFallbackIfHeap(gfxMemoryLocation aLocation,
                                                        MallocSizeOf aMallocSizeOf) const
 {
   size_t n = 0;
   for (uint32_t i = 0; i < mFrames.Length(); ++i) {
-    imgFrame* frame = mFrames.SafeElementAt(i, FrameDataPair());
-    NS_ABORT_IF_FALSE(frame, "Null frame in frame array!");
-    n += frame->SizeOfExcludingThisWithComputedFallbackIfHeap(aLocation, aMallocSizeOf);
+    FrameDataPair fdp = mFrames.SafeElementAt(i, FrameDataPair());
+    NS_ABORT_IF_FALSE(fdp, "Null frame in frame array!");
+    n += fdp->SizeOfExcludingThisWithComputedFallbackIfHeap(aLocation, aMallocSizeOf);
   }
 
   return n;
 }
 
 } // namespace image
 } // namespace mozilla
--- a/image/src/FrameSequence.h
+++ b/image/src/FrameSequence.h
@@ -4,16 +4,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_imagelib_FrameSequence_h_
 #define mozilla_imagelib_FrameSequence_h_
 
 #include "nsTArray.h"
 #include "mozilla/MemoryReporting.h"
+#include "mozilla/Move.h"
 #include "gfxTypes.h"
 #include "imgFrame.h"
 
 namespace mozilla {
 namespace image {
 
 /**
  * FrameDataPair is a slightly-smart tuple of (frame, raw frame data) where the
@@ -27,38 +28,56 @@ class FrameDataPair
 {
 public:
   explicit FrameDataPair(imgFrame* frame)
     : mFrame(frame)
     , mFrameData(nullptr)
   {}
 
   FrameDataPair()
-    : mFrame(nullptr)
+    : mFrameData(nullptr)
+  {}
+
+  FrameDataPair(const FrameDataPair& aOther)
+    : mFrame(aOther.mFrame)
     , mFrameData(nullptr)
   {}
 
-  FrameDataPair(FrameDataPair& other)
+  FrameDataPair(FrameDataPair&& aOther)
+    : mFrame(Move(aOther.mFrame))
+    , mFrameData(aOther.mFrameData)
   {
-    mFrame = other.mFrame;
-    mFrameData = other.mFrameData;
-
-    // since mFrame is an nsAutoPtr, the assignment operator above actually
-    // nulls out other.mFrame. In order to fully assume ownership over the
-    // frame, we also null out the other's mFrameData.
-    other.mFrameData = nullptr;
+    aOther.mFrameData = nullptr;
   }
 
   ~FrameDataPair()
   {
     if (mFrameData) {
       mFrame->UnlockImageData();
     }
   }
 
+  FrameDataPair& operator=(const FrameDataPair& aOther)
+  {
+    if (&aOther != this) {
+      mFrame = aOther.mFrame;
+      mFrameData = nullptr;
+    }
+    return *this;
+  }
+
+  FrameDataPair& operator=(FrameDataPair&& aOther)
+  {
+    MOZ_ASSERT(&aOther != this, "Moving to self");
+    mFrame = Move(aOther.mFrame);
+    mFrameData = aOther.mFrameData;
+    aOther.mFrameData = nullptr;
+    return *this;
+  }
+
   // Lock the frame and store its mFrameData. The frame will be unlocked (and
   // deleted) when this FrameDataPair is deleted.
   void LockAndGetData()
   {
     if (mFrame) {
       if (NS_SUCCEEDED(mFrame->LockImageData())) {
         if (mFrame->GetIsPaletted()) {
           mFrameData = reinterpret_cast<uint8_t*>(mFrame->GetPaletteData());
@@ -66,74 +85,74 @@ public:
           mFrameData = mFrame->GetImageData();
         }
       }
     }
   }
 
   // Null out this FrameDataPair and return its frame. You must ensure the
   // frame will be deleted separately.
-  imgFrame* Forget()
+  already_AddRefed<imgFrame> Forget()
   {
     if (mFrameData) {
       mFrame->UnlockImageData();
     }
 
-    imgFrame* frame = mFrame.forget();
     mFrameData = nullptr;
-    return frame;
+    return mFrame.forget();
   }
 
   bool HasFrameData() const
   {
     if (mFrameData) {
       MOZ_ASSERT(!!mFrame);
     }
     return !!mFrameData;
   }
 
   uint8_t* GetFrameData() const
   {
     return mFrameData;
   }
 
-  imgFrame* GetFrame() const
+  already_AddRefed<imgFrame> GetFrame() const
   {
-    return mFrame;
+    nsRefPtr<imgFrame> frame = mFrame;
+    return frame.forget();
   }
 
   // Resets this FrameDataPair to work with a different frame. Takes ownership
   // of the frame, deleting the old frame (if any).
   void SetFrame(imgFrame* frame)
   {
     if (mFrameData) {
       mFrame->UnlockImageData();
     }
 
     mFrame = frame;
     mFrameData = nullptr;
   }
 
-  operator imgFrame*() const
-  {
-    return GetFrame();
-  }
-
   imgFrame* operator->() const
   {
-    return GetFrame();
+    return mFrame.get();
   }
 
   bool operator==(imgFrame* other) const
   {
     return mFrame == other;
   }
 
+  operator bool() const
+  {
+    return mFrame != nullptr;
+  }
+
 private:
-  nsAutoPtr<imgFrame> mFrame;
+  nsRefPtr<imgFrame> mFrame;
   uint8_t* mFrameData;
 };
 
 /**
  * FrameSequence stores image frames (and their associated raw data pointers).
  * It is little more than a smart array.
  */
 class FrameSequence
@@ -158,17 +177,17 @@ public:
    * Remove (and delete) the frame at index framenum.
    */
   void RemoveFrame(uint32_t framenum);
 
   /**
    * Swap aFrame with the frame at sequence framenum, and return that frame.
    * You take ownership over the frame returned.
    */
-  imgFrame* SwapFrame(uint32_t framenum, imgFrame* aFrame);
+  already_AddRefed<imgFrame> SwapFrame(uint32_t framenum, imgFrame* aFrame);
 
   /**
    * Remove (and delete) all frames.
    */
   void ClearFrames();
 
   /* The total number of frames in this image. */
   uint32_t GetNumFrames() const;
--- a/image/src/RasterImage.cpp
+++ b/image/src/RasterImage.cpp
@@ -266,17 +266,17 @@ public:
       srcSurface = nullptr;
       dstSurface = nullptr;
     }
     return success;
   }
 
   // These values may only be touched on the main thread.
   WeakPtr<RasterImage> weakImage;
-  nsAutoPtr<imgFrame> dstFrame;
+  nsRefPtr<imgFrame> dstFrame;
   RefPtr<SourceSurface> srcSurface;
   RefPtr<SourceSurface> dstSurface;
 
   // Below are the values that may be touched on the scaling thread.
   uint8_t* srcData;
   uint8_t* dstData;
   nsIntRect srcRect;
   nsIntSize dstSize;
@@ -385,17 +385,16 @@ NS_IMPL_ISUPPORTS(RasterImage, imgIConta
 #endif
 
 //******************************************************************************
 RasterImage::RasterImage(imgStatusTracker* aStatusTracker,
                          ImageURL* aURI /* = nullptr */) :
   ImageResource(aURI), // invoke superclass's constructor
   mSize(0,0),
   mFrameDecodeFlags(DECODE_FLAGS_DEFAULT),
-  mMultipartDecodedFrame(nullptr),
   mAnim(nullptr),
   mLockCount(0),
   mDecodeCount(0),
   mRequestedSampleSize(0),
 #ifdef DEBUG
   mFramesNotified(0),
 #endif
   mDecodingMonitor("RasterImage Decoding Monitor"),
@@ -454,24 +453,23 @@ RasterImage::~RasterImage()
     DecodePool::StopDecoding(this);
     mDecoder = nullptr;
 
     // Unlock the last frame (if we have any). Our invariant is that, while we
     // have a decoder open, the last frame is always locked.
     // This would be done in ShutdownDecoder, but since mDecoder is non-null,
     // we didn't call ShutdownDecoder and we need to do it manually.
     if (GetNumFrames() > 0) {
-      imgFrame *curframe = mFrameBlender.RawGetFrame(GetNumFrames() - 1);
+      nsRefPtr<imgFrame> curframe = mFrameBlender.RawGetFrame(GetNumFrames() - 1);
       curframe->UnlockImageData();
     }
   }
 
   delete mAnim;
   mAnim = nullptr;
-  delete mMultipartDecodedFrame;
 
   // Total statistics
   num_containers--;
   total_source_bytes -= mSourceData.Length();
 
   if (NS_IsMainThread()) {
     DiscardTracker::Remove(&mDiscardTrackerNode);
   }
@@ -655,38 +653,38 @@ RasterImage::GetType(uint16_t *aType)
 //******************************************************************************
 /* [noscript, notxpcom] uint16_t GetType(); */
 NS_IMETHODIMP_(uint16_t)
 RasterImage::GetType()
 {
   return imgIContainer::TYPE_RASTER;
 }
 
-imgFrame*
+already_AddRefed<imgFrame>
 RasterImage::GetImgFrameNoDecode(uint32_t framenum)
 {
   if (!mAnim) {
     NS_ASSERTION(framenum == 0, "Don't ask for a frame > 0 if we're not animated!");
     return mFrameBlender.GetFrame(0);
   }
   return mFrameBlender.GetFrame(framenum);
 }
 
-imgFrame*
+already_AddRefed<imgFrame>
 RasterImage::GetImgFrame(uint32_t framenum)
 {
   nsresult rv = WantDecodedFrames();
   CONTAINER_ENSURE_TRUE(NS_SUCCEEDED(rv), nullptr);
   return GetImgFrameNoDecode(framenum);
 }
 
-imgFrame*
+already_AddRefed<imgFrame>
 RasterImage::GetDrawableImgFrame(uint32_t framenum)
 {
-  imgFrame* frame = nullptr;
+  nsRefPtr<imgFrame> frame;
 
   if (mMultipart && framenum == GetCurrentImgFrameIndex()) {
     // In the multipart case we prefer to use mMultipartDecodedFrame, which is
     // the most recent one we completely decoded, rather than display the real
     // current frame and risk severe tearing.
     frame = mMultipartDecodedFrame;
   }
 
@@ -694,29 +692,29 @@ RasterImage::GetDrawableImgFrame(uint32_
     frame = GetImgFrame(framenum);
   }
 
   // We will return a paletted frame if it's not marked as compositing failed
   // so we can catch crashes for reasons we haven't investigated.
   if (frame && frame->GetCompositingFailed())
     return nullptr;
 
-  return frame;
+  return frame.forget();
 }
 
 uint32_t
 RasterImage::GetCurrentImgFrameIndex() const
 {
   if (mAnim)
     return mAnim->GetCurrentAnimationFrameIndex();
 
   return 0;
 }
 
-imgFrame*
+already_AddRefed<imgFrame>
 RasterImage::GetCurrentImgFrame()
 {
   return GetImgFrame(GetCurrentImgFrameIndex());
 }
 
 //******************************************************************************
 /* [notxpcom] boolean frameIsOpaque(in uint32_t aWhichFrame); */
 NS_IMETHODIMP_(bool)
@@ -726,18 +724,19 @@ RasterImage::FrameIsOpaque(uint32_t aWhi
     NS_WARNING("aWhichFrame outside valid range!");
     return false;
   }
 
   if (mError)
     return false;
 
   // See if we can get an image frame.
-  imgFrame* frame = aWhichFrame == FRAME_FIRST ? GetImgFrameNoDecode(0)
-                                               : GetImgFrameNoDecode(GetCurrentImgFrameIndex());
+  nsRefPtr<imgFrame> frame = aWhichFrame == FRAME_FIRST
+                           ? GetImgFrameNoDecode(0)
+                           : GetImgFrameNoDecode(GetCurrentImgFrameIndex());
 
   // If we don't get a frame, the safe answer is "not opaque".
   if (!frame)
     return false;
 
   // Other, the frame is transparent if either:
   //  1. It needs a background.
   //  2. Its size doesn't cover our entire area.
@@ -750,18 +749,19 @@ nsIntRect
 RasterImage::FrameRect(uint32_t aWhichFrame)
 {
   if (aWhichFrame > FRAME_MAX_VALUE) {
     NS_WARNING("aWhichFrame outside valid range!");
     return nsIntRect();
   }
 
   // Get the requested frame.
-  imgFrame* frame = aWhichFrame == FRAME_FIRST ? GetImgFrameNoDecode(0)
-                                               : GetImgFrameNoDecode(GetCurrentImgFrameIndex());
+  nsRefPtr<imgFrame> frame = aWhichFrame == FRAME_FIRST
+                           ? GetImgFrameNoDecode(0)
+                           : GetImgFrameNoDecode(GetCurrentImgFrameIndex());
 
   // If we have the frame, use that rectangle.
   if (frame) {
     return frame->GetRect();
   }
 
   // If the frame doesn't exist, we return the empty rectangle. It's not clear
   // whether this is appropriate in general, but at the moment the only
@@ -850,17 +850,17 @@ RasterImage::CopyFrame(uint32_t aWhichFr
     CONTAINER_ENSURE_TRUE(NS_SUCCEEDED(rv), nullptr);
   }
 
   // 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);
+  nsRefPtr<imgFrame> frame = GetDrawableImgFrame(frameIndex);
   if (!frame) {
     return nullptr;
   }
 
   // Create a 32-bit image surface of our size, but draw using the frame's
   // rect, implicitly padding the frame out to the image's size.
 
   IntSize size(mSize.width, mSize.height);
@@ -924,17 +924,17 @@ RasterImage::GetFrame(uint32_t aWhichFra
     CONTAINER_ENSURE_TRUE(NS_SUCCEEDED(rv), nullptr);
   }
 
   // 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);
+  nsRefPtr<imgFrame> frame = GetDrawableImgFrame(frameIndex);
   if (!frame) {
     return nullptr;
   }
 
   RefPtr<SourceSurface> framesurf;
 
   // If this frame covers the entire image, we can just reuse its existing
   // surface.
@@ -1144,31 +1144,30 @@ RasterImage::InternalAddFrameHelper(uint
                                     uint8_t **imageData, uint32_t *imageLength,
                                     uint32_t **paletteData, uint32_t *paletteLength,
                                     imgFrame** aRetFrame)
 {
   NS_ABORT_IF_FALSE(framenum <= GetNumFrames(), "Invalid frame index!");
   if (framenum > GetNumFrames())
     return NS_ERROR_INVALID_ARG;
 
-  nsAutoPtr<imgFrame> frame(aFrame);
+  nsRefPtr<imgFrame> frame(aFrame);
 
   // We are in the middle of decoding. This will be unlocked when we finish
   // decoding or switch to another frame.
   frame->LockImageData();
 
   if (paletteData && paletteLength)
     frame->GetPaletteData(paletteData, paletteLength);
 
   frame->GetImageData(imageData, imageLength);
 
-  *aRetFrame = frame;
-
-  mFrameBlender.InsertFrame(framenum, frame.forget());
-
+  mFrameBlender.InsertFrame(framenum, frame);
+
+  frame.forget(aRetFrame);
   return NS_OK;
 }
 
 nsresult
 RasterImage::InternalAddFrame(uint32_t framenum,
                               int32_t aX, int32_t aY,
                               int32_t aWidth, int32_t aHeight,
                               SurfaceFormat aFormat,
@@ -1183,56 +1182,57 @@ RasterImage::InternalAddFrame(uint32_t f
   // previous frame when we create a new frame, and only when decoding do we
   // lock frames.
   NS_ABORT_IF_FALSE(mDecoder, "Only decoders may add frames!");
 
   NS_ABORT_IF_FALSE(framenum <= GetNumFrames(), "Invalid frame index!");
   if (framenum > GetNumFrames())
     return NS_ERROR_INVALID_ARG;
 
-  nsAutoPtr<imgFrame> frame(new imgFrame());
+  nsRefPtr<imgFrame> frame(new imgFrame());
 
   nsresult rv = frame->Init(aX, aY, aWidth, aHeight, aFormat, aPaletteDepth);
   if (!(mSize.width > 0 && mSize.height > 0))
     NS_WARNING("Shouldn't call InternalAddFrame with zero size");
   if (!NS_SUCCEEDED(rv))
     NS_WARNING("imgFrame::Init should succeed");
   NS_ENSURE_SUCCESS(rv, rv);
 
   // We know we are in a decoder. Therefore, we must unlock the previous frame
   // when we move on to decoding into the next frame.
   if (GetNumFrames() > 0) {
-    imgFrame *prevframe = mFrameBlender.RawGetFrame(GetNumFrames() - 1);
+    nsRefPtr<imgFrame> prevframe = mFrameBlender.RawGetFrame(GetNumFrames() - 1);
     prevframe->UnlockImageData();
   }
 
   if (GetNumFrames() == 0) {
-    return InternalAddFrameHelper(framenum, frame.forget(), imageData, imageLength,
+    return InternalAddFrameHelper(framenum, frame, imageData, imageLength,
                                   paletteData, paletteLength, aRetFrame);
   }
 
   if (GetNumFrames() == 1) {
     // Since we're about to add our second frame, initialize animation stuff
     EnsureAnimExists();
 
     // If we dispose of the first frame by clearing it, then the
     // First Frame's refresh area is all of itself.
     // RESTORE_PREVIOUS is invalid (assumed to be DISPOSE_CLEAR)
-    int32_t frameDisposalMethod = mFrameBlender.RawGetFrame(0)->GetFrameDisposalMethod();
+    nsRefPtr<imgFrame> firstFrame = mFrameBlender.RawGetFrame(0);
+    int32_t frameDisposalMethod = firstFrame->GetFrameDisposalMethod();
     if (frameDisposalMethod == FrameBlender::kDisposeClear ||
         frameDisposalMethod == FrameBlender::kDisposeRestorePrevious)
-      mAnim->SetFirstFrameRefreshArea(mFrameBlender.RawGetFrame(0)->GetRect());
+      mAnim->SetFirstFrameRefreshArea(firstFrame->GetRect());
   }
 
   // Calculate firstFrameRefreshArea
   // Some gifs are huge but only have a small area that they animate
   // We only need to refresh that small area when Frame 0 comes around again
   mAnim->UnionFirstFrameRefreshArea(frame->GetRect());
 
-  rv = InternalAddFrameHelper(framenum, frame.forget(), imageData, imageLength,
+  rv = InternalAddFrameHelper(framenum, frame, imageData, imageLength,
                               paletteData, paletteLength, aRetFrame);
 
   return rv;
 }
 
 bool
 RasterImage::ApplyDecodeFlags(uint32_t aNewFlags, uint32_t aWhichFrame)
 {
@@ -1332,57 +1332,56 @@ RasterImage::EnsureFrame(uint32_t aFrame
 
   // Adding a frame that doesn't already exist.
   if (aFrameNum == GetNumFrames()) {
     return InternalAddFrame(aFrameNum, aX, aY, aWidth, aHeight, aFormat,
                             aPaletteDepth, imageData, imageLength,
                             paletteData, paletteLength, aRetFrame);
   }
 
-  imgFrame *frame = mFrameBlender.RawGetFrame(aFrameNum);
+  nsRefPtr<imgFrame> frame = mFrameBlender.RawGetFrame(aFrameNum);
   if (!frame) {
     return InternalAddFrame(aFrameNum, aX, aY, aWidth, aHeight, aFormat,
                             aPaletteDepth, imageData, imageLength,
                             paletteData, paletteLength, aRetFrame);
   }
 
   // See if we can re-use the frame that already exists.
   nsIntRect rect = frame->GetRect();
   if (rect.x == aX && rect.y == aY && rect.width == aWidth &&
       rect.height == aHeight && frame->GetFormat() == aFormat &&
       frame->GetPaletteDepth() == aPaletteDepth) {
     frame->GetImageData(imageData, imageLength);
     if (paletteData) {
       frame->GetPaletteData(paletteData, paletteLength);
     }
 
-    *aRetFrame = frame;
-
     // We can re-use the frame if it has image data.
     if (*imageData && paletteData && *paletteData) {
+      frame.forget(aRetFrame);
       return NS_OK;
     }
     if (*imageData && !paletteData) {
+      frame.forget(aRetFrame);
       return NS_OK;
     }
   }
 
   // Not reusable, so replace the frame directly.
 
   // We know this frame is already locked, because it's the one we're currently
   // writing to.
   frame->UnlockImageData();
 
   mFrameBlender.RemoveFrame(aFrameNum);
-  nsAutoPtr<imgFrame> newFrame(new imgFrame());
+  nsRefPtr<imgFrame> newFrame(new imgFrame());
   nsresult rv = newFrame->Init(aX, aY, aWidth, aHeight, aFormat, aPaletteDepth);
   NS_ENSURE_SUCCESS(rv, rv);
-  return InternalAddFrameHelper(aFrameNum, newFrame.forget(), imageData,
-                                imageLength, paletteData, paletteLength,
-                                aRetFrame);
+  return InternalAddFrameHelper(aFrameNum, newFrame, imageData, imageLength,
+                                paletteData, paletteLength, aRetFrame);
 }
 
 nsresult
 RasterImage::EnsureFrame(uint32_t aFramenum, int32_t aX, int32_t aY,
                          int32_t aWidth, int32_t aHeight,
                          SurfaceFormat aFormat,
                          uint8_t** imageData, uint32_t* imageLength,
                          imgFrame** aFrame)
@@ -1399,17 +1398,17 @@ RasterImage::SetFrameAsNonPremult(uint32
 {
   if (mError)
     return NS_ERROR_FAILURE;
 
   NS_ABORT_IF_FALSE(aFrameNum < GetNumFrames(), "Invalid frame index!");
   if (aFrameNum >= GetNumFrames())
     return NS_ERROR_INVALID_ARG;
 
-  imgFrame* frame = mFrameBlender.RawGetFrame(aFrameNum);
+  nsRefPtr<imgFrame> frame = mFrameBlender.RawGetFrame(aFrameNum);
   NS_ABORT_IF_FALSE(frame, "Calling SetFrameAsNonPremult on frame that doesn't exist!");
   NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
 
   frame->SetAsNonPremult(aIsNonPremult);
 
   return NS_OK;
 }
 
@@ -1441,35 +1440,35 @@ RasterImage::DecodingComplete()
   // is not supported.
   //
   // We don't optimize the frame for multipart images because we reuse
   // the frame.
   if ((GetNumFrames() == 1) && !mMultipart) {
     // CanForciblyDiscard is used instead of CanForciblyDiscardAndRedecode
     // because we know decoding is complete at this point and this is not
     // an animation
+    nsRefPtr<imgFrame> firstFrame = mFrameBlender.RawGetFrame(0);
     if (DiscardingEnabled() && CanForciblyDiscard()) {
-      mFrameBlender.RawGetFrame(0)->SetDiscardable();
+      firstFrame->SetDiscardable();
     }
-    rv = mFrameBlender.RawGetFrame(0)->Optimize();
+    rv = firstFrame->Optimize();
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   // Double-buffer our frame in the multipart case, since we'll start decoding
   // into the first frame again immediately and this produces severe tearing.
   if (mMultipart) {
     if (GetNumFrames() == 1) {
       mMultipartDecodedFrame = mFrameBlender.SwapFrame(GetCurrentFrameIndex(),
                                                        mMultipartDecodedFrame);
     } else {
       // Don't double buffer for animated multipart images. It entails more
       // complexity and it's not really needed since we already are smart about
       // not displaying the still-decoding frame of an animated image. We may
       // have already stored an extra frame, though, so we'll release it here.
-      delete mMultipartDecodedFrame;
       mMultipartDecodedFrame = nullptr;
     }
   }
 
   if (mAnim) {
     mAnim->SetDoneDecoding(true);
   }
 
@@ -1492,17 +1491,17 @@ RasterImage::StartAnimation()
 {
   if (mError)
     return NS_ERROR_FAILURE;
 
   NS_ABORT_IF_FALSE(ShouldAnimate(), "Should not animate!");
 
   EnsureAnimExists();
 
-  imgFrame* currentFrame = GetCurrentImgFrame();
+  nsRefPtr<imgFrame> currentFrame = GetCurrentImgFrame();
   // A timeout of -1 means we should display this frame forever.
   if (currentFrame && mFrameBlender.GetTimeoutForFrame(GetCurrentImgFrameIndex()) < 0) {
     mAnimationFinished = true;
     return NS_ERROR_ABORT;
   }
 
   if (mAnim) {
     // We need to set the time that this initial frame was first displayed, as
@@ -1959,17 +1958,16 @@ RasterImage::Discard(bool force)
   // Delete all the decoded frames
   mFrameBlender.Discard();
 
   // Clear our downscaled frame.
   mScaleResult.status = SCALE_INVALID;
   mScaleResult.frame = nullptr;
 
   // Clear the last decoded multipart frame.
-  delete mMultipartDecodedFrame;
   mMultipartDecodedFrame = nullptr;
 
   // Flag that we no longer have decoded frames for this image
   mDecoded = false;
 
   // Notify that we discarded
   if (mStatusTracker)
     mStatusTracker->OnDiscard();
@@ -2084,17 +2082,17 @@ RasterImage::InitDecoder(bool aDoSizeDec
     default:
       NS_ABORT_IF_FALSE(0, "Shouldn't get here!");
   }
 
   // If we already have frames, we're probably in the multipart/x-mixed-replace
   // case. Regardless, we need to lock the last frame. Our invariant is that,
   // while we have a decoder open, the last frame is always locked.
   if (GetNumFrames() > 0) {
-    imgFrame *curframe = mFrameBlender.RawGetFrame(GetNumFrames() - 1);
+    nsRefPtr<imgFrame> curframe = mFrameBlender.RawGetFrame(GetNumFrames() - 1);
     curframe->LockImageData();
   }
 
   // Initialize the decoder
   if (!mDecodeRequest) {
     mDecodeRequest = new DecodeRequest(this);
   }
   MOZ_ASSERT(mDecodeRequest->mStatusTracker);
@@ -2166,17 +2164,17 @@ RasterImage::ShutdownDecoder(eShutdownIn
   mInDecoder = true;
   decoder->Finish(aIntent);
   mInDecoder = false;
   mFinishing = false;
 
   // Unlock the last frame (if we have any). Our invariant is that, while we
   // have a decoder open, the last frame is always locked.
   if (GetNumFrames() > 0) {
-    imgFrame *curframe = mFrameBlender.RawGetFrame(GetNumFrames() - 1);
+    nsRefPtr<imgFrame> curframe = mFrameBlender.RawGetFrame(GetNumFrames() - 1);
     curframe->UnlockImageData();
   }
 
   // Kill off our decode request, if it's pending.  (If not, this call is
   // harmless.)
   DecodePool::StopDecoding(this);
 
   nsresult decoderStatus = decoder->GetDecoderError();
@@ -2558,26 +2556,25 @@ void
 RasterImage::ScalingDone(ScaleRequest* request, ScaleStatus status)
 {
   MOZ_ASSERT(status == SCALE_DONE || status == SCALE_INVALID);
   MOZ_ASSERT(request);
 
   if (status == SCALE_DONE) {
     MOZ_ASSERT(request->done);
 
-    imgFrame *scaledFrame = request->dstFrame.forget();
-    scaledFrame->ImageUpdated(scaledFrame->GetRect());
+    mScaleResult.status = SCALE_DONE;
+    mScaleResult.frame = request->dstFrame.forget();
+    mScaleResult.scaledSize = request->dstSize;
+
+    mScaleResult.frame->ImageUpdated(mScaleResult.frame->GetRect());
 
     if (mStatusTracker) {
       mStatusTracker->FrameChanged(&request->srcRect);
     }
-
-    mScaleResult.status = SCALE_DONE;
-    mScaleResult.frame = scaledFrame;
-    mScaleResult.scaledSize = request->dstSize;
   } else {
     mScaleResult.status = SCALE_INVALID;
     mScaleResult.frame = nullptr;
   }
 
   // If we were waiting for this scale to come through, forget the scale
   // request. Otherwise, we still have a scale outstanding that it's possible
   // for us to (want to) stop.
@@ -2622,17 +2619,17 @@ RasterImage::RequestScale(imgFrame* aFra
 bool
 RasterImage::DrawWithPreDownscaleIfNeeded(imgFrame *aFrame,
                                           gfxContext *aContext,
                                           const nsIntSize& aSize,
                                           const ImageRegion& aRegion,
                                           GraphicsFilter aFilter,
                                           uint32_t aFlags)
 {
-  imgFrame *frame = aFrame;
+  nsRefPtr<imgFrame> frame = aFrame;
   nsIntRect framerect = frame->GetRect();
 
   gfxContextMatrixAutoSaveRestore saveMatrix(aContext);
   RefPtr<SourceSurface> surf;
   gfx::Size scale(double(aSize.width) / mSize.width,
                   double(aSize.height) / mSize.height);
 
   if (CanScale(aFilter, scale, aFlags) && !frame->IsSinglePixel()) {
@@ -2759,17 +2756,17 @@ RasterImage::Draw(gfxContext* aContext,
   // If a synchronous draw is requested, flush anything that might be sitting around
   if (aFlags & FLAG_SYNC_DECODE) {
     nsresult rv = SyncDecode();
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   uint32_t frameIndex = aWhichFrame == FRAME_FIRST ? 0
                                                    : GetCurrentImgFrameIndex();
-  imgFrame* frame = GetDrawableImgFrame(frameIndex);
+  nsRefPtr<imgFrame> frame = GetDrawableImgFrame(frameIndex);
   if (!frame) {
     return NS_OK; // Getting the frame (above) touches the image and kicks off decoding
   }
 
   bool drawn = DrawWithPreDownscaleIfNeeded(frame, aContext, aSize,
                                             aRegion, aFilter, aFlags);
   if (!drawn) {
     // The OS threw out some or all of our buffer. Start decoding again.
@@ -3690,17 +3687,17 @@ RasterImage::OptimalImageSizeForDest(con
         return mSize;     // We're waiting for exactly this result.
       }
     }
 
     // If there's only one instance of this image on this page, ask for a scale.
     uint32_t frameIndex = aWhichFrame == FRAME_FIRST ? 0
                                                      : GetCurrentImgFrameIndex();
 
-    imgFrame* frame = GetDrawableImgFrame(frameIndex);
+    nsRefPtr<imgFrame> frame = GetDrawableImgFrame(frameIndex);
     if (frame) {
       RequestScale(frame, destSize);
     }
   }
 
   // We either can't HQ scale to this size or the scaled version isn't ready
   // yet. Use our intrinsic size for now.
   return mSize;
--- a/image/src/RasterImage.h
+++ b/image/src/RasterImage.h
@@ -571,20 +571,20 @@ private:
    *
    * Does not change the size of mFrames.
    *
    * @param framenum The index of the frame to be deleted.
    *                 Must lie in [0, mFrames.Length() )
    */
   void DeleteImgFrame(uint32_t framenum);
 
-  imgFrame* GetImgFrameNoDecode(uint32_t framenum);
-  imgFrame* GetImgFrame(uint32_t framenum);
-  imgFrame* GetDrawableImgFrame(uint32_t framenum);
-  imgFrame* GetCurrentImgFrame();
+  already_AddRefed<imgFrame> GetImgFrameNoDecode(uint32_t framenum);
+  already_AddRefed<imgFrame> GetImgFrame(uint32_t framenum);
+  already_AddRefed<imgFrame> GetDrawableImgFrame(uint32_t framenum);
+  already_AddRefed<imgFrame> GetCurrentImgFrame();
   uint32_t GetCurrentImgFrameIndex() const;
 
   size_t SizeOfDecodedWithComputedFallbackIfHeap(gfxMemoryLocation aLocation,
                                                  MallocSizeOf aMallocSizeOf) const;
 
   void EnsureAnimExists();
 
   nsresult InternalAddFrameHelper(uint32_t framenum, imgFrame *frame,
@@ -632,19 +632,19 @@ private: // data
   // Valid flag bits are imgIContainer::FLAG_DECODE_NO_PREMULTIPLY_ALPHA
   // and imgIContainer::FLAG_DECODE_NO_COLORSPACE_CONVERSION.
   uint32_t                   mFrameDecodeFlags;
 
   //! All the frames of the image
   FrameBlender              mFrameBlender;
 
   // The last frame we decoded for multipart images.
-  imgFrame*                  mMultipartDecodedFrame;
+  nsRefPtr<imgFrame>        mMultipartDecodedFrame;
 
-  nsCOMPtr<nsIProperties>    mProperties;
+  nsCOMPtr<nsIProperties>   mProperties;
 
   // IMPORTANT: if you use mAnim in a method, call EnsureImageIsDecoded() first to ensure
   // that the frames actually exist (they may have been discarded to save memory, or
   // we maybe decoding on draw).
   FrameAnimator* mAnim;
 
   // Discard members
   uint32_t                   mLockCount;
@@ -743,17 +743,17 @@ private: // data
 
   struct ScaleResult
   {
     ScaleResult()
      : status(SCALE_INVALID)
     {}
 
     nsIntSize scaledSize;
-    nsAutoPtr<imgFrame> frame;
+    nsRefPtr<imgFrame> frame;
     ScaleStatus status;
   };
 
   ScaleResult mScaleResult;
 
   // We hold on to a bare pointer to a ScaleRequest while it's outstanding so
   // we can mark it as stopped if necessary. The ScaleWorker/DrawWorker duo
   // will inform us when to let go of this pointer.
--- a/image/src/imgFrame.cpp
+++ b/image/src/imgFrame.cpp
@@ -433,16 +433,26 @@ nsresult imgFrame::ImageUpdated(const ns
   return NS_OK;
 }
 
 nsIntRect imgFrame::GetRect() const
 {
   return nsIntRect(mOffset, nsIntSize(mSize.width, mSize.height));
 }
 
+int32_t
+imgFrame::GetStride() const
+{
+  if (mImageSurface) {
+    return mImageSurface->Stride();
+  }
+
+  return VolatileSurfaceStride(mSize, mFormat);
+}
+
 SurfaceFormat imgFrame::GetFormat() const
 {
   return mFormat;
 }
 
 bool imgFrame::GetNeedsBackground() const
 {
   // We need a background painted if we have alpha or we're incomplete.
@@ -660,16 +670,31 @@ imgFrame::GetSurface()
 
   VolatileBufferPtr<char> buf(mVBuf);
   if (buf.WasBufferPurged())
     return nullptr;
 
   return CreateLockedSurface(mVBuf, mSize, mFormat);
 }
 
+TemporaryRef<DrawTarget>
+imgFrame::GetDrawTarget()
+{
+  MOZ_ASSERT(mLockCount >= 1, "Should lock before requesting a DrawTarget");
+
+  uint8_t* data = GetImageData();
+  if (!data) {
+    return nullptr;
+  }
+
+  int32_t stride = GetStride();
+  return gfxPlatform::GetPlatform()->
+    CreateDrawTargetForData(data, mSize, stride, mFormat);
+}
+
 int32_t imgFrame::GetRawTimeout() const
 {
   return mTimeout;
 }
 
 void imgFrame::SetRawTimeout(int32_t aTimeout)
 {
   mTimeout = aTimeout;
--- a/image/src/imgFrame.h
+++ b/image/src/imgFrame.h
@@ -17,34 +17,39 @@ namespace mozilla {
 namespace image {
 
 class ImageRegion;
 
 class imgFrame
 {
   typedef gfx::Color Color;
   typedef gfx::DataSourceSurface DataSourceSurface;
+  typedef gfx::DrawTarget DrawTarget;
   typedef gfx::IntSize IntSize;
   typedef gfx::SourceSurface SourceSurface;
   typedef gfx::SurfaceFormat SurfaceFormat;
 
 public:
+  MOZ_DECLARE_REFCOUNTED_TYPENAME(imgFrame)
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(imgFrame)
+
   imgFrame();
-  ~imgFrame();
 
   nsresult Init(int32_t aX, int32_t aY, int32_t aWidth, int32_t aHeight, SurfaceFormat aFormat, uint8_t aPaletteDepth = 0);
   nsresult Optimize();
 
   bool Draw(gfxContext* aContext, const ImageRegion& aRegion,
             const nsIntMargin& aPadding, GraphicsFilter aFilter,
             uint32_t aImageFlags);
 
   nsresult ImageUpdated(const nsIntRect &aUpdateRect);
 
   nsIntRect GetRect() const;
+  IntSize GetSize() const { return mSize; }
+  int32_t GetStride() const;
   SurfaceFormat GetFormat() const;
   bool GetNeedsBackground() const;
   uint32_t GetImageBytesPerRow() const;
   uint32_t GetImageDataLength() const;
   bool GetIsPaletted() const;
   bool GetHasAlpha() const;
   void GetImageData(uint8_t **aData, uint32_t *length) const;
   uint8_t* GetImageData() const;
@@ -67,16 +72,17 @@ public:
   void SetCompositingFailed(bool val);
 
   nsresult LockImageData();
   nsresult UnlockImageData();
 
   void SetDiscardable();
 
   TemporaryRef<SourceSurface> GetSurface();
+  TemporaryRef<DrawTarget> GetDrawTarget();
 
   Color
   SinglePixelColor()
   {
     return mSinglePixelColor;
   }
 
   bool IsSinglePixel()
@@ -95,16 +101,18 @@ public:
     if (!mPaletteDepth)
       return 0;
 
     return ((1 << mPaletteDepth) * sizeof(uint32_t));
   }
 
 private: // methods
 
+  ~imgFrame();
+
   struct SurfaceWithFormat {
     nsRefPtr<gfxDrawable> mDrawable;
     SurfaceFormat mFormat;
     SurfaceWithFormat() {}
     SurfaceWithFormat(gfxDrawable* aDrawable, SurfaceFormat aFormat)
      : mDrawable(aDrawable), mFormat(aFormat) {}
     bool IsValid() { return !!mDrawable; }
   };
@@ -175,16 +183,16 @@ private: // data
         mFrame->UnlockImageData();
       }
     }
 
     // Whether the lock request succeeded.
     bool Succeeded() { return mSucceeded; }
 
   private:
-    imgFrame* mFrame;
+    nsRefPtr<imgFrame> mFrame;
     bool mSucceeded;
   };
 
 } // namespace image
 } // namespace mozilla
 
 #endif /* imgFrame_h */