Bug 512269 - imgIDecoder shouldn't use an nsIInputStream for input. r=joe
authorBobby Holley <bobbyholley@stanford.edu>
Thu, 15 Oct 2009 19:54:44 -0700
changeset 33898 1d5e6291b2586bf6e31bd21b7db46bf4ee82c5c8
parent 33897 9605f7ad200c69122574bd348ec7597c32118f47
child 33899 431c83030afef7e74b7da6aed3cb9102a764c99e
push id9755
push userbobbyholley@stanford.edu
push dateFri, 16 Oct 2009 02:55:03 +0000
treeherdermozilla-central@1d5e6291b258 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjoe
bugs512269
milestone1.9.3a1pre
Bug 512269 - imgIDecoder shouldn't use an nsIInputStream for input. r=joe
modules/libpr0n/decoders/bmp/nsBMPDecoder.cpp
modules/libpr0n/decoders/bmp/nsBMPDecoder.h
modules/libpr0n/decoders/bmp/nsICODecoder.cpp
modules/libpr0n/decoders/bmp/nsICODecoder.h
modules/libpr0n/decoders/gif/nsGIFDecoder2.cpp
modules/libpr0n/decoders/gif/nsGIFDecoder2.h
modules/libpr0n/decoders/icon/nsIconDecoder.cpp
modules/libpr0n/decoders/jpeg/nsJPEGDecoder.cpp
modules/libpr0n/decoders/jpeg/nsJPEGDecoder.h
modules/libpr0n/decoders/png/nsPNGDecoder.cpp
modules/libpr0n/decoders/png/nsPNGDecoder.h
modules/libpr0n/public/imgIDecoder.idl
modules/libpr0n/src/imgContainer.cpp
modules/libpr0n/src/imgContainer.h
--- a/modules/libpr0n/decoders/bmp/nsBMPDecoder.cpp
+++ b/modules/libpr0n/decoders/bmp/nsBMPDecoder.cpp
@@ -116,48 +116,16 @@ NS_IMETHODIMP nsBMPDecoder::Close(PRUint
     return NS_OK;
 }
 
 NS_IMETHODIMP nsBMPDecoder::Flush()
 {
     return NS_OK;
 }
 
-NS_METHOD nsBMPDecoder::ReadSegCb(nsIInputStream* aIn, void* aClosure,
-                             const char* aFromRawSegment, PRUint32 aToOffset,
-                             PRUint32 aCount, PRUint32 *aWriteCount) 
-{
-    nsBMPDecoder *decoder = reinterpret_cast<nsBMPDecoder*>(aClosure);
-
-    // Always read everything
-    *aWriteCount = aCount;
-
-    nsresult rv = decoder->ProcessData(aFromRawSegment, aCount);
-
-    // Necko doesn't propagate rvs. Set a flag before returning.
-    if (NS_FAILED(rv))
-        decoder->mError = PR_TRUE;
-    return rv;
-}
-
-NS_IMETHODIMP nsBMPDecoder::WriteFrom(nsIInputStream *aInStr, PRUint32 aCount)
-{
-    PR_LOG(gBMPLog, PR_LOG_DEBUG, ("nsBMPDecoder::WriteFrom(%p, %lu, %p)\n", aInStr, aCount));
-
-    // Decode, watching for errors.
-    nsresult rv = NS_OK;
-    PRUint32 ignored;
-    if (!mError)
-        rv = aInStr->ReadSegments(ReadSegCb, this, aCount, &ignored);
-    if (mError || NS_FAILED(rv)) {
-        return NS_ERROR_FAILURE;
-    }
-    return NS_OK;
-}
-
 // ----------------------------------------
 // Actual Data Processing
 // ----------------------------------------
 
 static void calcBitmask(PRUint32 aMask, PRUint8& aBegin, PRUint8& aLength)
 {
     // find the rightmost 1
     PRUint8 pos;
@@ -188,36 +156,43 @@ NS_METHOD nsBMPDecoder::CalcBitShift()
     mBitFields.greenLeftShift = 8 - length;
     // blue
     calcBitmask(mBitFields.blue, begin, length);
     mBitFields.blueRightShift = begin;
     mBitFields.blueLeftShift = 8 - length;
     return NS_OK;
 }
 
-NS_METHOD nsBMPDecoder::ProcessData(const char* aBuffer, PRUint32 aCount)
+NS_IMETHODIMP
+nsBMPDecoder::Write(const char* aBuffer, PRUint32 aCount)
 {
-    PR_LOG(gBMPLog, PR_LOG_DEBUG, ("nsBMPDecoder::ProcessData(%p, %lu)", aBuffer, aCount));
-    if (!aCount || !mCurLine) // aCount=0 means EOF, mCurLine=0 means we're past end of image
+    // No forgiveness
+    if (mError)
+      return NS_ERROR_FAILURE;
+
+    // aCount=0 means EOF, mCurLine=0 means we're past end of image
+    if (!aCount || !mCurLine)
         return NS_OK;
 
     nsresult rv;
     if (mPos < BFH_LENGTH) { /* In BITMAPFILEHEADER */
         PRUint32 toCopy = BFH_LENGTH - mPos;
         if (toCopy > aCount)
             toCopy = aCount;
         memcpy(mRawBuf + mPos, aBuffer, toCopy);
         mPos += toCopy;
         aCount -= toCopy;
         aBuffer += toCopy;
     }
     if (mPos == BFH_LENGTH) {
         ProcessFileHeader();
-        if (mBFH.signature[0] != 'B' || mBFH.signature[1] != 'M')
+        if (mBFH.signature[0] != 'B' || mBFH.signature[1] != 'M') {
+            mError = PR_TRUE;
             return NS_ERROR_FAILURE;
+        }
         if (mBFH.bihsize == OS2_BIH_LENGTH)
             mLOH = OS2_HEADER_LENGTH;
     }
     if (mPos >= BFH_LENGTH && mPos < mLOH) { /* In BITMAPINFOHEADER */
         PRUint32 toCopy = mLOH - mPos;
         if (toCopy > aCount)
             toCopy = aCount;
         memcpy(mRawBuf + (mPos - BFH_LENGTH), aBuffer, toCopy);
@@ -226,24 +201,28 @@ NS_METHOD nsBMPDecoder::ProcessData(cons
         aBuffer += toCopy;
     }
     if (mPos == mLOH) {
         ProcessInfoHeader();
         PR_LOG(gBMPLog, PR_LOG_DEBUG, ("BMP image is %lix%lix%lu. compression=%lu\n",
             mBIH.width, mBIH.height, mBIH.bpp, mBIH.compression));
         // Verify we support this bit depth
         if (mBIH.bpp != 1 && mBIH.bpp != 4 && mBIH.bpp != 8 &&
-            mBIH.bpp != 16 && mBIH.bpp != 24 && mBIH.bpp != 32)
+            mBIH.bpp != 16 && mBIH.bpp != 24 && mBIH.bpp != 32) {
+          mError = PR_TRUE;
           return NS_ERROR_UNEXPECTED;
+        }
 
         // BMPs with negative width are invalid
         // Reject extremely wide images to keep the math sane
         const PRInt32 k64KWidth = 0x0000FFFF;
-        if (mBIH.width < 0 || mBIH.width > k64KWidth)
+        if (mBIH.width < 0 || mBIH.width > k64KWidth) {
+            mError = PR_TRUE;
             return NS_ERROR_FAILURE;
+        }
 
         PRUint32 real_height = (mBIH.height > 0) ? mBIH.height : -mBIH.height;
 
         // Set the size and notify
         rv = mImage->SetSize(mBIH.width, real_height);
         NS_ENSURE_SUCCESS(rv, rv);
         if (mObserver) {
             rv = mObserver->OnStartContainer(nsnull, mImage);
@@ -260,18 +239,20 @@ NS_METHOD nsBMPDecoder::ProcessData(cons
 
         if (mBIH.bpp <= 8) {
             mNumColors = 1 << mBIH.bpp;
             if (mBIH.colors && mBIH.colors < mNumColors)
                 mNumColors = mBIH.colors;
 
             // Always allocate 256 even though mNumColors might be smaller
             mColors = new colorTable[256];
-            if (!mColors)
+            if (!mColors) {
+                mError = PR_TRUE;
                 return NS_ERROR_OUT_OF_MEMORY;
+            }
 
             memset(mColors, 0, 256 * sizeof(colorTable));
         }
         else if (mBIH.compression != BI_BITFIELDS && mBIH.bpp == 16) {
             // Use default 5-5-5 format
             mBitFields.red   = 0x7C00;
             mBitFields.green = 0x03E0;
             mBitFields.blue  = 0x001F;
@@ -284,30 +265,34 @@ NS_METHOD nsBMPDecoder::ProcessData(cons
                                      (PRUint8**)&mImageData, &imageLength);
         } else {
             // mRow is not used for RLE encoded images
             mRow = (PRUint8*)malloc((mBIH.width * mBIH.bpp)/8 + 4);
             // +4 because the line is padded to a 4 bit boundary, but I don't want
             // to make exact calculations here, that's unnecessary.
             // Also, it compensates rounding error.
             if (!mRow) {
+                mError = PR_TRUE;
                 return NS_ERROR_OUT_OF_MEMORY;
             }
             rv = mImage->AppendFrame(0, 0, mBIH.width, real_height, gfxASurface::ImageFormatRGB24,
                                      (PRUint8**)&mImageData, &imageLength);
         }
         NS_ENSURE_SUCCESS(rv, rv);
-        if (!mImageData)
+        if (!mImageData) {
+            mError = PR_TRUE;
             return NS_ERROR_FAILURE;
+        }
 
         // Prepare for transparancy
         if ((mBIH.compression == BI_RLE8) || (mBIH.compression == BI_RLE4)) {
             if (((mBIH.compression == BI_RLE8) && (mBIH.bpp != 8)) 
              || ((mBIH.compression == BI_RLE4) && (mBIH.bpp != 4) && (mBIH.bpp != 1))) {
                 PR_LOG(gBMPLog, PR_LOG_DEBUG, ("BMP RLE8/RLE4 compression only supports 8/4 bits per pixel\n"));
+                mError = PR_TRUE;
                 return NS_ERROR_FAILURE;
             }
             // Clear the image, as the RLE may jump over areas
             memset(mImageData, 0, imageLength);
         }
 
         if (mObserver) {
             mObserver->OnStartFrame(nsnull, 0);
@@ -443,16 +428,17 @@ NS_METHOD nsBMPDecoder::ProcessData(cons
 
                 }
             } while (aCount > 0);
         } 
         else if ((mBIH.compression == BI_RLE8) || (mBIH.compression == BI_RLE4)) {
             if (((mBIH.compression == BI_RLE8) && (mBIH.bpp != 8)) 
              || ((mBIH.compression == BI_RLE4) && (mBIH.bpp != 4) && (mBIH.bpp != 1))) {
                 PR_LOG(gBMPLog, PR_LOG_DEBUG, ("BMP RLE8/RLE4 compression only supports 8/4 bits per pixel\n"));
+                mError = PR_TRUE;
                 return NS_ERROR_FAILURE;
             }
 
             while (aCount > 0) {
                 PRUint8 byte;
 
                 switch(mState) {
                     case eRLEStateInitial:
@@ -509,18 +495,20 @@ NS_METHOD nsBMPDecoder::ProcessData(cons
 
                             default : // absolute mode
                                 // Save the number of pixels to read
                                 mStateData = byte;
                                 if (mCurPos + mStateData > (PRUint32)mBIH.width) {
                                     // We can work around bitmaps that specify one
                                     // pixel too many, but only if their width is odd.
                                     mStateData -= mBIH.width & 1;
-                                    if (mCurPos + mStateData > (PRUint32)mBIH.width)
+                                    if (mCurPos + mStateData > (PRUint32)mBIH.width) {
+                                        mError = PR_TRUE;
                                         return NS_ERROR_FAILURE;
+                                    }
                                 }
 
                                 // See if we will need to skip a byte
                                 // to word align the pixel data
                                 // mStateData is a number of pixels
                                 // so allow for the RLE compression type
                                 // Pixels RLE8=1 RLE4=2
                                 //    1    Pad    Pad
@@ -594,16 +582,17 @@ NS_METHOD nsBMPDecoder::ProcessData(cons
                                 mState = eRLEStateInitial;
                             }
                         }
                         // else state is still eRLEStateAbsoluteMode
                         continue;
 
                     default :
                         NS_NOTREACHED("BMP RLE decompression: unknown state!");
+                        mError = PR_TRUE;
                         return NS_ERROR_FAILURE;
                 }
                 // Because of the use of the continue statement
                 // we only get here for eol, eof or y delta
                 if (mCurLine == 0) { // Finished last line
                     break;
                 }
             }
--- a/modules/libpr0n/decoders/bmp/nsBMPDecoder.h
+++ b/modules/libpr0n/decoders/bmp/nsBMPDecoder.h
@@ -148,25 +148,16 @@ class nsBMPDecoder : public imgIDecoder
 public:
     NS_DECL_ISUPPORTS
     NS_DECL_IMGIDECODER
     
     nsBMPDecoder();
     ~nsBMPDecoder();
 
 private:
-    /** Callback for ReadSegments to avoid copying the data */
-    static NS_METHOD ReadSegCb(nsIInputStream* aIn, void* aClosure,
-                               const char* aFromRawSegment, PRUint32 aToOffset,
-                               PRUint32 aCount, PRUint32 *aWriteCount);
-
-    /** Processes the data.
-     * @param aBuffer Data to process.
-     * @oaram count Number of bytes in mBuffer */
-    NS_METHOD ProcessData(const char* aBuffer, PRUint32 aCount);
 
     /** Calculates the red-, green- and blueshift in mBitFields using
      * the bitmasks from mBitFields */
     NS_METHOD CalcBitShift();
 
     nsCOMPtr<imgIDecoderObserver> mObserver;
 
     nsCOMPtr<imgIContainer> mImage;
--- a/modules/libpr0n/decoders/bmp/nsICODecoder.cpp
+++ b/modules/libpr0n/decoders/bmp/nsICODecoder.cpp
@@ -147,53 +147,30 @@ NS_IMETHODIMP nsICODecoder::Close(PRUint
   return rv;
 }
 
 NS_IMETHODIMP nsICODecoder::Flush()
 {
   return NS_OK;
 }
 
-
-NS_METHOD nsICODecoder::ReadSegCb(nsIInputStream* aIn, void* aClosure,
-                             const char* aFromRawSegment, PRUint32 aToOffset,
-                             PRUint32 aCount, PRUint32 *aWriteCount) {
-  nsICODecoder *decoder = reinterpret_cast<nsICODecoder*>(aClosure);
-
-  // Always read everything
-  *aWriteCount = aCount;
-
-  // Process
-  nsresult rv = decoder->ProcessData(aFromRawSegment, aCount);
+NS_IMETHODIMP
+nsICODecoder::Write(const char* aBuffer, PRUint32 aCount)
+{
+  // No forgiveness
+  if (mError)
+    return NS_ERROR_FAILURE;
 
-  // rvs might not propagate correctly. Set a flag before returning.
-  if (NS_FAILED(rv))
-    decoder->mError = PR_TRUE;
-  return rv;
-}
-
-NS_IMETHODIMP nsICODecoder::WriteFrom(nsIInputStream *aInStr, PRUint32 aCount)
-{
-  // Decode, watching for errors
-  nsresult rv = NS_OK;
-  PRUint32 ignored;
-  if (!mError)
-    rv = aInStr->ReadSegments(ReadSegCb, this, aCount, &ignored);
-  if (mError || NS_FAILED(rv))
-    return NS_ERROR_FAILURE;
-  return NS_OK;
-}
-
-nsresult nsICODecoder::ProcessData(const char* aBuffer, PRUint32 aCount) {
   if (!aCount) // aCount=0 means EOF
     return NS_OK;
 
   while (aCount && (mPos < ICONCOUNTOFFSET)) { // Skip to the # of icons.
     if (mPos == 2) { // if the third byte is 1: This is an icon, 2: a cursor
       if ((*aBuffer != 1) && (*aBuffer != 2)) {
+        mError = PR_TRUE;
         return NS_ERROR_FAILURE;
       }
       mIsCursor = (*aBuffer == 2);
     }
     mPos++; aBuffer++; aCount--;
   }
 
   if (mPos == ICONCOUNTOFFSET && aCount >= 2) {
@@ -226,18 +203,20 @@ nsresult nsICODecoder::ProcessData(const
       mCurrIcon++;
       ProcessDirEntry(e);
       if ((e.mWidth == PREFICONSIZE && e.mHeight == PREFICONSIZE && e.mBitCount >= colorDepth)
            || (mCurrIcon == mNumIcons && mImageOffset == 0)) {
         mImageOffset = e.mImageOffset;
 
         // ensure mImageOffset is >= the size of the direntry headers (bug #245631)
         PRUint32 minImageOffset = DIRENTRYOFFSET + mNumIcons*sizeof(mDirEntryArray);
-        if (mImageOffset < minImageOffset)
+        if (mImageOffset < minImageOffset) {
+          mError = PR_TRUE;
           return NS_ERROR_FAILURE;
+        }
 
         colorDepth = e.mBitCount;
         memcpy(&mDirEntry, &e, sizeof(IconDirEntry));
       }
     }
   }
 
   if (mPos < mImageOffset) {
@@ -284,22 +263,25 @@ nsresult nsICODecoder::ProcessData(const
           break;
         case 4:
           mNumColors = 16;
           break;
         case 8:
           mNumColors = 256;
           break;
         default:
+          mError = PR_TRUE;
           return NS_ERROR_FAILURE;
       }
 
       mColors = new colorTable[mNumColors];
-      if (!mColors)
+      if (!mColors) {
+        mError = PR_TRUE;
         return NS_ERROR_OUT_OF_MEMORY;
+      }
     }
 
     if (mIsCursor) {
       nsCOMPtr<nsIProperties> props(do_QueryInterface(mImage));
       if (props) {
         nsCOMPtr<nsISupportsPRUint32> intwrapx = do_CreateInstance("@mozilla.org/supports-PRUint32;1");
         nsCOMPtr<nsISupportsPRUint32> intwrapy = do_CreateInstance("@mozilla.org/supports-PRUint32;1");
 
@@ -313,19 +295,21 @@ nsresult nsICODecoder::ProcessData(const
       }
     }
 
     mCurLine = mDirEntry.mHeight;
     mRow = (PRUint8*)malloc((mDirEntry.mWidth * mBIH.bpp)/8 + 4);
     // +4 because the line is padded to a 4 bit boundary, but I don't want
     // to make exact calculations here, that's unnecessary.
     // Also, it compensates rounding error.
-    if (!mRow)
+    if (!mRow) {
+      mError = PR_TRUE;
       return NS_ERROR_OUT_OF_MEMORY;
-    
+    }
+
     PRUint32 imageLength;
     rv = mImage->AppendFrame(0, 0, mDirEntry.mWidth, mDirEntry.mHeight,
                              gfxASurface::ImageFormatARGB32, (PRUint8**)&mImageData, &imageLength);
     NS_ENSURE_SUCCESS(rv, rv);
 
     if (mObserver) {
       mObserver->OnStartFrame(nsnull, 0);
       NS_ENSURE_SUCCESS(rv, rv);
@@ -363,18 +347,20 @@ nsresult nsICODecoder::ProcessData(const
       // Increment mPos to avoid reprocessing the info header.
       mPos++;
     }
 
     // Ensure memory has been allocated before decoding. If we get this far 
     // without allocated memory, the file is most likely invalid.
     NS_ASSERTION(mRow, "mRow is null");
     NS_ASSERTION(mImageData, "mImageData is null");
-    if (!mRow || !mImageData)
+    if (!mRow || !mImageData) {
+      mError = PR_TRUE;
       return NS_ERROR_FAILURE;
+    }
 
     PRUint32 rowSize = (mBIH.bpp * mDirEntry.mWidth + 7) / 8; // +7 to round up
     if (rowSize % 4)
       rowSize += (4 - (rowSize % 4)); // Pad to DWORD Boundary
     PRUint32 toCopy;
     do {
         toCopy = rowSize - mRowBytes;
         if (toCopy) {
@@ -448,16 +434,17 @@ nsresult nsICODecoder::ProcessData(const
                   }                        
                   SetPixel(d, p[2], p[1], p[0], mHaveAlphaData ? p[3] : 0xFF);
                   p += 4;
                   --lpos;
                 }
                 break;
               default:
                 // This is probably the wrong place to check this...
+                mError = PR_TRUE;
                 return NS_ERROR_FAILURE;
             }
 
             if (mCurLine == 0)
               mDecodingAndMask = PR_TRUE;
               
             mRowBytes = 0;
         }
@@ -468,25 +455,29 @@ nsresult nsICODecoder::ProcessData(const
   if (mDecodingAndMask && !mHaveAlphaData) {
     PRUint32 rowSize = CalcAlphaRowSize();
 
     if (mPos == (1 + mImageOffset + BITMAPINFOSIZE + mNumColors*4)) {
       mPos++;
       mRowBytes = 0;
       mCurLine = mDirEntry.mHeight;
       mRow = (PRUint8*)realloc(mRow, rowSize);
-      if (!mRow)
+      if (!mRow) {
+        mError = PR_TRUE;
         return NS_ERROR_OUT_OF_MEMORY;
+      }
     }
 
     // Ensure memory has been allocated before decoding.
     NS_ASSERTION(mRow, "mRow is null");
     NS_ASSERTION(mImageData, "mImageData is null");
-    if (!mRow || !mImageData)
+    if (!mRow || !mImageData) {
+      mError = PR_TRUE;
       return NS_ERROR_FAILURE;
+    }
 
     while (mCurLine > 0 && aCount > 0) {
       PRUint32 toCopy = PR_MIN(rowSize - mRowBytes, aCount);
       if (toCopy) {
         memcpy(mRow + mRowBytes, aBuffer, toCopy);
         aCount -= toCopy;
         aBuffer += toCopy;
         mRowBytes += toCopy;
--- a/modules/libpr0n/decoders/bmp/nsICODecoder.h
+++ b/modules/libpr0n/decoders/bmp/nsICODecoder.h
@@ -74,23 +74,17 @@ class nsICODecoder : public imgIDecoder
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_IMGIDECODER
 
   nsICODecoder();
   virtual ~nsICODecoder();
 
 private:
-  /** Callback for ReadSegments to avoid copying the data */
-  static NS_METHOD ReadSegCb(nsIInputStream* aIn, void* aClosure,
-                             const char* aFromRawSegment, PRUint32 aToOffset,
-                             PRUint32 aCount, PRUint32 *aWriteCount);
-
   // Private helper methods
-  nsresult ProcessData(const char* aBuffer, PRUint32 aCount);
   void ProcessDirEntry(IconDirEntry& aTarget);
   void ProcessInfoHeader();
 
   nsresult SetImageData();
 
   PRUint32 CalcAlphaRowSize();
 
 private:
--- a/modules/libpr0n/decoders/gif/nsGIFDecoder2.cpp
+++ b/modules/libpr0n/decoders/gif/nsGIFDecoder2.cpp
@@ -186,59 +186,16 @@ NS_IMETHODIMP nsGIFDecoder2::Close(PRUin
 
 //******************************************************************************
 /* void flush (); */
 NS_IMETHODIMP nsGIFDecoder2::Flush()
 {
     return NS_OK;
 }
 
-//******************************************************************************
-/* static callback from nsIInputStream::ReadSegments */
-NS_METHOD nsGIFDecoder2::ReadDataOut(nsIInputStream* in,
-                                     void* closure,
-                                     const char* fromRawSegment,
-                                     PRUint32 toOffset,
-                                     PRUint32 count,
-                                     PRUint32 *writeCount)
-{
-  nsGIFDecoder2 *decoder = static_cast<nsGIFDecoder2*>(closure);
-
-  // Always read everything
-  *writeCount = count;
-
-  // Process
-  nsresult rv = decoder->ProcessData((unsigned char*)fromRawSegment, count);
-
-  // We do some fine-grained error control here. If we have at least one frame
-  // of an animated gif, we still want to display it (mostly for legacy reasons).
-  // libpr0n code is strict, so we have to lie and tell it we were successful. So
-  // if we have something to salvage, we send off final decode notifications, and
-  // pretend that we're decoded. Otherwise, we set mError.
-  if (NS_FAILED(rv)) {
-
-    // Determine if we want to salvage the situation
-    PRUint32 numFrames = 0;
-    if (decoder->mImageContainer)
-      decoder->mImageContainer->GetNumFrames(&numFrames);
-
-    // If we're salvaging, send off notifications
-    if (numFrames > 1) { // XXXbholley - this is from the old code, but why not > 0?
-      decoder->EndGIF(/* aSuccess = */ PR_TRUE);
-    }
-
-    // Otherwise, set mError
-    else
-      decoder->mError = PR_TRUE;
-  }
-
-  // Necko is dubious with callbacks, so we don't use rvs to propagate errors.
-  return NS_OK;
-}
-
 // Push any new rows according to mCurrentPass/mLastFlushedPass and
 // mCurrentRow/mLastFlushedRow.  Note: caller is responsible for
 // updating mlastFlushed{Row,Pass}.
 nsresult
 nsGIFDecoder2::FlushImageData(PRUint32 fromRow, PRUint32 rows)
 {
   nsIntRect r(0, fromRow, mGIFStruct.width, rows);
 
@@ -277,49 +234,62 @@ nsGIFDecoder2::FlushImageData()
 
     default:   // more than one pass on - push the whole frame
       rv = FlushImageData(0, mGIFStruct.height);
   }
   return rv;
 }
 
 //******************************************************************************
-nsresult nsGIFDecoder2::ProcessData(unsigned char *data, PRUint32 count)
+/* void write (in string aBuffer, in PRUint32 aCount); */
+NS_IMETHODIMP
+nsGIFDecoder2::Write(const char *aBuffer, PRUint32 aCount)
 {
+  // Don't forgive previously flagged errors
+  if (mError)
+    return NS_ERROR_FAILURE;
+
   // Push the data to the GIF decoder
-  nsresult rv = GifWrite(data, count);
-  NS_ENSURE_SUCCESS(rv, rv);
+  nsresult rv = GifWrite((const unsigned char *)aBuffer, aCount);
 
   // Flushing is only needed for first frame
-  if (!mGIFStruct.images_decoded) {
+  if (NS_SUCCEEDED(rv) && !mGIFStruct.images_decoded) {
     rv = FlushImageData();
     NS_ENSURE_SUCCESS(rv, rv);
     mLastFlushedRow = mCurrentRow;
     mLastFlushedPass = mCurrentPass;
   }
 
-  return NS_OK;
-}
+  // We do some fine-grained error control here. If we have at least one frame
+  // of an animated gif, we still want to display it (mostly for legacy reasons).
+  // libpr0n code is strict, so we have to lie and tell it we were successful. So
+  // if we have something to salvage, we send off final decode notifications, and
+  // pretend that we're decoded. Otherwise, we set mError.
+  if (NS_FAILED(rv)) {
+
+    // Determine if we want to salvage the situation
+    PRUint32 numFrames = 0;
+    if (mImageContainer)
+      mImageContainer->GetNumFrames(&numFrames);
 
-//******************************************************************************
-/* void  writeFrom (in nsIInputStream inStr, in unsigned long count); */
-NS_IMETHODIMP nsGIFDecoder2::WriteFrom(nsIInputStream *inStr, PRUint32 count)
-{
-  // Decode, watching for errors
-  nsresult rv = NS_OK;
-  PRUint32 ignored;
-  if (!mError)
-    rv = inStr->ReadSegments(nsGIFDecoder2::ReadDataOut, this,
-                             count, &ignored);
-  if (mError || NS_FAILED(rv))
-    return NS_ERROR_FAILURE;
-  return NS_OK;
+    // If we're salvaging, send off notifications
+    // Note that we need to make sure that we have 2 frames, since that tells us
+    // that the first frame is complete (the second could be in any state).
+    if (numFrames > 1) {
+      EndGIF(/* aSuccess = */ PR_TRUE);
+    }
+
+    // Otherwise, set mError
+    else
+      mError = PR_TRUE;
+  }
+
+  return mError ? NS_ERROR_FAILURE : NS_OK;
 }
 
-
 //******************************************************************************
 // GIF decoder callback methods. Part of public API for GIF2
 //******************************************************************************
 
 //******************************************************************************
 void nsGIFDecoder2::BeginGIF()
 {
   if (mGIFOpen)
--- a/modules/libpr0n/decoders/gif/nsGIFDecoder2.h
+++ b/modules/libpr0n/decoders/gif/nsGIFDecoder2.h
@@ -54,32 +54,24 @@
      0x1dd2,                                         \
      0x11b2,                                         \
     {0xa7, 0xf8, 0xca, 0x39, 0x7e, 0x01, 0x79, 0xc4} \
 }
 
 //////////////////////////////////////////////////////////////////////
 // nsGIFDecoder2 Definition
 
-class nsGIFDecoder2 : public imgIDecoder   
+class nsGIFDecoder2 : public imgIDecoder
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_IMGIDECODER
 
   nsGIFDecoder2();
   ~nsGIFDecoder2();
-  
-  nsresult ProcessData(unsigned char *data, PRUint32 count);
-  static NS_METHOD ReadDataOut(nsIInputStream* in,
-                               void* closure,
-                               const char* fromRawSegment,
-                               PRUint32 toOffset,
-                               PRUint32 count,
-                               PRUint32 *writeCount);
 
 private:
   /* These functions will be called when the decoder has a decoded row,
    * frame size information, etc. */
 
   void      BeginGIF();
   void      EndGIF(PRBool aSuccess);
   nsresult  BeginImageFrame(gfx_depth aDepth);
--- a/modules/libpr0n/decoders/icon/nsIconDecoder.cpp
+++ b/modules/libpr0n/decoders/icon/nsIconDecoder.cpp
@@ -109,115 +109,102 @@ NS_IMETHODIMP nsIconDecoder::Close(PRUin
   return NS_OK;
 }
 
 NS_IMETHODIMP nsIconDecoder::Flush()
 {
   return NS_OK;
 }
 
-static nsresult
-WriteIconData(nsIInputStream *aInStream, void *aClosure, const char *aFromSegment,
-              PRUint32 aToOffset, PRUint32 aCount, PRUint32 *aWriteCount)
+NS_IMETHODIMP
+nsIconDecoder::Write(const char *aBuffer, PRUint32 aCount)
 {
   nsresult rv;
 
-  // We always read everything
-  *aWriteCount = aCount;
-
   // We put this here to avoid errors about crossing initialization with case
   // jumps on linux.
   PRUint32 bytesToRead = 0;
 
-  // Grab the parameters
-  nsIconDecoder *decoder = static_cast<nsIconDecoder*>(aClosure);
-
   // Performance isn't critical here, so our update rectangle is 
   // always the full icon
-  nsIntRect r(0, 0, decoder->mWidth, decoder->mHeight);
+  nsIntRect r(0, 0, mWidth, mHeight);
 
   // Loop until the input data is gone
   while (aCount > 0) {
-    switch (decoder->mState) {
+    switch (mState) {
       case iconStateStart:
 
         // Grab the width
-        decoder->mWidth = (PRUint8)*aFromSegment;
+        mWidth = (PRUint8)*aBuffer;
 
         // Book Keeping
-        aFromSegment++;
+        aBuffer++;
         aCount--;
-        decoder->mState = iconStateHaveHeight;
+        mState = iconStateHaveHeight;
         break;
 
       case iconStateHaveHeight:
 
         // Grab the Height
-        decoder->mHeight = (PRUint8)*aFromSegment;
+        mHeight = (PRUint8)*aBuffer;
 
         // Set up the container and signal
-        decoder->mImage->SetSize(decoder->mWidth,
-                                 decoder->mHeight);
-        if (decoder->mObserver)
-          decoder->mObserver->OnStartContainer(nsnull, decoder->mImage);
+        mImage->SetSize(mWidth, mHeight);
+        if (mObserver)
+          mObserver->OnStartContainer(nsnull, mImage);
 
         // If We're doing a header-only decode, we're done
-        if (decoder->mFlags & imgIDecoder::DECODER_FLAG_HEADERONLY) {
-          decoder->mState = iconStateFinished;
+        if (mFlags & imgIDecoder::DECODER_FLAG_HEADERONLY) {
+          mState = iconStateFinished;
           break;
         }
 
         // Add the frame and signal
-        rv = decoder->mImage->AppendFrame(0, 0,
-                                          decoder->mWidth,
-                                          decoder->mHeight,
-                                          gfxASurface::ImageFormatARGB32,
-                                          &decoder->mImageData, 
-                                          &decoder->mPixBytesTotal);
+        rv = mImage->AppendFrame(0, 0, mWidth, mHeight,
+                                 gfxASurface::ImageFormatARGB32,
+                                 &mImageData, &mPixBytesTotal);
         if (NS_FAILED(rv)) {
-          decoder->mState = iconStateError;
+          mState = iconStateError;
           return rv;
         }
-        if (decoder->mObserver)
-          decoder->mObserver->OnStartFrame(nsnull, 0);
+        if (mObserver)
+         mObserver->OnStartFrame(nsnull, 0);
 
         // Book Keeping
-        aFromSegment++;
+        aBuffer++;
         aCount--;
-        decoder->mState = iconStateReadPixels;
+        mState = iconStateReadPixels;
         break;
 
       case iconStateReadPixels:
 
         // How many bytes are we reading?
-        bytesToRead = PR_MAX(aCount,
-                             decoder->mPixBytesTotal - decoder->mPixBytesRead);
+        bytesToRead = PR_MAX(aCount, mPixBytesTotal - mPixBytesRead);
 
         // Copy the bytes
-        memcpy(decoder->mImageData + decoder->mPixBytesRead,
-               aFromSegment, bytesToRead);
+        memcpy(mImageData + mPixBytesRead, aBuffer, bytesToRead);
 
         // Notify
-        rv = decoder->mImage->FrameUpdated(0, r);
+        rv = mImage->FrameUpdated(0, r);
         if (NS_FAILED(rv)) {
-          decoder->mState = iconStateError;
+          mState = iconStateError;
           return rv;
         }
-        if (decoder->mObserver)
-          decoder->mObserver->OnDataAvailable(nsnull, PR_TRUE, &r);
+        if (mObserver)
+          mObserver->OnDataAvailable(nsnull, PR_TRUE, &r);
 
         // Book Keeping
-        aFromSegment += bytesToRead;
+        aBuffer += bytesToRead;
         aCount -= bytesToRead;
-        decoder->mPixBytesRead += bytesToRead;
+        mPixBytesRead += bytesToRead;
 
         // If we've got all the pixel bytes, we're finished
-        if (decoder->mPixBytesRead == decoder->mPixBytesTotal) {
-          decoder->NotifyDone(/* aSuccess = */ PR_TRUE);
-          decoder->mState = iconStateFinished;
+        if (mPixBytesRead == mPixBytesTotal) {
+          NotifyDone(/* aSuccess = */ PR_TRUE);
+          mState = iconStateFinished;
         }
         break;
 
       case iconStateFinished:
 
         // Consume all excess data silently
         aCount = 0;
 
@@ -248,21 +235,8 @@ nsIconDecoder::NotifyDone(PRBool aSucces
     mObserver->OnStopDecode(nsnull, aSuccess ? NS_OK : NS_ERROR_FAILURE,
                             nsnull);
   }
 
   // Flag that we've notified
   mNotifiedDone = PR_TRUE;
 }
 
-
-NS_IMETHODIMP nsIconDecoder::WriteFrom(nsIInputStream *inStr, PRUint32 count)
-{
-  // Decode, watching for errors.
-  nsresult rv = NS_OK;
-  PRUint32 ignored;
-  if (mState != iconStateError)
-    rv = inStr->ReadSegments(WriteIconData, this, count, &ignored);
-  if ((mState == iconStateError) || NS_FAILED(rv))
-    return NS_ERROR_FAILURE;
-  return NS_OK;
-}
-
--- a/modules/libpr0n/decoders/jpeg/nsJPEGDecoder.cpp
+++ b/modules/libpr0n/decoders/jpeg/nsJPEGDecoder.cpp
@@ -96,17 +96,16 @@ static void cmyk_convert_rgb(JSAMPROW ro
 #define MAX_JPEG_MARKER_LENGTH  (((PRUint32)1 << 16) - 1)
 
 
 nsJPEGDecoder::nsJPEGDecoder()
 {
   mState = JPEG_HEADER;
   mReading = PR_TRUE;
   mNotifiedDone = PR_FALSE;
-  mError = PR_FALSE;
   mImageData = nsnull;
 
   mBytesToSkip = 0;
   memset(&mInfo, 0, sizeof(jpeg_decompress_struct));
   memset(&mSourceMgr, 0, sizeof(mSourceMgr));
   mInfo.client_data = (void*)this;
 
   mSegment = nsnull;
@@ -198,17 +197,17 @@ NS_IMETHODIMP nsJPEGDecoder::Close(PRUin
 
   /* Step 8: Release JPEG decompression object */
   mInfo.src = nsnull;
 
   jpeg_destroy_decompress(&mInfo);
 
   /* If we already know we're in an error state, don't
      bother flagging another one here. */
-  if (mError)
+  if (mState == JPEG_ERROR)
     return NS_OK;
 
   /* If we're doing a full decode and haven't notified of completion yet,
    * we must not have got everything we wanted. Send error notifications. */
   if (!(aFlags & CLOSE_FLAG_DONTNOTIFY) &&
       !(mFlags && imgIDecoder::DECODER_FLAG_HEADERONLY) &&
       !mNotifiedDone)
     NotifyDone(/* aSuccess = */ PR_FALSE);
@@ -218,93 +217,55 @@ NS_IMETHODIMP nsJPEGDecoder::Close(PRUin
 }
 
 /* void flush (); */
 NS_IMETHODIMP nsJPEGDecoder::Flush()
 {
   LOG_SCOPE(gJPEGlog, "nsJPEGDecoder::Flush");
 
   if (mState != JPEG_DONE && mState != JPEG_SINK_NON_JPEG_TRAILER && mState != JPEG_ERROR)
-    return this->ProcessData(nsnull, 0);
+    return this->Write(nsnull, 0);
 
   return NS_OK;
 }
 
-static NS_METHOD ReadDataOut(nsIInputStream* in,
-                             void* closure,
-                             const char* fromRawSegment,
-                             PRUint32 toOffset,
-                             PRUint32 count,
-                             PRUint32 *writeCount)
+//******************************************************************************
+nsresult nsJPEGDecoder::Write(const char *aBuffer, PRUint32 aCount)
 {
-  nsJPEGDecoder *decoder = static_cast<nsJPEGDecoder*>(closure);
-
-  // We always read everything
-  *writeCount = count;
-
-  // Process some data
-  nsresult rv = decoder->ProcessData(fromRawSegment, count);
-
-  // Necko's error propagation is dubious - use an explicit flag
-  if (NS_FAILED(rv))
-    decoder->mError = rv;
-  return rv;
-}
-
+  mSegment = (const JOCTET *)aBuffer;
+  mSegmentLen = aCount;
 
-/* void writeFrom (in nsIInputStream inStr, in unsigned long count); */
-NS_IMETHODIMP nsJPEGDecoder::WriteFrom(nsIInputStream *inStr, PRUint32 count)
-{
-  NS_ENSURE_ARG_POINTER(inStr);
-
-  // Decode, watching for errors
-  nsresult rv = NS_OK;
-  PRUint32 ignored;
-  if (!mError)
-    rv = inStr->ReadSegments(ReadDataOut, this, count, &ignored);
-  if (mError || NS_FAILED(rv))
-    return NS_ERROR_FAILURE;
-  return NS_OK;
-}
-
-//******************************************************************************
-nsresult nsJPEGDecoder::ProcessData(const char *data, PRUint32 count)
-{
-  LOG_SCOPE_WITH_PARAM(gJPEGlog, "nsJPEGDecoder::ProcessData", "count", count);
-
-  mSegment = (const JOCTET *)data;
-  mSegmentLen = count;
-
-  /* Return here if there is a fatal error. */
+  /* Return here if there is a fatal error within libjpeg. */
   nsresult error_code;
   if ((error_code = setjmp(mErr.setjmp_buffer)) != 0) {
-    mState = JPEG_SINK_NON_JPEG_TRAILER;
     if (error_code == NS_ERROR_FAILURE) {
-      /* Error due to corrupt stream - return NS_OK so that libpr0n
-         doesn't throw away a partial image load */
+      /* Error due to corrupt stream - return NS_OK and consume silently
+         so that libpr0n doesn't throw away a partial image load */
+      mState = JPEG_SINK_NON_JPEG_TRAILER;
       PR_LOG(gJPEGDecoderAccountingLog, PR_LOG_DEBUG,
              ("} (setjmp returned NS_ERROR_FAILURE)"));
       return NS_OK;
     } else {
       /* Error due to reasons external to the stream (probably out of
          memory) - let libpr0n attempt to clean up, even though
          mozilla is seconds away from falling flat on its face. */
+      mState = JPEG_ERROR;
       PR_LOG(gJPEGDecoderAccountingLog, PR_LOG_DEBUG,
              ("} (setjmp returned an error)"));
       return error_code;
     }
   }
 
   PR_LOG(gJPEGlog, PR_LOG_DEBUG,
-         ("[this=%p] nsJPEGDecoder::ProcessData -- processing JPEG data\n", this));
+         ("[this=%p] nsJPEGDecoder::Write -- processing JPEG data\n", this));
 
   switch (mState) {
   case JPEG_HEADER:
   {
-    LOG_SCOPE(gJPEGlog, "nsJPEGDecoder::ProcessData -- entering JPEG_HEADER case");
+    LOG_SCOPE(gJPEGlog, "nsJPEGDecoder::Write -- entering JPEG_HEADER case");
 
     /* Step 3: read file parameters with jpeg_read_header() */
     if (jpeg_read_header(&mInfo, TRUE) == JPEG_SUSPENDED) {
       PR_LOG(gJPEGDecoderAccountingLog, PR_LOG_DEBUG,
              ("} (JPEG_SUSPENDED)"));
       return NS_OK; /* I/O suspension */
     }
 
@@ -448,27 +409,27 @@ nsresult nsJPEGDecoder::ProcessData(cons
                                            &mImageData, &imagelength))) {
       mState = JPEG_ERROR;
       PR_LOG(gJPEGDecoderAccountingLog, PR_LOG_DEBUG,
              ("} (could not initialize image frame)"));
       return NS_ERROR_OUT_OF_MEMORY;
     }
 
     PR_LOG(gJPEGDecoderAccountingLog, PR_LOG_DEBUG,
-           ("        JPEGDecoderAccounting: nsJPEGDecoder::ProcessData -- created image frame with %ux%u pixels",
+           ("        JPEGDecoderAccounting: nsJPEGDecoder::Write -- created image frame with %ux%u pixels",
             mInfo.image_width, mInfo.image_height));
 
     if (mObserver)
       mObserver->OnStartFrame(nsnull, 0);
     mState = JPEG_START_DECOMPRESS;
   }
 
   case JPEG_START_DECOMPRESS:
   {
-    LOG_SCOPE(gJPEGlog, "nsJPEGDecoder::ProcessData -- entering JPEG_START_DECOMPRESS case");
+    LOG_SCOPE(gJPEGlog, "nsJPEGDecoder::Write -- entering JPEG_START_DECOMPRESS case");
     /* Step 4: set parameters for decompression */
 
     /* FIXME -- Should reset dct_method and dither mode
      * for final pass of progressive JPEG
      */
     mInfo.dct_method =  JDCT_ISLOW;
     mInfo.dither_mode = JDITHER_FS;
     mInfo.do_fancy_upsampling = TRUE;
@@ -493,17 +454,17 @@ nsresult nsJPEGDecoder::ProcessData(cons
     /* If this is a progressive JPEG ... */
     mState = mInfo.buffered_image ? JPEG_DECOMPRESS_PROGRESSIVE : JPEG_DECOMPRESS_SEQUENTIAL;
   }
 
   case JPEG_DECOMPRESS_SEQUENTIAL:
   {
     if (mState == JPEG_DECOMPRESS_SEQUENTIAL)
     {
-      LOG_SCOPE(gJPEGlog, "nsJPEGDecoder::ProcessData -- JPEG_DECOMPRESS_SEQUENTIAL case");
+      LOG_SCOPE(gJPEGlog, "nsJPEGDecoder::Write -- JPEG_DECOMPRESS_SEQUENTIAL case");
       
       PRBool suspend;
       nsresult rv = OutputScanlines(&suspend);
       if (NS_FAILED(rv))
         return rv;
       
       if (suspend) {
         PR_LOG(gJPEGDecoderAccountingLog, PR_LOG_DEBUG,
@@ -516,17 +477,17 @@ nsresult nsJPEGDecoder::ProcessData(cons
       mState = JPEG_DONE;
     }
   }
 
   case JPEG_DECOMPRESS_PROGRESSIVE:
   {
     if (mState == JPEG_DECOMPRESS_PROGRESSIVE)
     {
-      LOG_SCOPE(gJPEGlog, "nsJPEGDecoder::ProcessData -- JPEG_DECOMPRESS_PROGRESSIVE case");
+      LOG_SCOPE(gJPEGlog, "nsJPEGDecoder::Write -- JPEG_DECOMPRESS_PROGRESSIVE case");
 
       int status;
       do {
         status = jpeg_consume_input(&mInfo);
       } while ((status != JPEG_SUSPENDED) &&
                (status != JPEG_REACHED_EOI));
 
       for (;;) {
@@ -608,18 +569,17 @@ nsresult nsJPEGDecoder::ProcessData(cons
     PR_LOG(gJPEGlog, PR_LOG_DEBUG,
            ("[this=%p] nsJPEGDecoder::ProcessData -- entering JPEG_SINK_NON_JPEG_TRAILER case\n", this));
 
     break;
 
   case JPEG_ERROR:
     PR_LOG(gJPEGlog, PR_LOG_DEBUG,
            ("[this=%p] nsJPEGDecoder::ProcessData -- entering JPEG_ERROR case\n", this));
-
-    break;
+    return NS_ERROR_FAILURE;
   }
 
   PR_LOG(gJPEGDecoderAccountingLog, PR_LOG_DEBUG,
          ("} (end of function)"));
   return NS_OK;
 }
 
 void
@@ -950,19 +910,20 @@ fill_input_buffer (j_decompress_ptr jd)
  * data has been read to clean up JPEG source manager. NOT called by 
  * jpeg_abort() or jpeg_destroy().
  */
 METHODDEF(void)
 term_source (j_decompress_ptr jd)
 {
   nsJPEGDecoder *decoder = (nsJPEGDecoder *)(jd->client_data);
 
-  // This function shouldn't be called if we ran into an error
-  NS_ABORT_IF_FALSE(!decoder->mError,
-                    "Calling term_source on a JPEG with mError=true!");
+  // This function shouldn't be called if we ran into an error we didn't
+  // recover from.
+  NS_ABORT_IF_FALSE(decoder->mState != JPEG_ERROR,
+                    "Calling term_source on a JPEG with mState == JPEG_ERROR!");
 
   // Notify
   decoder->NotifyDone(/* aSuccess = */ PR_TRUE);
 }
 
 
 /**************** YCbCr -> Cairo's RGB24/ARGB32 conversion: most common case **************/
 
--- a/modules/libpr0n/decoders/jpeg/nsJPEGDecoder.h
+++ b/modules/libpr0n/decoders/jpeg/nsJPEGDecoder.h
@@ -85,34 +85,32 @@ class nsJPEGDecoder : public imgIDecoder
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_IMGIDECODER
 
   nsJPEGDecoder();
   virtual ~nsJPEGDecoder();
 
-  nsresult  ProcessData(const char *data, PRUint32 count);
   void NotifyDone(PRBool aSuccess);
 
 protected:
   nsresult OutputScanlines(PRBool* suspend);
 
 public:
   nsCOMPtr<imgIContainer> mImage;
   nsCOMPtr<imgIDecoderObserver> mObserver;
 
   PRUint32 mFlags;
   PRUint8 *mImageData;
 
   struct jpeg_decompress_struct mInfo;
   struct jpeg_source_mgr mSourceMgr;
   decoder_error_mgr mErr;
   jstate mState;
-  PRBool mError;
 
   PRUint32 mBytesToSkip;
 
   const JOCTET *mSegment;   // The current segment we are decoding from
   PRUint32 mSegmentLen;     // amount of data in mSegment
 
   JOCTET *mBackBuffer;
   PRUint32 mBackBufferLen; // Offset of end of active backtrack data
--- a/modules/libpr0n/decoders/png/nsPNGDecoder.cpp
+++ b/modules/libpr0n/decoders/png/nsPNGDecoder.cpp
@@ -316,19 +316,18 @@ NS_IMETHODIMP nsPNGDecoder::Close(PRUint
 }
 
 /* void flush (); */
 NS_IMETHODIMP nsPNGDecoder::Flush()
 {
   return NS_OK;
 }
 
-// We make this a method to get the benefit of the 'this' parameter
-NS_METHOD
-nsPNGDecoder::ProcessData(unsigned char* aBuffer, PRUint32 aCount)
+NS_IMETHODIMP
+nsPNGDecoder::Write(const char *aBuffer, PRUint32 aCount)
 {
   // We use gotos, so we need to declare variables here
   nsresult rv;
   PRUint32 width = 0;
   PRUint32 height = 0;
 
   // No forgiveness if we previously hit an error
   if (mError)
@@ -377,17 +376,17 @@ nsPNGDecoder::ProcessData(unsigned char*
 
     // libpng uses setjmp/longjmp for error handling - set the buffer
     if (setjmp(mPNG->jmpbuf)) {
       png_destroy_read_struct(&mPNG, &mInfo, NULL);
       goto error;
     }
 
     // Pass the data off to libpng
-    png_process_data(mPNG, mInfo, aBuffer, aCount);
+    png_process_data(mPNG, mInfo, (unsigned char *)aBuffer, aCount);
 
   }
 
   return NS_OK;
 
   // Consolidate error handling
   error:
   mError = PR_TRUE;
@@ -410,53 +409,16 @@ nsPNGDecoder::NotifyDone(PRBool aSuccess
     mObserver->OnStopDecode(nsnull, aSuccess ? NS_OK : NS_ERROR_FAILURE,
                             nsnull);
   }
 
   // Mark that we've been called
   mNotifiedDone = PR_TRUE;
 }
 
-static NS_METHOD ReadDataOut(nsIInputStream* in,
-                             void* closure,
-                             const char* fromRawSegment,
-                             PRUint32 toOffset,
-                             PRUint32 count,
-                             PRUint32 *writeCount)
-{
-  // Grab the decoder
-  NS_ENSURE_ARG_POINTER(closure);
-  nsPNGDecoder *decoder = static_cast<nsPNGDecoder*>(closure);
-
-  // We always read everything
-  *writeCount = count;
-
-  // Twiddle the types, then process the data
-  char *unConst = const_cast<char *>(fromRawSegment);
-  unsigned char *buffer = reinterpret_cast<unsigned char *>(unConst);
-  return decoder->ProcessData(buffer, count);
-}
-
-
-/* writeFrom (in nsIInputStream inStr, in unsigned long count); */
-NS_IMETHODIMP nsPNGDecoder::WriteFrom(nsIInputStream *inStr, PRUint32 count)
-{
-  NS_ASSERTION(inStr, "Got a null input stream!");
-
-  // Decode, watching for errors
-  nsresult rv = NS_OK;
-  PRUint32 ignored;
-  if (!mError)
-    rv = inStr->ReadSegments(ReadDataOut, this, count, &ignored);
-  if (mError || NS_FAILED(rv))
-    rv = NS_ERROR_FAILURE;
-
-  return rv;
-}
-
 // Sets up gamma pre-correction in libpng before our callback gets called. 
 // We need to do this if we don't end up with a CMS profile.
 static void
 PNGDoGammaCorrection(png_structp png_ptr, png_infop info_ptr)
 {
   double aGamma;
 
   if (png_get_gAMA(png_ptr, info_ptr, &aGamma)) {
--- a/modules/libpr0n/decoders/png/nsPNGDecoder.h
+++ b/modules/libpr0n/decoders/png/nsPNGDecoder.h
@@ -71,17 +71,16 @@ public:
   virtual ~nsPNGDecoder();
 
   void CreateFrame(png_uint_32 x_offset, png_uint_32 y_offset, 
                    PRInt32 width, PRInt32 height, 
                    gfxASurface::gfxImageFormat format);
   void SetAnimFrameInfo();
   
   void EndImageFrame();
-  NS_METHOD ProcessData(unsigned char* aBuffer, PRUint32 aCount);
   void NotifyDone(PRBool aSuccess);
 
 public:
   nsCOMPtr<imgIContainer> mImage;
   nsCOMPtr<imgIDecoderObserver> mObserver;
   PRUint32 mFlags;
 
   png_structp mPNG;
--- a/modules/libpr0n/public/imgIDecoder.idl
+++ b/modules/libpr0n/public/imgIDecoder.idl
@@ -94,30 +94,26 @@ interface imgIDecoder : nsISupports
   void close(in PRUint32 aFlags);
 
   /**
    * Flushes the stream.
    */
   void flush();
 
   /**
-   * Writes data into the stream from an input stream.
+   * Writes data to the decoder.
    *
    * For Header-Only decodes, OnStartContainer is the only notification
-   * fired.
+   * fired. It is an error to write any more data to the decoder for header-only
+   * decodes after SetSize() has been called.
    *
    * If a decoding error occurs, an internal flag is set and an error is
-   * returned. Each subsequent call to writeFrom will fail immediately
+   * returned. Each subsequent call to write will fail immediately
    * for the lifetime of the decoder. Shutdown notifications of the OnStopX
    * variety, as well as DecodingComplete(), are guaranteed not to be called
    * if a decoding error occurs.
    *
-   * Implementer's note: This method is defined by this interface in order
-   * to allow the output stream to efficiently copy the data from the input
-   * stream into its internal buffer (if any). If this method was provide
-   * as an external facility, a separate char* buffer would need to be used
-   * in order to call the output stream's other Write method.
-   * @param fromStream the stream from which the data is read
-   * @param count the maximun number of bytes to write
+   * @param aBuffer buffer containing the data to be written
+   * @param aCount the number of bytes to write
    */
-  void writeFrom(in nsIInputStream inStr, in unsigned long count);
+  void write(in string aBuffer, in unsigned long count);
 
 };
--- a/modules/libpr0n/src/imgContainer.cpp
+++ b/modules/libpr0n/src/imgContainer.cpp
@@ -151,17 +151,16 @@ imgContainer::imgContainer() :
   mLockCount(0),
   mDiscardTimer(nsnull),
   mHasSourceData(PR_FALSE),
   mDecoded(PR_FALSE),
   mHasBeenDecoded(PR_FALSE),
   mDecoder(nsnull),
   mWorker(nsnull),
   mBytesDecoded(0),
-  mDecoderInput(nsnull),
   mDecoderFlags(imgIDecoder::DECODER_FLAG_NONE),
   mWorkerPending(PR_FALSE),
   mInDecoder(PR_FALSE),
   mError(PR_FALSE)
 {
   // Statistics
   num_containers++;
 }
@@ -2067,24 +2066,16 @@ imgContainer::InitDecoder (PRUint32 dFla
   // Store the flags for this decoder
   mDecoderFlags = dFlags;
 
   // Initialize the decoder
   nsCOMPtr<imgIDecoderObserver> observer(do_QueryReferent(mObserver));
   nsresult result = mDecoder->Init(this, observer, dFlags);
   CONTAINER_ENSURE_SUCCESS(result);
 
-  // Create an nsIInputStream for the data. Because nsIStringInputStreams don't
-  // like their dependent data to grow dynamically, we reset the stream to the
-  // proper buffer each time we write data to the decoder. Nevertheless, it's
-  // worth keeping the structure around to avoid needless construction and
-  // destruction.
-  mDecoderInput = do_CreateInstance("@mozilla.org/io/string-input-stream;1");
-  CONTAINER_ENSURE_TRUE(mDecoderInput, NS_ERROR_OUT_OF_MEMORY);
-
   // Create a decode worker
   mWorker = new imgDecodeWorker(this);
   CONTAINER_ENSURE_TRUE(mWorker, NS_ERROR_OUT_OF_MEMORY);
 
   return NS_OK;
 }
 
 // Flushes, closes, and nulls-out a decoder. Cleans up any related decoding
@@ -2133,19 +2124,16 @@ imgContainer::ShutdownDecoder(eShutdownI
   // null out the decoder, _then_ check for errors on the close (otherwise the
   // error routine might re-invoke ShutdownDecoder)
   mDecoder = nsnull;
   if (NS_FAILED(rv)) {
     DoError();
     return rv;
   }
 
-  // Get rid of the stream
-  mDecoderInput = nsnull;
-
   // Kill off the worker
   mWorker = nsnull;
 
   // We just shut down the decoder. If we didn't get what we want, but expected
   // to, flag an error
   PRBool failed = PR_FALSE;
   if ((mDecoderFlags & imgIDecoder::DECODER_FLAG_HEADERONLY) && !mHasSize)
     failed = PR_TRUE;
@@ -2160,31 +2148,26 @@ imgContainer::ShutdownDecoder(eShutdownI
   mDecoderFlags = imgIDecoder::DECODER_FLAG_NONE;
 
   // Reset number of decoded bytes
   mBytesDecoded = 0;
 
   return NS_OK;
 }
 
-// Wraps a shared stream around the data and passes the stream to the decoder,
-// updating the total number of bytes written.
+// Writes the data to the decoder, updating the total number of bytes written.
 nsresult
 imgContainer::WriteToDecoder(const char *aBuffer, PRUint32 aCount)
 {
   // We should have a decoder
   NS_ABORT_IF_FALSE(mDecoder, "Trying to write to null decoder!");
 
-  // Wrap a shared stream around the data
-  nsresult rv = mDecoderInput->ShareData(aBuffer, aCount);
-  CONTAINER_ENSURE_SUCCESS(rv);
-
   // Write
   mInDecoder = PR_TRUE;
-  rv = mDecoder->WriteFrom(mDecoderInput, aCount);
+  nsresult rv = mDecoder->Write(aBuffer, aCount);
   mInDecoder = PR_FALSE;
   CONTAINER_ENSURE_SUCCESS(rv);
 
   // Keep track of the total number of bytes written over the lifetime of the
   // decoder
   mBytesDecoded += aCount;
 
   return NS_OK;
--- a/modules/libpr0n/src/imgContainer.h
+++ b/modules/libpr0n/src/imgContainer.h
@@ -56,17 +56,16 @@
 #include "nsCOMArray.h"
 #include "nsCOMPtr.h"
 #include "imgIContainer.h"
 #include "imgIDecoder.h"
 #include "nsIProperties.h"
 #include "nsITimer.h"
 #include "nsWeakReference.h"
 #include "nsTArray.h"
-#include "nsIStringStream.h"
 #include "imgFrame.h"
 #include "nsThreadUtils.h"
 
 #define NS_IMGCONTAINER_CID \
 { /* c76ff2c1-9bf6-418a-b143-3340c00112f7 */         \
      0x376ff2c1,                                     \
      0x9bf6,                                         \
      0x418a,                                         \
@@ -330,17 +329,16 @@ private: // data
   PRBool                     mHasBeenDecoded;
 
   friend class imgDecodeWorker;
 
   // Decoder and friends
   nsCOMPtr<imgIDecoder>          mDecoder;
   nsRefPtr<imgDecodeWorker>      mWorker;
   PRUint32                       mBytesDecoded;
-  nsCOMPtr<nsIStringInputStream> mDecoderInput;
   PRUint32                       mDecoderFlags;
   PRBool                         mWorkerPending;
   PRBool                         mInDecoder;
 
   // Error handling
   PRBool                         mError;
 
   // Discard code