Backed out changeset 2fd42e5e2920 (bug 1057904) for bc3 test failures on a CLOSED TREE
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Wed, 26 Nov 2014 11:57:41 +0100
changeset 241935 b5dda6f0177021dcbf696de326ccf3327c14c4db
parent 241934 2b59e07890a82b901114eed26414b995d7aa9080
child 241936 5e415846b95144826d3f625849403d51c2a225a1
push id4311
push userraliiev@mozilla.com
push dateMon, 12 Jan 2015 19:37:41 +0000
treeherdermozilla-beta@150c9fed433b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1057904
milestone36.0a1
backs out2fd42e5e2920dfbed25a7186c81505a883ed17e0
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
Backed out changeset 2fd42e5e2920 (bug 1057904) for bc3 test failures on a CLOSED TREE
image/src/FrameBlender.cpp
image/src/FrameBlender.h
image/src/FrameSequence.cpp
image/src/FrameSequence.h
image/src/RasterImage.cpp
image/src/imgFrame.cpp
image/src/imgFrame.h
image/src/moz.build
--- a/image/src/FrameBlender.cpp
+++ b/image/src/FrameBlender.cpp
@@ -1,183 +1,204 @@
 /* -*- 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 "FrameBlender.h"
 
 #include "mozilla/MemoryReporting.h"
-#include "mozilla/Move.h"
 #include "MainThreadUtils.h"
 
 #include "pixman.h"
 
 namespace mozilla {
 
 using namespace gfx;
 
 namespace image {
 
-FrameBlender::FrameBlender()
- : mAnim(nullptr)
+FrameBlender::FrameBlender(FrameSequence* aSequenceToUse /* = nullptr */)
+ : mFrames(aSequenceToUse)
+ , mAnim(nullptr)
  , mLoopCount(-1)
 {
+  if (!mFrames) {
+    mFrames = new FrameSequence();
+  }
 }
 
 FrameBlender::~FrameBlender()
 {
   delete mAnim;
 }
 
-already_AddRefed<imgFrame>
-FrameBlender::GetFrame(uint32_t aFrameNum)
+already_AddRefed<FrameSequence>
+FrameBlender::GetFrameSequence()
 {
-  if (mAnim && mAnim->lastCompositedFrameIndex == int32_t(aFrameNum)) {
-    nsRefPtr<imgFrame> frame = mAnim->compositingFrame.get();
-    return frame.forget();
-  }
-  return RawGetFrame(aFrameNum);
+  nsRefPtr<FrameSequence> seq(mFrames);
+  return seq.forget();
 }
 
 already_AddRefed<imgFrame>
-FrameBlender::RawGetFrame(uint32_t aFrameNum)
+FrameBlender::GetFrame(uint32_t framenum) const
 {
   if (!mAnim) {
-    NS_ASSERTION(aFrameNum == 0,
-                 "Don't ask for a frame > 0 if we're not animated!");
-    aFrameNum = 0;
+    NS_ASSERTION(framenum == 0, "Don't ask for a frame > 0 if we're not animated!");
+    return mFrames->GetFrame(0).GetFrame();
   }
-  if (aFrameNum >= mFrames.Length()) {
-    return nullptr;
+  if (mAnim->lastCompositedFrameIndex == int32_t(framenum)) {
+    return mAnim->compositingFrame.GetFrame();
   }
-  nsRefPtr<imgFrame> frame = mFrames[aFrameNum].get();
-  return frame.forget();
+  return mFrames->GetFrame(framenum).GetFrame();
 }
 
-int32_t
-FrameBlender::GetFrameDisposalMethod(uint32_t aFrameNum) const
+already_AddRefed<imgFrame>
+FrameBlender::RawGetFrame(uint32_t framenum) const
 {
   if (!mAnim) {
-    NS_ASSERTION(aFrameNum == 0,
-                 "Don't ask for a frame > 0 if we're not animated!");
-    aFrameNum = 0;
+    NS_ASSERTION(framenum == 0, "Don't ask for a frame > 0 if we're not animated!");
+    return mFrames->GetFrame(0).GetFrame();
   }
-  if (aFrameNum >= mFrames.Length()) {
-    return FrameBlender::kDisposeNotSpecified;
-  }
-  return mFrames[aFrameNum]->GetFrameDisposalMethod();
+  return mFrames->GetFrame(framenum).GetFrame();
 }
 
 uint32_t
 FrameBlender::GetNumFrames() const
 {
-  return mFrames.Length();
+  return mFrames->GetNumFrames();
 }
 
 int32_t
-FrameBlender::GetTimeoutForFrame(uint32_t aFrameNum)
+FrameBlender::GetTimeoutForFrame(uint32_t framenum) const
 {
-  nsRefPtr<imgFrame> frame = RawGetFrame(aFrameNum);
+  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:
   //   10ms goes 100ms
   //   >10ms go correct speed
   // It seems that there are broken tools out there that set a 0ms or 10ms
   // timeout when they really want a "default" one.  So munge values in that
   // range.
-  if (timeout >= 0 && timeout <= 10 && mLoopCount != 0) {
+  if (timeout >= 0 && timeout <= 10 && mLoopCount != 0)
     return 100;
-  }
-
   return timeout;
 }
 
 void
 FrameBlender::SetLoopCount(int32_t aLoopCount)
 {
   mLoopCount = aLoopCount;
 }
 
 int32_t
 FrameBlender::GetLoopCount() const
 {
   return mLoopCount;
 }
 
 void
-FrameBlender::RemoveFrame(uint32_t aFrameNum)
+FrameBlender::RemoveFrame(uint32_t framenum)
 {
-  MOZ_ASSERT(aFrameNum < GetNumFrames(), "Deleting invalid frame!");
-  mFrames.RemoveElementAt(aFrameNum);
+  NS_ABORT_IF_FALSE(framenum < GetNumFrames(), "Deleting invalid frame!");
+
+  mFrames->RemoveFrame(framenum);
 }
 
 void
 FrameBlender::ClearFrames()
 {
-  mFrames.Clear();
-  mFrames.Compact();
+  // Forget our old frame sequence, letting whoever else has it deal with it.
+  mFrames = new FrameSequence();
 }
 
 void
-FrameBlender::InsertFrame(uint32_t aFrameNum, RawAccessFrameRef&& aRef)
+FrameBlender::InsertFrame(uint32_t framenum, imgFrame* aFrame)
 {
-  MOZ_ASSERT(aRef, "Need a reference to a frame");
-  MOZ_ASSERT(aFrameNum <= GetNumFrames(), "Inserting invalid frame");
+  NS_ABORT_IF_FALSE(framenum <= GetNumFrames(), "Inserting invalid frame!");
+  mFrames->InsertFrame(framenum, aFrame);
+  if (GetNumFrames() > 1) {
+    EnsureAnimExists();
+  }
+}
 
-  mFrames.InsertElementAt(aFrameNum, Move(aRef));
-  if (GetNumFrames() == 2) {
-    MOZ_ASSERT(!mAnim, "Shouldn't have an animation context yet");
-    mAnim = new Anim();
+already_AddRefed<imgFrame>
+FrameBlender::SwapFrame(uint32_t framenum, imgFrame* aFrame)
+{
+  NS_ABORT_IF_FALSE(framenum < GetNumFrames(), "Swapping invalid frame!");
+
+  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;
+    nsRefPtr<imgFrame> toDelete(mFrames->SwapFrame(framenum, aFrame));
+  } else {
+    ret = mFrames->SwapFrame(framenum, aFrame);
   }
 
-  MOZ_ASSERT(GetNumFrames() < 2 || mAnim,
-             "If we're animated we should have an animation context now");
+  return ret.forget();
+}
+
+void
+FrameBlender::EnsureAnimExists()
+{
+  if (!mAnim) {
+    // Create the animation context
+    mAnim = new Anim();
+
+    // We should only get into this code path directly after we've created our
+    // second frame (hence we know we're animated).
+    MOZ_ASSERT(GetNumFrames() == 2);
+  }
 }
 
 //******************************************************************************
 // DoBlend gets called when the timer for animation get fired and we have to
 // update the composited frame of the animation.
 bool
 FrameBlender::DoBlend(nsIntRect* aDirtyRect,
                       uint32_t aPrevFrameIndex,
                       uint32_t aNextFrameIndex)
 {
-  nsRefPtr<imgFrame> prevFrame = GetFrame(aPrevFrameIndex);
-  nsRefPtr<imgFrame> nextFrame = GetFrame(aNextFrameIndex);
-
-  MOZ_ASSERT(prevFrame && nextFrame, "Should have frames here");
+  if (!aDirtyRect) {
+    return false;
+  }
 
-  int32_t prevFrameDisposalMethod = GetFrameDisposalMethod(aPrevFrameIndex);
+  const FrameDataPair& prevFrame = mFrames->GetFrame(aPrevFrameIndex);
+  const FrameDataPair& nextFrame = mFrames->GetFrame(aNextFrameIndex);
+  if (!prevFrame.HasFrameData() || !nextFrame.HasFrameData()) {
+    return false;
+  }
+
+  int32_t prevFrameDisposalMethod = prevFrame->GetFrameDisposalMethod();
   if (prevFrameDisposalMethod == FrameBlender::kDisposeRestorePrevious &&
-      !mAnim->compositingPrevFrame) {
+      !mAnim->compositingPrevFrame)
     prevFrameDisposalMethod = FrameBlender::kDisposeClear;
-  }
 
   nsIntRect prevFrameRect = prevFrame->GetRect();
   bool isFullPrevFrame = (prevFrameRect.x == 0 && prevFrameRect.y == 0 &&
                           prevFrameRect.width == mSize.width &&
                           prevFrameRect.height == mSize.height);
 
   // Optimization: DisposeClearAll if the previous frame is the same size as
   //               container and it's clearing itself
   if (isFullPrevFrame &&
-      (prevFrameDisposalMethod == FrameBlender::kDisposeClear)) {
+      (prevFrameDisposalMethod == FrameBlender::kDisposeClear))
     prevFrameDisposalMethod = FrameBlender::kDisposeClearAll;
-  }
 
-  int32_t nextFrameDisposalMethod = GetFrameDisposalMethod(aNextFrameIndex);
+  int32_t nextFrameDisposalMethod = nextFrame->GetFrameDisposalMethod();
   nsIntRect nextFrameRect = nextFrame->GetRect();
   bool isFullNextFrame = (nextFrameRect.x == 0 && nextFrameRect.y == 0 &&
                           nextFrameRect.width == mSize.width &&
                           nextFrameRect.height == mSize.height);
 
   if (!nextFrame->GetIsPaletted()) {
     // Optimization: Skip compositing if the previous frame wants to clear the
     //               whole image
@@ -234,23 +255,24 @@ FrameBlender::DoBlend(nsIntRect* aDirtyR
   if (mAnim->lastCompositedFrameIndex == int32_t(aNextFrameIndex)) {
     return true;
   }
 
   bool needToBlankComposite = false;
 
   // Create the Compositing Frame
   if (!mAnim->compositingFrame) {
-    nsRefPtr<imgFrame> newFrame = new imgFrame;
-    nsresult rv = newFrame->InitForDecoder(mSize, SurfaceFormat::B8G8R8A8);
+    mAnim->compositingFrame.SetFrame(new imgFrame());
+    nsresult rv =
+      mAnim->compositingFrame->InitForDecoder(mSize, SurfaceFormat::B8G8R8A8);
     if (NS_FAILED(rv)) {
-      mAnim->compositingFrame.reset();
+      mAnim->compositingFrame.SetFrame(nullptr);
       return false;
     }
-    mAnim->compositingFrame = newFrame->RawAccessRef();
+    mAnim->compositingFrame.LockAndGetData();
     needToBlankComposite = true;
   } else if (int32_t(aNextFrameIndex) != mAnim->lastCompositedFrameIndex+1) {
 
     // If we are not drawing on top of last composited frame,
     // then we are building a new composite frame, so let's clear it first.
     needToBlankComposite = true;
   }
 
@@ -283,117 +305,119 @@ 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->GetRawData(),
+          ClearFrame(mAnim->compositingFrame.GetFrameData(),
                      mAnim->compositingFrame->GetRect());
         } else {
           // Only blank out previous frame area (both color & Mask/Alpha)
-          ClearFrame(mAnim->compositingFrame->GetRawData(),
+          ClearFrame(mAnim->compositingFrame.GetFrameData(),
                      mAnim->compositingFrame->GetRect(),
                      prevFrameRect);
         }
         break;
 
       case FrameBlender::kDisposeClearAll:
-        ClearFrame(mAnim->compositingFrame->GetRawData(),
+        ClearFrame(mAnim->compositingFrame.GetFrameData(),
                    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->GetRawData(),
+          CopyFrameImage(mAnim->compositingPrevFrame.GetFrameData(),
                          mAnim->compositingPrevFrame->GetRect(),
-                         mAnim->compositingFrame->GetRawData(),
+                         mAnim->compositingFrame.GetFrameData(),
                          mAnim->compositingFrame->GetRect());
 
           // destroy only if we don't need it for this frame's disposal
           if (nextFrameDisposalMethod != FrameBlender::kDisposeRestorePrevious)
-            mAnim->compositingPrevFrame.reset();
+            mAnim->compositingPrevFrame.SetFrame(nullptr);
         } else {
-          ClearFrame(mAnim->compositingFrame->GetRawData(),
+          ClearFrame(mAnim->compositingFrame.GetFrameData(),
                      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->GetRawData(),
+            CopyFrameImage(prevFrame.GetFrameData(),
                            prevFrame->GetRect(),
-                           mAnim->compositingFrame->GetRawData(),
+                           mAnim->compositingFrame.GetFrameData(),
                            mAnim->compositingFrame->GetRect());
           } else {
             if (needToBlankComposite) {
               // Only blank composite when prev is transparent or not full.
               if (prevFrame->GetHasAlpha() || !isFullPrevFrame) {
-                ClearFrame(mAnim->compositingFrame->GetRawData(),
+                ClearFrame(mAnim->compositingFrame.GetFrameData(),
                            mAnim->compositingFrame->GetRect());
               }
             }
-            DrawFrameTo(prevFrame->GetRawData(), prevFrameRect,
+            DrawFrameTo(prevFrame.GetFrameData(), prevFrameRect,
                         prevFrame->PaletteDataLength(),
                         prevFrame->GetHasAlpha(),
-                        mAnim->compositingFrame->GetRawData(),
+                        mAnim->compositingFrame.GetFrameData(),
                         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->GetRawData(),
+    ClearFrame(mAnim->compositingFrame.GetFrameData(),
                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.
     // It would be better if we just stored the area that nextFrame is going to
     // overwrite.
     if (!mAnim->compositingPrevFrame) {
-      nsRefPtr<imgFrame> newFrame = new imgFrame;
-      nsresult rv = newFrame->InitForDecoder(mSize, SurfaceFormat::B8G8R8A8);
+      mAnim->compositingPrevFrame.SetFrame(new imgFrame());
+      nsresult rv =
+        mAnim->compositingPrevFrame->InitForDecoder(mSize,
+                                                    SurfaceFormat::B8G8R8A8);
       if (NS_FAILED(rv)) {
-        mAnim->compositingPrevFrame.reset();
+        mAnim->compositingPrevFrame.SetFrame(nullptr);
         return false;
       }
 
-      mAnim->compositingPrevFrame = newFrame->RawAccessRef();
+      mAnim->compositingPrevFrame.LockAndGetData();
     }
 
-    CopyFrameImage(mAnim->compositingFrame->GetRawData(),
+    CopyFrameImage(mAnim->compositingFrame.GetFrameData(),
                    mAnim->compositingFrame->GetRect(),
-                   mAnim->compositingPrevFrame->GetRawData(),
+                   mAnim->compositingPrevFrame.GetFrameData(),
                    mAnim->compositingPrevFrame->GetRect());
   }
 
   // blit next frame into it's correct spot
-  DrawFrameTo(nextFrame->GetRawData(), nextFrameRect,
+  DrawFrameTo(nextFrame.GetFrameData(), nextFrameRect,
               nextFrame->PaletteDataLength(),
               nextFrame->GetHasAlpha(),
-              mAnim->compositingFrame->GetRawData(),
+              mAnim->compositingFrame.GetFrameData(),
               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);
 
@@ -561,33 +585,24 @@ FrameBlender::Discard()
   // Delete all the decoded frames, then clear the array.
   ClearFrames();
 }
 
 size_t
 FrameBlender::SizeOfDecodedWithComputedFallbackIfHeap(gfxMemoryLocation aLocation,
                                                       MallocSizeOf aMallocSizeOf) const
 {
-  size_t n = 0;
-
-  for (uint32_t i = 0; i < mFrames.Length(); ++i) {
-    n += mFrames[i]->SizeOfExcludingThisWithComputedFallbackIfHeap(aLocation,
-                                                                   aMallocSizeOf);
-  }
+  size_t n = mFrames->SizeOfDecodedWithComputedFallbackIfHeap(aLocation, aMallocSizeOf);
 
   if (mAnim) {
     if (mAnim->compositingFrame) {
-      n += mAnim->compositingFrame
-                ->SizeOfExcludingThisWithComputedFallbackIfHeap(aLocation,
-                                                                aMallocSizeOf);
+      n += mAnim->compositingFrame->SizeOfExcludingThisWithComputedFallbackIfHeap(aLocation, aMallocSizeOf);
     }
     if (mAnim->compositingPrevFrame) {
-      n += mAnim->compositingPrevFrame
-                ->SizeOfExcludingThisWithComputedFallbackIfHeap(aLocation,
-                                                                aMallocSizeOf);
+      n += mAnim->compositingPrevFrame->SizeOfExcludingThisWithComputedFallbackIfHeap(aLocation, aMallocSizeOf);
     }
   }
 
   return n;
 }
 
 void
 FrameBlender::ResetAnimation()
--- a/image/src/FrameBlender.h
+++ b/image/src/FrameBlender.h
@@ -4,67 +4,72 @@
  * 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_FrameBlender_h_
 #define mozilla_imagelib_FrameBlender_h_
 
 #include "mozilla/MemoryReporting.h"
 #include "gfxTypes.h"
-#include "imgFrame.h"
+#include "FrameSequence.h"
 #include "nsCOMPtr.h"
 
 namespace mozilla {
 namespace image {
 
+class imgFrame;
+
 /**
  * FrameBlender stores and gives access to imgFrames. It also knows how to
  * blend frames from previous to next, looping if necessary.
  *
  * All logic about when and whether to blend are external to FrameBlender.
  */
 class FrameBlender
 {
 public:
 
   /**
    * Create a new FrameBlender with a given frame sequence.
    *
    * If aSequenceToUse is not specified, it will be allocated automatically.
    */
-  FrameBlender();
+  explicit FrameBlender(FrameSequence* aSequenceToUse = nullptr);
   ~FrameBlender();
 
   bool DoBlend(nsIntRect* aDirtyRect, uint32_t aPrevFrameIndex,
                uint32_t aNextFrameIndex);
 
+  already_AddRefed<FrameSequence> GetFrameSequence();
+
   /**
    * Get the @aIndex-th frame, including (if applicable) any results of
    * blending.
    */
-  already_AddRefed<imgFrame> GetFrame(uint32_t aIndex);
+  already_AddRefed<imgFrame> GetFrame(uint32_t aIndex) const;
 
   /**
    * Get the @aIndex-th frame in the frame index, ignoring results of blending.
    */
-  already_AddRefed<imgFrame> RawGetFrame(uint32_t aIndex);
+  already_AddRefed<imgFrame> RawGetFrame(uint32_t aIndex) const;
 
-  void InsertFrame(uint32_t aFrameNum, RawAccessFrameRef&& aRef);
-  void RemoveFrame(uint32_t aFrameNum);
+  void InsertFrame(uint32_t framenum, imgFrame* aFrame);
+  void RemoveFrame(uint32_t framenum);
+  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
    * it's never 0. If the animation does not loop then no adjustments are made.
    */
-  int32_t GetTimeoutForFrame(uint32_t aFrameNum);
+  int32_t GetTimeoutForFrame(uint32_t framenum) const;
 
   /*
    * Set number of times to loop the image.
    * @note -1 means loop forever.
    */
   void SetLoopCount(int32_t aLoopCount);
   int32_t GetLoopCount() const;
 
@@ -102,53 +107,46 @@ public:
   // alpha blending.
   enum FrameAlpha {
     kFrameHasAlpha,
     kFrameOpaque
   };
 
 private:
 
-  /**
-   * Get the disposal method of the @aIndex-th frame.
-   *
-   * Note that it's not safe to use GetFrame(aIndex)->GetFrameDisposalMethod()
-   * instead, because the frame GetFrame() returns may be a blended frame which
-   * does not have the correct disposal method set.
-   */
-  int32_t GetFrameDisposalMethod(uint32_t aIndex) const;
-
   struct Anim
   {
     //! Track the last composited frame for Optimizations (See DoComposite code)
     int32_t lastCompositedFrameIndex;
 
     /** For managing blending of frames
      *
      * Some animations will use the compositingFrame to composite images
      * and just hand this back to the caller when it is time to draw the frame.
      * NOTE: When clearing compositingFrame, remember to set
      *       lastCompositedFrameIndex to -1.  Code assume that if
      *       lastCompositedFrameIndex >= 0 then compositingFrame exists.
      */
-    RawAccessFrameRef compositingFrame;
+    FrameDataPair compositingFrame;
 
     /** the previous composited frame, for DISPOSE_RESTORE_PREVIOUS
      *
      * The Previous Frame (all frames composited up to the current) needs to be
      * stored in cases where the image specifies it wants the last frame back
      * when it's done with the current frame.
      */
-    RawAccessFrameRef compositingPrevFrame;
+    FrameDataPair compositingPrevFrame;
 
     Anim() :
       lastCompositedFrameIndex(-1)
     {}
   };
 
+  void EnsureAnimExists();
+
   /** Clears an area of <aFrame> with transparent black.
    *
    * @param aFrameData Target Frame data
    * @param aFrameRect The rectangle of the data pointed ot by aFrameData
    *
    * @note Does also clears the transparancy mask
    */
   static void ClearFrame(uint8_t* aFrameData, const nsIntRect& aFrameRect);
@@ -177,17 +175,17 @@ private:
    */
   static nsresult DrawFrameTo(const uint8_t *aSrcData, const nsIntRect& aSrcRect,
                               uint32_t aSrcPaletteLength, bool aSrcHasAlpha,
                               uint8_t *aDstPixels, const nsIntRect& aDstRect,
                               FrameBlendMethod aBlendMethod);
 
 private: // data
   //! All the frames of the image
-  nsTArray<RawAccessFrameRef> mFrames;
+  nsRefPtr<FrameSequence> mFrames;
   nsIntSize mSize;
   Anim* mAnim;
   int32_t mLoopCount;
 };
 
 } // namespace image
 } // namespace mozilla
 
new file mode 100644
--- /dev/null
+++ b/image/src/FrameSequence.cpp
@@ -0,0 +1,103 @@
+/* -*- 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 "FrameSequence.h"
+
+namespace mozilla {
+namespace image {
+
+FrameSequence::~FrameSequence()
+{
+  ClearFrames();
+}
+
+const FrameDataPair&
+FrameSequence::GetFrame(uint32_t framenum) const
+{
+  if (framenum >= mFrames.Length()) {
+    static FrameDataPair empty;
+    return empty;
+  }
+
+  return mFrames[framenum];
+}
+
+uint32_t
+FrameSequence::GetNumFrames() const
+{
+  return mFrames.Length();
+}
+
+void
+FrameSequence::RemoveFrame(uint32_t framenum)
+{
+  NS_ABORT_IF_FALSE(framenum < mFrames.Length(), "Deleting invalid frame!");
+
+  mFrames.RemoveElementAt(framenum);
+}
+
+void
+FrameSequence::ClearFrames()
+{
+  // Since FrameDataPair holds an nsAutoPtr to its frame, clearing the mFrames
+  // array also deletes all the frames.
+  mFrames.Clear();
+}
+
+void
+FrameSequence::InsertFrame(uint32_t framenum, imgFrame* aFrame)
+{
+  NS_ABORT_IF_FALSE(framenum <= mFrames.Length(), "Inserting invalid frame!");
+  mFrames.InsertElementAt(framenum, aFrame);
+  if (GetNumFrames() > 1) {
+    // If we're creating our second element, we now know we're animated.
+    // Therefore, we need to lock the first frame too.
+    if (GetNumFrames() == 2) {
+      mFrames[0].LockAndGetData();
+    }
+
+    // Whenever we have more than one frame, we always lock *all* our frames
+    // so we have all the image data pointers.
+    mFrames[framenum].LockAndGetData();
+  }
+}
+
+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()) {
+    ret = mFrames[framenum];
+  }
+
+  if (aFrame) {
+    mFrames.ReplaceElementAt(framenum, aFrame);
+  } else {
+    mFrames.RemoveElementAt(framenum);
+  }
+
+  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) {
+    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
new file mode 100644
--- /dev/null
+++ b/image/src/FrameSequence.h
@@ -0,0 +1,206 @@
+/* -*- 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_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
+ * raw frame data is allowed to be (and is, initially) null.
+ *
+ * If you call LockAndGetData, you will be able to call GetFrameData() on that
+ * instance, and when the FrameDataPair is destructed, the imgFrame lock will
+ * be unlocked.
+ */
+class FrameDataPair
+{
+public:
+  explicit FrameDataPair(imgFrame* frame)
+    : mFrame(frame)
+    , mFrameData(nullptr)
+  {}
+
+  FrameDataPair()
+    : mFrameData(nullptr)
+  {}
+
+  FrameDataPair(const FrameDataPair& aOther)
+    : mFrame(aOther.mFrame)
+    , mFrameData(nullptr)
+  {}
+
+  FrameDataPair(FrameDataPair&& aOther)
+    : mFrame(Move(aOther.mFrame))
+    , mFrameData(aOther.mFrameData)
+  {
+    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());
+        } else {
+          mFrameData = mFrame->GetImageData();
+        }
+      }
+    }
+  }
+
+  // Null out this FrameDataPair and return its frame. You must ensure the
+  // frame will be deleted separately.
+  already_AddRefed<imgFrame> Forget()
+  {
+    if (mFrameData) {
+      mFrame->UnlockImageData();
+    }
+
+    mFrameData = nullptr;
+    return mFrame.forget();
+  }
+
+  bool HasFrameData() const
+  {
+    if (mFrameData) {
+      MOZ_ASSERT(!!mFrame);
+    }
+    return !!mFrameData;
+  }
+
+  uint8_t* GetFrameData() const
+  {
+    return mFrameData;
+  }
+
+  already_AddRefed<imgFrame> GetFrame() const
+  {
+    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;
+  }
+
+  imgFrame* operator->() const
+  {
+    return mFrame.get();
+  }
+
+  bool operator==(imgFrame* other) const
+  {
+    return mFrame == other;
+  }
+
+  operator bool() const
+  {
+    return mFrame != nullptr;
+  }
+
+private:
+  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
+{
+  ~FrameSequence();
+
+public:
+
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(FrameSequence)
+
+  /**
+   * Get the read-only (frame, data) pair at index aIndex.
+   */
+  const FrameDataPair& GetFrame(uint32_t aIndex) const;
+
+  /**
+   * Insert a frame into the array. FrameSequence takes ownership of the frame.
+   */
+  void InsertFrame(uint32_t framenum, imgFrame* aFrame);
+
+  /**
+   * 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.
+   */
+  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;
+
+  size_t SizeOfDecodedWithComputedFallbackIfHeap(gfxMemoryLocation aLocation,
+                                                 MallocSizeOf aMallocSizeOf) const;
+
+private: // data
+  //! All the frames of the image
+  nsTArray<FrameDataPair> mFrames;
+};
+
+} // namespace image
+} // namespace mozilla
+
+#endif /* mozilla_imagelib_FrameSequence_h_ */
--- a/image/src/RasterImage.cpp
+++ b/image/src/RasterImage.cpp
@@ -356,16 +356,25 @@ RasterImage::~RasterImage()
   }
 
   if (mDecoder) {
     // Kill off our decode request, if it's pending.  (If not, this call is
     // harmless.)
     ReentrantMonitorAutoEnter lock(mDecodingMonitor);
     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) {
+      nsRefPtr<imgFrame> curframe = mFrameBlender.RawGetFrame(GetNumFrames() - 1);
+      curframe->UnlockImageData();
+    }
   }
 
   // Release any HQ scaled frames from the surface cache.
   SurfaceCache::Discard(this);
 
   mAnim = nullptr;
 
   // Total statistics
@@ -1009,30 +1018,28 @@ 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;
 
-  nsRefPtr<imgFrame> frame = aFrame;
-  RawAccessFrameRef ref = frame->RawAccessRef();
-  if (!ref) {
-    // Probably the OS discarded the frame. Exceedingly unlikely since we just
-    // created it, but it could happen.
-    return NS_ERROR_FAILURE;
-  }
+  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);
 
-  mFrameBlender.InsertFrame(framenum, Move(ref));
+  mFrameBlender.InsertFrame(framenum, frame);
 
   frame.forget(aRetFrame);
   return NS_OK;
 }
 
 nsresult
 RasterImage::InternalAddFrame(uint32_t framenum,
                               int32_t aX, int32_t aY,
@@ -1059,16 +1066,23 @@ RasterImage::InternalAddFrame(uint32_t f
   nsIntRect frameRect(aX, aY, aWidth, aHeight);
   nsresult rv = frame->InitForDecoder(frameRect, 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) {
+    nsRefPtr<imgFrame> prevframe = mFrameBlender.RawGetFrame(GetNumFrames() - 1);
+    prevframe->UnlockImageData();
+  }
+
   if (GetNumFrames() == 0) {
     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();
@@ -1222,16 +1236,21 @@ RasterImage::EnsureFrame(uint32_t aFrame
     }
     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);
   nsRefPtr<imgFrame> newFrame(new imgFrame());
   nsIntRect frameRect(aX, aY, aWidth, aHeight);
   nsresult rv = newFrame->InitForDecoder(frameRect, aFormat, aPaletteDepth);
   NS_ENSURE_SUCCESS(rv, rv);
   return InternalAddFrameHelper(aFrameNum, newFrame, imageData, imageLength,
                                 paletteData, paletteLength, aRetFrame);
 }
@@ -1305,17 +1324,18 @@ RasterImage::DecodingComplete()
       firstFrame->SetDiscardable();
     }
   }
 
   // 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.GetFrame(GetCurrentFrameIndex());
+      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.
       mMultipartDecodedFrame = nullptr;
     }
   }
@@ -1906,16 +1926,24 @@ RasterImage::InitDecoder(bool aDoSizeDec
       break;
     case eDecoderType_icon:
       mDecoder = new nsIconDecoder(*this);
       break;
     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) {
+    nsRefPtr<imgFrame> curframe = mFrameBlender.RawGetFrame(GetNumFrames() - 1);
+    curframe->LockImageData();
+  }
+
   // Initialize the decoder
   mDecoder->SetSizeDecode(aDoSizeDecode);
   mDecoder->SetDecodeFlags(mFrameDecodeFlags);
   if (!aDoSizeDecode) {
     // We already have the size; tell the decoder so it can preallocate a
     // frame.  By default, we create an ARGB frame with no offset. If decoders
     // need a different type, they need to ask for it themselves.
     mDecoder->NeedNewFrame(0, 0, 0, mSize.width, mSize.height,
@@ -1968,16 +1996,23 @@ RasterImage::ShutdownDecoder(ShutdownRea
   // Finalize the decoder
   // null out mDecoder, _then_ check for errors on the close (otherwise the
   // error routine might re-invoke ShutdownDecoder)
   nsRefPtr<Decoder> decoder = mDecoder;
   mDecoder = nullptr;
 
   decoder->Finish(aReason);
 
+  // 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) {
+    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();
   if (NS_FAILED(decoderStatus)) {
     DoError();
     return decoderStatus;
--- a/image/src/imgFrame.cpp
+++ b/image/src/imgFrame.cpp
@@ -574,44 +574,19 @@ imgFrame::GetStride() const
 {
   if (mImageSurface) {
     return mImageSurface->Stride();
   }
 
   return VolatileSurfaceStride(mSize, mFormat);
 }
 
-<<<<<<< found
 SurfaceFormat imgFrame::GetFormat() const
 {
-||||||| expected
-bool imgFrame::GetNeedsBackground() const
-{
-  // We need a background painted if we have alpha or we're incomplete.
-=======
-bool imgFrame::GetNeedsBackground() const
-{
-  // We need a background painted if we're incomplete.
-  if (!ImageComplete()) {
-    return true;
-  }
-
-  // We need a background painted if we might not be opaque.
->>>>>>> replacement
-<<<<<<< found
   return mFormat;
-||||||| expected
-  return (mFormat == SurfaceFormat::B8G8R8A8 || !ImageComplete());
-=======
-  if (mFormat == SurfaceFormat::B8G8R8A8 && !mHasNoAlpha) {
-    return true;
-  }
-
-  return false;
->>>>>>> replacement
 }
 
 uint32_t imgFrame::GetImageBytesPerRow() const
 {
   if (mVBuf)
     return mSize.width * BytesPerPixel(mFormat);
 
   if (mPaletteDepth)
@@ -673,26 +648,16 @@ void imgFrame::GetPaletteData(uint32_t *
 uint32_t* imgFrame::GetPaletteData() const
 {
   uint32_t* data;
   uint32_t length;
   GetPaletteData(&data, &length);
   return data;
 }
 
-uint8_t*
-imgFrame::GetRawData() const
-{
-  MOZ_ASSERT(mLockCount, "Should be locked to call GetRawData()");
-  if (mPalettedImageData) {
-    return mPalettedImageData;
-  }
-  return GetImageData();
-}
-
 nsresult imgFrame::LockImageData()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   NS_ABORT_IF_FALSE(mLockCount >= 0, "Unbalanced locks and unlocks");
   if (mLockCount < 0) {
     return NS_ERROR_FAILURE;
   }
--- a/image/src/imgFrame.h
+++ b/image/src/imgFrame.h
@@ -91,17 +91,16 @@ public:
   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;
   void GetPaletteData(uint32_t **aPalette, uint32_t *length) const;
   uint32_t* GetPaletteData() const;
-  uint8_t* GetRawData() const;
 
   int32_t GetRawTimeout() const;
   void SetRawTimeout(int32_t aTimeout);
 
   int32_t GetFrameDisposalMethod() const;
   void SetFrameDisposalMethod(int32_t aFrameDisposalMethod);
   int32_t GetBlendMethod() const;
   void SetBlendMethod(int32_t aBlendMethod);
--- a/image/src/moz.build
+++ b/image/src/moz.build
@@ -17,16 +17,17 @@ EXPORTS += [
 UNIFIED_SOURCES += [
     'ClippedImage.cpp',
     'DecodePool.cpp',
     'Decoder.cpp',
     'DiscardTracker.cpp',
     'DynamicImage.cpp',
     'FrameAnimator.cpp',
     'FrameBlender.cpp',
+    'FrameSequence.cpp',
     'FrozenImage.cpp',
     'Image.cpp',
     'ImageFactory.cpp',
     'ImageMetadata.cpp',
     'ImageOps.cpp',
     'ImageWrapper.cpp',
     'imgFrame.cpp',
     'imgTools.cpp',