Bug 716140 - Make mImageData and mColormap (and their sizes) protected members of mozilla::image::Decoder rather than the leaf classes.
authorJoe Drew <joe@drew.ca>
Mon, 28 Jan 2013 12:26:36 -0500
changeset 132062 ef71ebfb90a03ab8921c5c2ec024129d545e25d5
parent 132061 7d0a6850c8875b94d2c2303a2266972242c1776e
child 132063 c86eb1ff89168e43b8668fc41f3e22f3c0eb5fbd
push idunknown
push userunknown
push dateunknown
bugs716140
milestone22.0a1
Bug 716140 - Make mImageData and mColormap (and their sizes) protected members of mozilla::image::Decoder rather than the leaf classes.
image/decoders/nsBMPDecoder.cpp
image/decoders/nsBMPDecoder.h
image/decoders/nsGIFDecoder2.cpp
image/decoders/nsGIFDecoder2.h
image/decoders/nsIconDecoder.cpp
image/decoders/nsIconDecoder.h
image/decoders/nsJPEGDecoder.h
image/decoders/nsPNGDecoder.h
image/decoders/nsWBMPDecoder.cpp
image/decoders/nsWBMPDecoder.h
image/src/Decoder.cpp
image/src/Decoder.h
image/src/RasterImage.cpp
image/src/RasterImage.h
image/src/imgStatusTracker.cpp
--- a/image/decoders/nsBMPDecoder.cpp
+++ b/image/decoders/nsBMPDecoder.cpp
@@ -35,17 +35,16 @@ GetBMPLog()
 #define LINE(row) ((mBIH.height < 0) ? (-mBIH.height - (row)) : ((row) - 1))
 #define PIXEL_OFFSET(row, col) (LINE(row) * mBIH.width + col)
 
 nsBMPDecoder::nsBMPDecoder(RasterImage &aImage)
  : Decoder(aImage)
 {
   mColors = nullptr;
   mRow = nullptr;
-  mImageData = nullptr;
   mCurPos = mPos = mNumColors = mRowBytes = 0;
   mOldLine = mCurLine = 1; // Otherwise decoder will never start
   mState = eRLEStateInitial;
   mStateData = 0;
   mLOH = WIN_V3_HEADER_LENGTH;
   mUseAlphaData = mHaveAlphaData = false;
 }
 
@@ -84,17 +83,17 @@ nsBMPDecoder::GetHeight() const
 {
   return abs(mBIH.height);
 }
 
 // Obtains the internal output image buffer
 uint32_t* 
 nsBMPDecoder::GetImageData() 
 {
-  return mImageData;
+  return reinterpret_cast<uint32_t*>(mImageData);
 }
 
 // Obtains the size of the compressed image resource
 int32_t 
 nsBMPDecoder::GetCompressedImageSize() const
 {
   // For everything except BI_RGB the header field must be defined
   if (mBIH.compression != BI_RGB) {
@@ -424,17 +423,17 @@ nsBMPDecoder::WriteInternal(const char* 
                     memcpy(mRow + mRowBytes, aBuffer, toCopy);
                     aCount -= toCopy;
                     aBuffer += toCopy;
                     mRowBytes += toCopy;
                 }
                 if (rowSize == mRowBytes) {
                     // Collected a whole row into mRow, process it
                     uint8_t* p = mRow;
-                    uint32_t* d = mImageData + PIXEL_OFFSET(mCurLine, 0);
+                    uint32_t* d = reinterpret_cast<uint32_t*>(mImageData) + PIXEL_OFFSET(mCurLine, 0);
                     uint32_t lpos = mBIH.width;
                     switch (mBIH.bpp) {
                       case 1:
                         while (lpos > 0) {
                           int8_t bit;
                           uint8_t idx;
                           for (bit = 7; bit >= 0 && lpos > 0; bit--) {
                               idx = (*p >> bit) & 1;
@@ -482,17 +481,17 @@ nsBMPDecoder::WriteInternal(const char* 
                             if (!mHaveAlphaData && p[3]) {
                               // Non-zero alpha byte detected! Clear previous
                               // pixels that we have already processed.
                               // This works because we know that if we 
                               // are reaching here then the alpha data in byte 
                               // 4 has been right all along.  And we know it
                               // has been set to 0 the whole time, so that 
                               // means that everything is transparent so far.
-                              uint32_t* start = mImageData + GetWidth() * (mCurLine - 1);
+                              uint32_t* start = reinterpret_cast<uint32_t*>(mImageData) + GetWidth() * (mCurLine - 1);
                               uint32_t heightDifference = GetHeight() - mCurLine + 1;
                               uint32_t pixelCount = GetWidth() * heightDifference;
 
                               memset(start, 0, pixelCount * sizeof(uint32_t));
 
                               mHaveAlphaData = true;
                             }
                             SetPixel(d, p[2], p[1], p[0], mHaveAlphaData ? p[3] : 0xFF);
@@ -542,17 +541,17 @@ nsBMPDecoder::WriteInternal(const char* 
                             // the first byte (mStateData) specifies the
                             // number of consecutive pixels to be drawn 
                             // using the color index contained in
                             // the second byte
                             // Work around bitmaps that specify too many pixels
                             mState = eRLEStateInitial;
                             uint32_t pixelsNeeded = std::min<uint32_t>(mBIH.width - mCurPos, mStateData);
                             if (pixelsNeeded) {
-                                uint32_t* d = mImageData + PIXEL_OFFSET(mCurLine, mCurPos);
+                                uint32_t* d = reinterpret_cast<uint32_t*>(mImageData) + PIXEL_OFFSET(mCurLine, mCurPos);
                                 mCurPos += pixelsNeeded;
                                 if (mBIH.compression == BI_RLE8) {
                                     do {
                                         SetPixel(d, byte, mColors);
                                         pixelsNeeded --;
                                     } while (pixelsNeeded);
                                 } else {
                                     do {
@@ -630,17 +629,17 @@ nsBMPDecoder::WriteInternal(const char* 
 
                     case eRLEStateAbsoluteMode: // Absolute Mode
                     case eRLEStateAbsoluteModePadded:
                         if (mStateData) {
                             // In absolute mode, the second byte (mStateData)
                             // represents the number of pixels 
                             // that follow, each of which contains 
                             // the color index of a single pixel.
-                            uint32_t* d = mImageData + PIXEL_OFFSET(mCurLine, mCurPos);
+                            uint32_t* d = reinterpret_cast<uint32_t*>(mImageData) + PIXEL_OFFSET(mCurLine, mCurPos);
                             uint32_t* oldPos = d;
                             if (mBIH.compression == BI_RLE8) {
                                 while (aCount > 0 && mStateData > 0) {
                                     byte = *aBuffer++;
                                     aCount--;
                                     SetPixel(d, byte, mColors);
                                     mStateData--;
                                 }
--- a/image/decoders/nsBMPDecoder.h
+++ b/image/decoders/nsBMPDecoder.h
@@ -62,17 +62,16 @@ private:
 
     uint32_t mLOH; ///< Length of the header
 
     uint32_t mNumColors; ///< The number of used colors, i.e. the number of entries in mColors
     colorTable *mColors;
 
     bitFields mBitFields;
 
-    uint32_t *mImageData; ///< Pointer to the image data for the frame
     uint8_t *mRow;      ///< Holds one raw line of the image
     uint32_t mRowBytes; ///< How many bytes of the row were already received
     int32_t mCurLine;   ///< Index of the line of the image that's currently being decoded: [height,1]
     int32_t mOldLine;   ///< Previous index of the line 
     int32_t mCurPos;    ///< Index in the current line of the image
 
     ERLEState mState;   ///< Maintains the current state of the RLE decoding
     uint32_t mStateData;///< Decoding information that is needed depending on mState
--- a/image/decoders/nsGIFDecoder2.cpp
+++ b/image/decoders/nsGIFDecoder2.cpp
@@ -71,18 +71,16 @@ namespace image {
 #define GETINT16(p)   ((p)[1]<<8|(p)[0])
 //////////////////////////////////////////////////////////////////////
 // GIF Decoder Implementation
 
 nsGIFDecoder2::nsGIFDecoder2(RasterImage &aImage)
   : Decoder(aImage)
   , mCurrentRow(-1)
   , mLastFlushedRow(-1)
-  , mImageData(nullptr)
-  , mColormap(nullptr)
   , mOldColor(0)
   , mCurrentFrame(-1)
   , mCurrentPass(0)
   , mLastFlushedPass(0)
   , mGIFOpen(false)
   , mSawTransparency(false)
 {
   // Clear out the structure, excluding the arrays
--- a/image/decoders/nsGIFDecoder2.h
+++ b/image/decoders/nsGIFDecoder2.h
@@ -44,19 +44,16 @@ private:
   uint32_t  OutputRow();
   bool      DoLzw(const uint8_t *q);
 
   inline int ClearCode() const { return 1 << mGIFStruct.datasize; }
 
   int32_t mCurrentRow;
   int32_t mLastFlushedRow;
 
-  uint8_t *mImageData;       // Pointer to image data in either Cairo or 8bit format
-  uint32_t *mColormap;       // Current colormap to be used in Cairo format
-  uint32_t mColormapSize;
   uint32_t mOldColor;        // The old value of the transparent pixel
 
   // The frame number of the currently-decoding frame when we're in the middle
   // of decoding it, and -1 otherwise.
   int32_t mCurrentFrame;
 
   uint8_t mCurrentPass;
   uint8_t mLastFlushedPass;
--- a/image/decoders/nsIconDecoder.cpp
+++ b/image/decoders/nsIconDecoder.cpp
@@ -16,18 +16,16 @@
 namespace mozilla {
 namespace image {
 
 nsIconDecoder::nsIconDecoder(RasterImage &aImage)
  : Decoder(aImage),
    mWidth(-1),
    mHeight(-1),
    mPixBytesRead(0),
-   mPixBytesTotal(0),
-   mImageData(nullptr),
    mState(iconStateStart)
 {
   // Nothing to do
 }
 
 nsIconDecoder::~nsIconDecoder()
 { }
 
@@ -36,20 +34,16 @@ nsIconDecoder::WriteInternal(const char 
 {
   NS_ABORT_IF_FALSE(!HasError(), "Shouldn't call WriteInternal after error!");
 
   // We put this here to avoid errors about crossing initialization with case
   // jumps on linux.
   uint32_t bytesToRead = 0;
   nsresult rv;
 
-  // Performance isn't critical here, so our update rectangle is 
-  // always the full icon
-  nsIntRect r(0, 0, mWidth, mHeight);
-
   // Loop until the input data is gone
   while (aCount > 0) {
     switch (mState) {
       case iconStateStart:
 
         // Grab the width
         mWidth = (uint8_t)*aBuffer;
 
@@ -76,54 +70,60 @@ nsIconDecoder::WriteInternal(const char 
         if (IsSizeDecode()) {
           mState = iconStateFinished;
           break;
         }
 
         // Add the frame and signal
         rv = mImage.EnsureFrame(0, 0, 0, mWidth, mHeight,
                                 gfxASurface::ImageFormatARGB32,
-                                &mImageData, &mPixBytesTotal);
+                                &mImageData, &mImageDataLength);
         if (NS_FAILED(rv)) {
           PostDecoderError(rv);
           return;
         }
 
         // Tell the superclass we're starting a frame
         PostFrameStart();
 
         // Book Keeping
         aBuffer++;
         aCount--;
         mState = iconStateReadPixels;
         break;
 
       case iconStateReadPixels:
+      {
 
         // How many bytes are we reading?
-        bytesToRead = std::min(aCount, mPixBytesTotal - mPixBytesRead);
+        bytesToRead = std::min(aCount, mImageDataLength - mPixBytesRead);
 
         // Copy the bytes
         memcpy(mImageData + mPixBytesRead, aBuffer, bytesToRead);
 
+        // Performance isn't critical here, so our update rectangle is
+        // always the full icon
+        nsIntRect r(0, 0, mWidth, mHeight);
+
         // Invalidate
         PostInvalidation(r);
 
         // Book Keeping
         aBuffer += bytesToRead;
         aCount -= bytesToRead;
         mPixBytesRead += bytesToRead;
 
         // If we've got all the pixel bytes, we're finished
-        if (mPixBytesRead == mPixBytesTotal) {
+        if (mPixBytesRead == mImageDataLength) {
           PostFrameStop();
           PostDecodeDone();
           mState = iconStateFinished;
         }
         break;
+      }
 
       case iconStateFinished:
 
         // Consume all excess data silently
         aCount = 0;
 
         break;
     }
--- a/image/decoders/nsIconDecoder.h
+++ b/image/decoders/nsIconDecoder.h
@@ -41,18 +41,16 @@ public:
   nsIconDecoder(RasterImage &aImage);
   virtual ~nsIconDecoder();
 
   virtual void WriteInternal(const char* aBuffer, uint32_t aCount);
 
   uint8_t mWidth;
   uint8_t mHeight;
   uint32_t mPixBytesRead;
-  uint32_t mPixBytesTotal;
-  uint8_t* mImageData;
   uint32_t mState;
 };
 
 enum {
   iconStateStart      = 0,
   iconStateHaveHeight = 1,
   iconStateReadPixels = 2,
   iconStateFinished   = 3
--- a/image/decoders/nsJPEGDecoder.h
+++ b/image/decoders/nsJPEGDecoder.h
@@ -60,18 +60,16 @@ public:
 
   virtual Telemetry::ID SpeedHistogram();
   void NotifyDone();
 
 protected:
   void OutputScanlines(bool* suspend);
 
 public:
-  uint8_t *mImageData;
-
   struct jpeg_decompress_struct mInfo;
   struct jpeg_source_mgr mSourceMgr;
   decoder_error_mgr mErr;
   jstate mState;
 
   uint32_t mBytesToSkip;
 
   const JOCTET *mSegment;   // The current segment we are decoding from
--- a/image/decoders/nsPNGDecoder.h
+++ b/image/decoders/nsPNGDecoder.h
@@ -67,17 +67,16 @@ public:
   }
 
 public:
   png_structp mPNG;
   png_infop mInfo;
   nsIntRect mFrameRect;
   uint8_t *mCMSLine;
   uint8_t *interlacebuf;
-  uint8_t *mImageData;
   qcms_profile *mInProfile;
   qcms_transform *mTransform;
 
   gfxASurface::gfxImageFormat format;
 
   // For size decodes
   uint8_t *mHeaderBuf;
   uint32_t mHeaderBytesRead;
--- a/image/decoders/nsWBMPDecoder.cpp
+++ b/image/decoders/nsWBMPDecoder.cpp
@@ -65,17 +65,16 @@ static WbmpIntDecodeStatus DecodeEncoded
   // Out of data but in the middle of an encoded int.
   return IntParseInProgress;
 }
 
 nsWBMPDecoder::nsWBMPDecoder(RasterImage &aImage)
  : Decoder(aImage),
    mWidth(0),
    mHeight(0),
-   mImageData(nullptr),
    mRow(nullptr),
    mRowBytes(0),
    mCurLine(0),
    mState(WbmpStateStart)
 {
   // Nothing to do
 }
 
@@ -170,21 +169,20 @@ nsWBMPDecoder::WriteInternal(const char 
           }
 
           // If We're doing a size decode, we're done
           if (IsSizeDecode()) {
             mState = WbmpStateFinished;
             return;
           }
 
-          uint32_t imageLength;
           // Add the frame and signal
           nsresult rv = mImage.EnsureFrame(0, 0, 0, mWidth, mHeight,
                                            gfxASurface::ImageFormatRGB24,
-                                           (uint8_t**)&mImageData, &imageLength);
+                                           (uint8_t**)&mImageData, &mImageDataLength);
 
           if (NS_FAILED(rv) || !mImageData) {
             PostDecoderError(NS_ERROR_FAILURE);
             mState = DecodingFailed;
             return;
           }
 
           // Create mRow, the buffer that holds one line of the raw image data
@@ -232,17 +230,17 @@ nsWBMPDecoder::WriteInternal(const char 
             aCount -= toCopy;
             aBuffer += toCopy;
             mRowBytes += toCopy;
           }
 
           // If there is a filled buffered row of raw data, process the row.
           if (rowSize == mRowBytes) {
             uint8_t *p = mRow;
-            uint32_t *d = mImageData + (mWidth * mCurLine); // position of the first pixel at mCurLine
+            uint32_t *d = reinterpret_cast<uint32_t*>(mImageData) + (mWidth * mCurLine); // position of the first pixel at mCurLine
             uint32_t lpos = 0;
 
             while (lpos < mWidth) {
               for (int8_t bit = 7; bit >= 0; bit--) {
                 if (lpos >= mWidth)
                   break;
                 bool pixelWhite = (*p >> bit) & 1;
                 SetPixel(d, pixelWhite);
--- a/image/decoders/nsWBMPDecoder.h
+++ b/image/decoders/nsWBMPDecoder.h
@@ -44,18 +44,16 @@ public:
   virtual ~nsWBMPDecoder();
 
   virtual void WriteInternal(const char* aBuffer, uint32_t aCount);
 
 private:
   uint32_t mWidth;
   uint32_t mHeight;
 
-  uint32_t *mImageData;
-
   uint8_t* mRow;                    // Holds one raw line of the image
   uint32_t mRowBytes;               // How many bytes of the row were already received
   uint32_t mCurLine;                // The current line being decoded (0 to mHeight - 1)
 
   WbmpDecodingState mState;         // Describes what part of the file we are decoding now.
 };
 
 } // namespace image
--- a/image/src/Decoder.cpp
+++ b/image/src/Decoder.cpp
@@ -10,16 +10,18 @@
 #include "nsIScriptError.h"
 #include "sampler.h"
 
 namespace mozilla {
 namespace image {
 
 Decoder::Decoder(RasterImage &aImage)
   : mImage(aImage)
+  , mImageData(nullptr)
+  , mColormap(nullptr)
   , mDecodeFlags(0)
   , mDecodeDone(false)
   , mDataError(false)
   , mFrameCount(0)
   , mFailCode(NS_OK)
   , mInitialized(false)
   , mSizeDecode(false)
   , mInFrame(false)
--- a/image/src/Decoder.h
+++ b/image/src/Decoder.h
@@ -187,16 +187,21 @@ protected:
   /*
    * Member variables.
    *
    */
   RasterImage &mImage;
   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;
+
   uint32_t mDecodeFlags;
   bool mDecodeDone;
   bool mDataError;
 
 private:
   uint32_t mFrameCount; // Number of frames, including anything in-progress
 
   nsIntRect mInvalidRect; // Tracks an invalidation region in the current frame.
--- a/image/src/RasterImage.cpp
+++ b/image/src/RasterImage.cpp
@@ -1843,17 +1843,17 @@ RasterImage::DoImageDataComplete()
 
   // This call should come straight from necko - no reentrancy allowed
   NS_ABORT_IF_FALSE(!mInDecoder, "Re-entrant call to AddSourceData!");
 
   // If we're not storing any source data, then all the data was written
   // directly to the decoder in the AddSourceData() calls. This means we're
   // done, so we can shut down the decoder.
   if (!StoringSourceData()) {
-    RasterImage::FinishedSomeDecoding(this);
+    FinishedSomeDecoding();
   }
 
   // If there's a decoder open, synchronously decode the beginning of the image
   // to check for errors and get the image's size.  (If we already have the
   // image's size, this does nothing.)  Then kick off an async decode of the
   // rest of the image.
   if (mDecoder) {
     nsresult rv = DecodeWorker::Singleton()->DecodeUntilSizeAvailable(this);
@@ -2821,17 +2821,17 @@ RasterImage::RequestDecodeCore(RequestDe
   }
 
 
   // If we have a size decode open, interrupt it and shut it down; or if
   // the decoder has different flags than what we need
   if (mDecoder &&
       (mDecoder->IsSizeDecode() || mDecoder->GetDecodeFlags() != mFrameDecodeFlags))
   {
-    RasterImage::FinishedSomeDecoding(this, eShutdownIntent_NotNeeded);
+    FinishedSomeDecoding(eShutdownIntent_NotNeeded);
   }
 
   // If we don't have a decoder, create one 
   if (!mDecoder) {
     NS_ABORT_IF_FALSE(mFrames.IsEmpty(), "Trying to decode to non-empty frame-array");
     rv = InitDecoder(/* aDoSizeDecode = */ false);
 
     CONTAINER_ENSURE_SUCCESS(rv);
@@ -2880,17 +2880,17 @@ RasterImage::SyncDecode()
   // disallow this type of call in the API, and check for it in API methods.
   NS_ABORT_IF_FALSE(!mInDecoder, "Yikes, forcing sync in reentrant call!");
 
   // If we have a size decoder open, or one with different flags than
   // what we need, shut it down
   if (mDecoder &&
       (mDecoder->IsSizeDecode() || mDecoder->GetDecodeFlags() != mFrameDecodeFlags))
   {
-    RasterImage::FinishedSomeDecoding(this, eShutdownIntent_NotNeeded);
+    FinishedSomeDecoding(eShutdownIntent_NotNeeded);
   }
 
   // If we don't have a decoder, create one 
   if (!mDecoder) {
     NS_ABORT_IF_FALSE(mFrames.IsEmpty(), "Trying to decode to non-empty frame-array");
     rv = InitDecoder(/* aDoSizeDecode = */ false);
     CONTAINER_ENSURE_SUCCESS(rv);
   }
@@ -2911,17 +2911,17 @@ RasterImage::SyncDecode()
 
   // If we finished the decode, shutdown the decoder
   if (mDecoder && IsDecodeFinished()) {
     // We have to shut down the decoder *now*, so we explicitly shut down the
     // decoder, and let FinishedSomeDecoding handle the rest for us.
     nsRefPtr<DecodeRequest> request = mDecodeRequest;
     nsresult rv = ShutdownDecoder(eShutdownIntent_Done);
     CONTAINER_ENSURE_SUCCESS(rv);
-    RasterImage::FinishedSomeDecoding(this, eShutdownIntent_Done, request);
+    FinishedSomeDecoding(eShutdownIntent_Done, request);
   }
 
   // All good if no errors!
   return mError ? NS_ERROR_FAILURE : NS_OK;
 }
 
 static inline bool
 IsDownscale(const gfxSize& scale)
@@ -3200,17 +3200,17 @@ RasterImage::UnlockImage()
   // and our lock count is now zero (so nothing is forcing us to keep the
   // decoded data around), try to cancel the decode and throw away whatever
   // we've decoded.
   if (mHasBeenDecoded && mDecoder &&
       mLockCount == 0 && CanForciblyDiscard()) {
     PR_LOG(GetCompressedImageAccountingLog(), PR_LOG_DEBUG,
            ("RasterImage[0x%p] canceling decode because image "
             "is now unlocked.", this));
-    RasterImage::FinishedSomeDecoding(this, eShutdownIntent_NotNeeded);
+    FinishedSomeDecoding(eShutdownIntent_NotNeeded);
     ForceDiscard();
     return NS_OK;
   }
 
   // Otherwise, we might still be a candidate for discarding in the future.  If
   // we are, add ourselves to the discard tracker.
   if (CanDiscard()) {
     nsresult rv = DiscardTracker::Reset(&mDiscardTrackerNode);
@@ -3298,17 +3298,17 @@ void
 RasterImage::DoError()
 {
   // If we've flagged an error before, we have nothing to do
   if (mError)
     return;
 
   // If we're mid-decode, shut down the decoder.
   if (mDecoder) {
-    FinishedSomeDecoding(this, eShutdownIntent_Error);
+    FinishedSomeDecoding(eShutdownIntent_Error);
   }
 
   // Put the container in an error state
   mError = true;
 
   // Log our error
   LOG_CONTAINER_ERROR;
 }
@@ -3358,42 +3358,42 @@ RasterImage::GetFramesNotified(uint32_t 
   NS_ENSURE_ARG_POINTER(aFramesNotified);
 
   *aFramesNotified = mFramesNotified;
 
   return NS_OK;
 }
 #endif
 
-/* static */ void
-RasterImage::FinishedSomeDecoding(RasterImage* aImage,
-                                  eShutdownIntent aIntent /* = eShutdownIntent_Done */,
+void
+RasterImage::FinishedSomeDecoding(eShutdownIntent aIntent /* = eShutdownIntent_Done */,
                                   DecodeRequest* aRequest /* = nullptr */)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   nsRefPtr<DecodeRequest> request;
   if (aRequest) {
     request = aRequest;
   } else {
-    request = aImage->mDecodeRequest;
+    request = mDecodeRequest;
   }
 
   // Ensure that, if the decoder is the last reference to the image, we don't
   // destroy it by destroying the decoder.
-  nsRefPtr<RasterImage> image = aImage;
+  nsRefPtr<RasterImage> image(this);
 
   bool done = false;
 
   if (image->mDecoder) {
     if (request && request->mChunkCount && !image->mDecoder->IsSizeDecode()) {
       Telemetry::Accumulate(Telemetry::IMAGE_DECODE_CHUNKS, request->mChunkCount);
     }
 
-    // If the decode finished, tell the image and shut down the decoder.
+    // If the decode finished, or we're specifically being told to shut down,
+    // tell the image and shut down the decoder.
     if (image->mDecoder->GetDecodeDone() || image->IsDecodeFinished() ||
         aIntent != eShutdownIntent_Done) {
       done = true;
 
       // Hold on to a reference to the decoder until we're done with it
       nsRefPtr<Decoder> decoder = image->mDecoder;
 
       // We need to shut down the decoder first, in order to ensure all
@@ -3415,27 +3415,27 @@ RasterImage::FinishedSomeDecoding(Raster
           int32_t KBps = int32_t(request->mImage->mBytesDecoded /
                                  (1024 * request->mDecodeTime.ToSeconds()));
           Telemetry::Accumulate(id, KBps);
         }
       }
     }
   }
 
-  // If it have any frames, tell the image what happened to the most recent
+  // If it has any frames, tell the image what happened to the most recent
   // one. This will be better when frames are published to decoders instead of
   // decoders requesting them.
   if (image->GetNumFrames()) {
     nsIntRect rect;
     if (request) {
       rect = request->mStatusTracker->GetInvalidRect();
     } else {
       rect = image->CurrentStatusTracker().GetInvalidRect();
     }
-    aImage->FrameUpdated(image->GetNumFrames() - 1, rect);
+    image->FrameUpdated(image->GetNumFrames() - 1, rect);
   }
 
   // Then, tell the observers what happened in the decoder.
   // If we have no request, we have not yet created a decoder, but we still
   // need to send out notifications.
   if (request) {
     image->mStatusTracker->SyncAndSyncNotifyDifference(request->mStatusTracker);
   } else {
@@ -3456,24 +3456,24 @@ RasterImage::DecodeWorker::Singleton()
 
 RasterImage::DecodeWorker::~DecodeWorker()
 {
   MOZ_ASSERT(NS_IsMainThread(), "Must shut down DecodeWorker on main thread!");
 
   // Shut down all the decoders since we're going away.
   DecodeRequest* request = mASAPDecodeRequests.getFirst();
   while (request) {
-    RasterImage::FinishedSomeDecoding(request->mImage, eShutdownIntent_NotNeeded);
+    request->mImage->FinishedSomeDecoding(eShutdownIntent_NotNeeded);
 
     request = request->getNext();
   }
 
   request = mNormalDecodeRequests.getFirst();
   while (request) {
-    RasterImage::FinishedSomeDecoding(request->mImage, eShutdownIntent_NotNeeded);
+    request->mImage->FinishedSomeDecoding(eShutdownIntent_NotNeeded);
 
     request = request->getNext();
   }
 }
 
 void
 RasterImage::DecodeWorker::MarkAsASAP(RasterImage* aImg)
 {
@@ -3539,17 +3539,17 @@ RasterImage::DecodeWorker::RequestDecode
 }
 
 void
 RasterImage::DecodeWorker::DecodeABitOf(RasterImage* aImg)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   DecodeSomeOfImage(aImg);
-  RasterImage::FinishedSomeDecoding(aImg);
+  aImg->FinishedSomeDecoding();
 
   // If we aren't yet finished decoding and we have more data in hand, add
   // this request to the back of the priority list.
   if (aImg->mDecoder &&
       !aImg->mError &&
       !aImg->IsDecodeFinished() &&
       aImg->mSourceData.Length() > aImg->mBytesDecoded) {
     RequestDecode(aImg);
@@ -3612,21 +3612,21 @@ RasterImage::DecodeWorker::Run()
     if (image->mDecoder &&
         !image->mError &&
         !image->IsDecodeFinished() &&
         bytesDecoded < request->mBytesToDecode) {
       AddDecodeRequest(request, request->mBytesToDecode - bytesDecoded);
 
       // If we have a new frame, let everybody know about it.
       if (image->mDecoder->GetFrameCount() != oldCount) {
-        DecodeDoneWorker::DidSomeDecoding(image, request);
+        DecodeDoneWorker::NotifyFinishedSomeDecoding(image, request);
       }
     } else {
       // Nothing more for us to do - let everyone know what happened.
-      DecodeDoneWorker::DidSomeDecoding(image, request);
+      DecodeDoneWorker::NotifyFinishedSomeDecoding(image, request);
     }
 
   } while ((TimeStamp::Now() - eventStart).ToMilliseconds() <= gMaxMSBeforeYield);
 
   // If decode requests are pending, re-post ourself to the event loop.
   if (!mASAPDecodeRequests.isEmpty() || !mNormalDecodeRequests.isEmpty()) {
     EnsurePendingInEventLoop();
   }
@@ -3639,17 +3639,17 @@ RasterImage::DecodeWorker::Run()
 
 nsresult
 RasterImage::DecodeWorker::DecodeUntilSizeAvailable(RasterImage* aImg)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   nsresult rv = DecodeSomeOfImage(aImg, DECODE_TYPE_UNTIL_SIZE);
 
-  RasterImage::FinishedSomeDecoding(aImg);
+  aImg->FinishedSomeDecoding();
 
   return rv;
 }
 
 nsresult
 RasterImage::DecodeWorker::DecodeSomeOfImage(RasterImage* aImg,
                                              DecodeType aDecodeType /* = DECODE_TYPE_NORMAL */,
                                              uint32_t bytesToDecode /* = 0 */)
@@ -3748,26 +3748,26 @@ RasterImage::DecodeWorker::DecodeSomeOfI
 }
 
 RasterImage::DecodeDoneWorker::DecodeDoneWorker(RasterImage* image, DecodeRequest* request)
  : mImage(image)
  , mRequest(request)
 {}
 
 void
-RasterImage::DecodeDoneWorker::DidSomeDecoding(RasterImage* image, DecodeRequest* request)
+RasterImage::DecodeDoneWorker::NotifyFinishedSomeDecoding(RasterImage* image, DecodeRequest* request)
 {
   nsCOMPtr<DecodeDoneWorker> worker = new DecodeDoneWorker(image, request);
   NS_DispatchToMainThread(worker);
 }
 
 NS_IMETHODIMP
 RasterImage::DecodeDoneWorker::Run()
 {
-  RasterImage::FinishedSomeDecoding(mImage, eShutdownIntent_Done, mRequest);
+  mImage->FinishedSomeDecoding(eShutdownIntent_Done, mRequest);
 
   // If we didn't finish decoding yet, try again
   if (mImage->mDecoder && !mImage->IsDecodeFinished() &&
       mImage->mSourceData.Length() > mImage->mBytesDecoded) {
     DecodeWorker::Singleton()->RequestDecode(mImage);
   }
 
   return NS_OK;
--- a/image/src/RasterImage.h
+++ b/image/src/RasterImage.h
@@ -528,32 +528,31 @@ private:
   public:
     /**
      * Called by the DecodeWorker with an image when it's done some significant
      * portion of decoding that needs to be notified about.
      *
      * Ensures the decode state accumulated by the decoding process gets
      * applied to the image.
      */
-    static void DidSomeDecoding(RasterImage* image, DecodeRequest* request);
+    static void NotifyFinishedSomeDecoding(RasterImage* image, DecodeRequest* request);
 
     NS_IMETHOD Run();
 
   private: /* methods */
     DecodeDoneWorker(RasterImage* image, DecodeRequest* request);
 
   private: /* members */
 
     nsRefPtr<RasterImage> mImage;
     nsRefPtr<DecodeRequest> mRequest;
   };
 
-  static void FinishedSomeDecoding(RasterImage* image,
-                                   eShutdownIntent intent = eShutdownIntent_Done,
-                                   DecodeRequest* request = nullptr);
+  void FinishedSomeDecoding(eShutdownIntent intent = eShutdownIntent_Done,
+                            DecodeRequest* request = nullptr);
 
   void DrawWithPreDownscaleIfNeeded(imgFrame *aFrame,
                                     gfxContext *aContext,
                                     gfxPattern::GraphicsFilter aFilter,
                                     const gfxMatrix &aUserSpaceToImageSpace,
                                     const gfxRect &aFill,
                                     const nsIntRect &aSubimage);
 
--- a/image/src/imgStatusTracker.cpp
+++ b/image/src/imgStatusTracker.cpp
@@ -521,24 +521,22 @@ imgStatusTracker::SyncAndSyncNotifyDiffe
   mInvalidRect = mInvalidRect.Union(other->mInvalidRect);
   mState |= diffState | loadState;
   if (unblockedOnload) {
     mState &= ~stateBlockingOnload;
   }
   mImageStatus = other->mImageStatus;
   mIsMultipart = other->mIsMultipart;
   mHadLastPart = other->mHadLastPart;
+  mImageStatus |= other->mImageStatus;
 
   // The error state is sticky and overrides all other bits.
-  if (mImageStatus == imgIRequest::STATUS_ERROR ||
-      other->mImageStatus == imgIRequest::STATUS_ERROR) {
+  if (mImageStatus & imgIRequest::STATUS_ERROR) {
     mImageStatus = imgIRequest::STATUS_ERROR;
   } else {
-    mImageStatus |= other->mImageStatus;
-
     // Unset the bits that can get unset as part of the decoding process.
     if (!(other->mImageStatus & imgIRequest::STATUS_DECODE_STARTED)) {
       mImageStatus &= ~imgIRequest::STATUS_DECODE_STARTED;
     }
   }
 
   SyncNotifyState(mConsumers, !!mImage, diffState, mInvalidRect, mHadLastPart);