Bug 821023 - imgStatusTracker should not know about imgRequest. r=joe
authorSeth Fowler <seth@mozilla.com>
Fri, 14 Dec 2012 15:42:18 -0800
changeset 125330 38889f86a465d060fb69f943269aa253fa0ba3af
parent 125329 bb7ffa195fa5611685fd7a56f358ef654b1ef0b9
child 125331 9637f34d0df031ef90eff67f01658b7f50d77b7f
push id2151
push userlsblakk@mozilla.com
push dateTue, 19 Feb 2013 18:06:57 +0000
treeherdermozilla-beta@4952e88741ec [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjoe
bugs821023
milestone20.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 821023 - imgStatusTracker should not know about imgRequest. r=joe
image/src/Image.cpp
image/src/Image.h
image/src/ImageFactory.cpp
image/src/ImageFactory.h
image/src/Makefile.in
image/src/RasterImage.cpp
image/src/RasterImage.h
image/src/VectorImage.cpp
image/src/VectorImage.h
image/src/imgLoader.cpp
image/src/imgRequest.cpp
image/src/imgRequest.h
image/src/imgRequestProxy.cpp
image/src/imgRequestProxy.h
image/src/imgStatusTracker.cpp
image/src/imgStatusTracker.h
--- a/image/src/Image.cpp
+++ b/image/src/Image.cpp
@@ -4,29 +4,30 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "Image.h"
 
 namespace mozilla {
 namespace image {
 
 // Constructor
-Image::Image(imgStatusTracker* aStatusTracker) :
+Image::Image(imgStatusTracker* aStatusTracker, nsIURI* aURI) :
   mInnerWindowId(0),
+  mURI(aURI),
   mAnimationConsumers(0),
   mAnimationMode(kNormalAnimMode),
   mInitialized(false),
   mAnimating(false),
   mError(false)
 {
   if (aStatusTracker) {
     mStatusTracker = aStatusTracker;
     mStatusTracker->SetImage(this);
   } else {
-    mStatusTracker = new imgStatusTracker(this, nullptr);
+    mStatusTracker = new imgStatusTracker(this);
   }
 }
 
 uint32_t
 Image::SizeOfData()
 {
   if (mError)
     return 0;
--- a/image/src/Image.h
+++ b/image/src/Image.h
@@ -3,16 +3,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef MOZILLA_IMAGELIB_IMAGE_H_
 #define MOZILLA_IMAGELIB_IMAGE_H_
 
 #include "imgIContainer.h"
 #include "imgStatusTracker.h"
+#include "nsIURI.h"
 #include "nsIRequest.h"
 #include "nsIInputStream.h"
 
 namespace mozilla {
 namespace image {
 
 class Image : public imgIContainer
 {
@@ -44,17 +45,16 @@ public:
    * Creates a new image container.
    *
    * @param aObserver Observer to send decoder and animation notifications to.
    * @param aMimeType The mimetype of the image.
    * @param aFlags Initialization flags of the INIT_FLAG_* variety.
    */
   virtual nsresult Init(imgIDecoderObserver* aObserver,
                         const char* aMimeType,
-                        const char* aURIString,
                         uint32_t aFlags) = 0;
 
   /**
    * The rectangle defining the location and size of the currently displayed
    * frame.
    */
   virtual void GetCurrentFrameRect(nsIntRect& aRect) = 0;
 
@@ -126,18 +126,20 @@ public:
   void SetInnerWindowID(uint64_t aInnerWindowId) {
     mInnerWindowId = aInnerWindowId;
   }
   uint64_t InnerWindowID() const { return mInnerWindowId; }
 
   bool HasError()    { return mError; }
   void SetHasError() { mError = true; }
 
+  nsIURI* GetURI() { return mURI; }
+
 protected:
-  Image(imgStatusTracker* aStatusTracker);
+  Image(imgStatusTracker* aStatusTracker, nsIURI* aURI);
 
   // Shared functionality for implementors of imgIContainer. Every
   // implementation of attribute animationMode should forward here.
   nsresult GetAnimationModeInternal(uint16_t *aAnimationMode);
   nsresult SetAnimationModeInternal(uint16_t aAnimationMode);
 
   /**
    * Decides whether animation should or should not be happening,
@@ -146,17 +148,18 @@ protected:
   virtual void EvaluateAnimation();
 
   virtual nsresult StartAnimation() = 0;
   virtual nsresult StopAnimation() = 0;
 
   uint64_t mInnerWindowId;
 
   // Member data shared by all implementations of this abstract class
-  nsAutoPtr<imgStatusTracker> mStatusTracker;
+  nsRefPtr<imgStatusTracker>  mStatusTracker;
+  nsCOMPtr<nsIURI>            mURI;
   uint32_t                    mAnimationConsumers;
   uint16_t                    mAnimationMode;   // Enum values in imgIContainer
   bool                        mInitialized:1;   // Have we been initalized?
   bool                        mAnimating:1;     // Are we currently animating?
   bool                        mError:1;         // Error handling
 
   /**
    * Extended by child classes, if they have additional
--- a/image/src/ImageFactory.cpp
+++ b/image/src/ImageFactory.cpp
@@ -3,16 +3,17 @@
  * 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 "mozilla/Preferences.h"
 #include "mozilla/Likely.h"
 
 #include "nsIHttpChannel.h"
+#include "nsSimpleURI.h"
 
 #include "RasterImage.h"
 #include "VectorImage.h"
 
 #include "ImageFactory.h"
 
 namespace mozilla {
 namespace image {
@@ -75,38 +76,30 @@ ComputeImageFlags(nsIURI* uri, bool isMu
 /* static */ already_AddRefed<Image>
 ImageFactory::CreateImage(nsIRequest* aRequest,
                           imgStatusTracker* aStatusTracker,
                           const nsCString& aMimeType,
                           nsIURI* aURI,
                           bool aIsMultiPart,
                           uint32_t aInnerWindowId)
 {
-  nsresult rv;
-
   // Register our pref observers if we haven't yet.
   if (MOZ_UNLIKELY(!gInitializedPrefCaches))
     InitPrefCaches();
 
-  // Get the image's URI string.
-  nsAutoCString uriString;
-  rv = aURI ? aURI->GetSpec(uriString) : NS_ERROR_FAILURE;
-  if (NS_FAILED(rv))
-    uriString.Assign("<unknown image URI>");
-
   // Compute the image's initialization flags.
   uint32_t imageFlags = ComputeImageFlags(aURI, aIsMultiPart);
 
   // Select the type of image to create based on MIME type.
   if (aMimeType.Equals(SVG_MIMETYPE)) {
     return CreateVectorImage(aRequest, aStatusTracker, aMimeType,
-                             uriString, imageFlags, aInnerWindowId);
+                             aURI, imageFlags, aInnerWindowId);
   } else {
     return CreateRasterImage(aRequest, aStatusTracker, aMimeType,
-                             uriString, imageFlags, aInnerWindowId);
+                             aURI, imageFlags, aInnerWindowId);
   }
 }
 
 // Marks an image as having an error before returning it. Used with macros like
 // NS_ENSURE_SUCCESS, since we guarantee to always return an image even if an
 // error occurs, but callers need to be able to tell that this happened.
 template <typename T>
 static already_AddRefed<Image>
@@ -118,37 +111,37 @@ BadImage(nsRefPtr<T>& image)
 
 /* static */ already_AddRefed<Image>
 ImageFactory::CreateAnonymousImage(const nsCString& aMimeType)
 {
   nsresult rv;
 
   nsRefPtr<RasterImage> newImage = new RasterImage();
 
-  rv = newImage->Init(nullptr, aMimeType.get(), "<unknown>", Image::INIT_FLAG_NONE);
+  rv = newImage->Init(nullptr, aMimeType.get(), Image::INIT_FLAG_NONE);
   NS_ENSURE_SUCCESS(rv, BadImage(newImage));
 
   return newImage.forget();
 }
 
 /* static */ already_AddRefed<Image>
 
 ImageFactory::CreateRasterImage(nsIRequest* aRequest,
                                 imgStatusTracker* aStatusTracker,
                                 const nsCString& aMimeType,
-                                const nsCString& aURIString,
+                                nsIURI* aURI,
                                 uint32_t aImageFlags,
                                 uint32_t aInnerWindowId)
 {
   nsresult rv;
 
-  nsRefPtr<RasterImage> newImage = new RasterImage(aStatusTracker);
+  nsRefPtr<RasterImage> newImage = new RasterImage(aStatusTracker, aURI);
 
   rv = newImage->Init(aStatusTracker->GetDecoderObserver(),
-                      aMimeType.get(), aURIString.get(), aImageFlags);
+                      aMimeType.get(), aImageFlags);
   NS_ENSURE_SUCCESS(rv, BadImage(newImage));
 
   newImage->SetInnerWindowID(aInnerWindowId);
 
   // Use content-length as a size hint for http channels.
   nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aRequest));
   if (httpChannel) {
     nsAutoCString contentLength;
@@ -178,26 +171,26 @@ ImageFactory::CreateRasterImage(nsIReque
 
   return newImage.forget();
 }
 
 /* static */ already_AddRefed<Image>
 ImageFactory::CreateVectorImage(nsIRequest* aRequest,
                                 imgStatusTracker* aStatusTracker,
                                 const nsCString& aMimeType,
-                                const nsCString& aURIString,
+                                nsIURI* aURI,
                                 uint32_t aImageFlags,
                                 uint32_t aInnerWindowId)
 {
   nsresult rv;
 
-  nsRefPtr<VectorImage> newImage = new VectorImage(aStatusTracker);
+  nsRefPtr<VectorImage> newImage = new VectorImage(aStatusTracker, aURI);
 
   rv = newImage->Init(aStatusTracker->GetDecoderObserver(),
-                      aMimeType.get(), aURIString.get(), aImageFlags);
+                      aMimeType.get(), aImageFlags);
   NS_ENSURE_SUCCESS(rv, BadImage(newImage));
 
   newImage->SetInnerWindowID(aInnerWindowId);
 
   rv = newImage->OnStartRequest(aRequest, nullptr);
   NS_ENSURE_SUCCESS(rv, BadImage(newImage));
 
   return newImage.forget();
--- a/image/src/ImageFactory.h
+++ b/image/src/ImageFactory.h
@@ -44,24 +44,24 @@ struct ImageFactory
   static already_AddRefed<Image> CreateAnonymousImage(const nsCString& aMimeType);
 
 
 private:
   // Factory functions that create specific types of image containers.
   static already_AddRefed<Image> CreateRasterImage(nsIRequest* aRequest,
                                                    imgStatusTracker* aStatusTracker,
                                                    const nsCString& aMimeType,
-                                                   const nsCString& aURIString,
+                                                   nsIURI* aURI,
                                                    uint32_t aImageFlags,
                                                    uint32_t aInnerWindowId);
 
   static already_AddRefed<Image> CreateVectorImage(nsIRequest* aRequest,
                                                    imgStatusTracker* aStatusTracker,
                                                    const nsCString& aMimeType,
-                                                   const nsCString& aURIString,
+                                                   nsIURI* aURI,
                                                    uint32_t aImageFlags,
                                                    uint32_t aInnerWindowId);
 
   // This is a static factory class, so disallow instantiation.
   virtual ~ImageFactory() = 0;
 };
 
 } // namespace image
--- a/image/src/Makefile.in
+++ b/image/src/Makefile.in
@@ -39,16 +39,19 @@ CPPSRCS		= \
 			imgRequestProxy.cpp \
 			imgTools.cpp \
 			imgStatusTracker.cpp \
 			$(NULL)
 
 # We need to instantiate the decoders
 LOCAL_INCLUDES += -I$(topsrcdir)/image/decoders
 
+# For URI-related functionality
+LOCAL_INCLUDES += -I$(topsrcdir)/netwerk/base/src
+
 # Because SVGDocumentWrapper.cpp includes "nsSVGSVGElement.h"
 LOCAL_INCLUDES += \
 			-I$(topsrcdir)/content/svg/content/src \
 			-I$(topsrcdir)/content/base/src \
 			$(NULL)
 
 # Because VectorImage.cpp includes nsSVGUtils.h and nsSVGEffects.h
 LOCAL_INCLUDES += -I$(topsrcdir)/layout/svg
--- a/image/src/RasterImage.cpp
+++ b/image/src/RasterImage.cpp
@@ -347,18 +347,19 @@ static nsCOMPtr<nsIThread> sScaleWorkerT
 #ifndef DEBUG
 NS_IMPL_ISUPPORTS2(RasterImage, imgIContainer, nsIProperties)
 #else
 NS_IMPL_ISUPPORTS3(RasterImage, imgIContainer, nsIProperties,
                    imgIContainerDebug)
 #endif
 
 //******************************************************************************
-RasterImage::RasterImage(imgStatusTracker* aStatusTracker) :
-  Image(aStatusTracker), // invoke superclass's constructor
+RasterImage::RasterImage(imgStatusTracker* aStatusTracker,
+                         nsIURI* aURI /* = nullptr */) :
+  Image(aStatusTracker, aURI), // invoke superclass's constructor
   mSize(0,0),
   mFrameDecodeFlags(DECODE_FLAGS_DEFAULT),
   mAnim(nullptr),
   mLoopCount(-1),
   mObserver(nullptr),
   mLockCount(0),
   mDecoder(nullptr),
   mDecodeRequest(this),
@@ -436,17 +437,16 @@ RasterImage::Initialize()
   // Create our singletons now, so we don't have to worry about what thread
   // they're created on.
   DecodeWorker::Singleton();
 }
 
 nsresult
 RasterImage::Init(imgIDecoderObserver *aObserver,
                   const char* aMimeType,
-                  const char* aURIString,
                   uint32_t aFlags)
 {
   // We don't support re-initialization
   if (mInitialized)
     return NS_ERROR_ILLEGAL_VALUE;
 
   // Not sure an error can happen before init, but be safe
   if (mError)
@@ -459,17 +459,16 @@ RasterImage::Init(imgIDecoderObserver *a
   NS_ABORT_IF_FALSE(!(aFlags & INIT_FLAG_MULTIPART) ||
                     (!(aFlags & INIT_FLAG_DISCARDABLE) &&
                      !(aFlags & INIT_FLAG_DECODE_ON_DRAW)),
                     "Can't be discardable or decode-on-draw for multipart");
 
   // Store initialization data
   mObserver = do_GetWeakReference(aObserver);
   mSourceDataMimeType.Assign(aMimeType);
-  mURIString.Assign(aURIString);
   mDiscardable = !!(aFlags & INIT_FLAG_DISCARDABLE);
   mDecodeOnDraw = !!(aFlags & INIT_FLAG_DECODE_ON_DRAW);
   mMultipart = !!(aFlags & INIT_FLAG_MULTIPART);
 
   // Statistics
   if (mDiscardable) {
     num_discardable_containers++;
     discardable_source_bytes += mSourceData.Length();
@@ -683,17 +682,17 @@ RasterImage::ExtractFrame(uint32_t aWhic
     return NS_ERROR_FAILURE;
 
   // Make a new container. This should switch to another class with bug 505959.
   nsRefPtr<RasterImage> img(new RasterImage());
 
   // We don't actually have a mimetype in this case. The empty string tells the
   // init routine not to try to instantiate a decoder. This should be fixed in
   // bug 505959.
-  img->Init(nullptr, "", "", INIT_FLAG_NONE);
+  img->Init(nullptr, "", INIT_FLAG_NONE);
   img->SetSize(aRegion.width, aRegion.height);
   img->mDecoded = true; // Also, we need to mark the image as decoded
   img->mHasBeenDecoded = true;
   img->mFrameDecodeFlags = aFlags & DECODE_FLAGS_MASK;
 
   if (!ApplyDecodeFlags(aFlags))
     return NS_ERROR_NOT_AVAILABLE;
   
--- a/image/src/RasterImage.h
+++ b/image/src/RasterImage.h
@@ -157,17 +157,16 @@ public:
   virtual ~RasterImage();
 
   virtual nsresult StartAnimation();
   virtual nsresult StopAnimation();
 
   // Methods inherited from Image
   nsresult Init(imgIDecoderObserver* aObserver,
                 const char* aMimeType,
-                const char* aURIString,
                 uint32_t aFlags);
   virtual void  GetCurrentFrameRect(nsIntRect& aRect) MOZ_OVERRIDE;
 
   // Raster-specific methods
   static NS_METHOD WriteToRasterImage(nsIInputStream* aIn, void* aClosure,
                                       const char* aFromRawSegment,
                                       uint32_t aToOffset, uint32_t aCount,
                                       uint32_t* aWriteCount);
@@ -728,17 +727,17 @@ private: // data
   // Helpers
   void DoError();
   bool CanDiscard();
   bool CanForciblyDiscard();
   bool DiscardingActive();
   bool StoringSourceData() const;
 
 protected:
-  RasterImage(imgStatusTracker* aStatusTracker = nullptr);
+  RasterImage(imgStatusTracker* aStatusTracker = nullptr, nsIURI* aURI = nullptr);
 
   bool ShouldAnimate();
 
   friend class ImageFactory;
 };
 
 inline NS_IMETHODIMP RasterImage::GetAnimationMode(uint16_t *aAnimationMode) {
   return GetAnimationModeInternal(aAnimationMode);
--- a/image/src/VectorImage.cpp
+++ b/image/src/VectorImage.cpp
@@ -164,18 +164,19 @@ SVGDrawingCallback::operator()(gfxContex
 NS_IMPL_ISUPPORTS3(VectorImage,
                    imgIContainer,
                    nsIStreamListener,
                    nsIRequestObserver)
 
 //------------------------------------------------------------------------------
 // Constructor / Destructor
 
-VectorImage::VectorImage(imgStatusTracker* aStatusTracker) :
-  Image(aStatusTracker), // invoke superclass's constructor
+VectorImage::VectorImage(imgStatusTracker* aStatusTracker,
+                         nsIURI* aURI /* = nullptr */) :
+  Image(aStatusTracker, aURI), // invoke superclass's constructor
   mRestrictedRegion(0, 0, 0, 0),
   mIsInitialized(false),
   mIsFullyLoaded(false),
   mIsDrawing(false),
   mHaveAnimations(false),
   mHaveRestrictedRegion(false)
 {
 }
@@ -185,17 +186,16 @@ VectorImage::~VectorImage()
 }
 
 //------------------------------------------------------------------------------
 // Methods inherited from Image.h
 
 nsresult
 VectorImage::Init(imgIDecoderObserver* aObserver,
                   const char* aMimeType,
-                  const char* aURIString,
                   uint32_t aFlags)
 {
   // We don't support re-initialization
   if (mIsInitialized)
     return NS_ERROR_ILLEGAL_VALUE;
 
   NS_ABORT_IF_FALSE(!mIsFullyLoaded && !mHaveAnimations &&
                     !mHaveRestrictedRegion && !mError,
--- a/image/src/VectorImage.h
+++ b/image/src/VectorImage.h
@@ -34,17 +34,16 @@ public:
   NS_DECL_IMGICONTAINER
 
   // (no public constructor - use ImageFactory)
   virtual ~VectorImage();
 
   // Methods inherited from Image
   nsresult Init(imgIDecoderObserver* aObserver,
                 const char* aMimeType,
-                const char* aURIString,
                 uint32_t aFlags);
   virtual void GetCurrentFrameRect(nsIntRect& aRect) MOZ_OVERRIDE;
 
   virtual size_t HeapSizeOfSourceWithComputedFallback(nsMallocSizeOfFun aMallocSizeOf) const;
   virtual size_t HeapSizeOfDecodedWithComputedFallback(nsMallocSizeOfFun aMallocSizeOf) const;
   virtual size_t NonHeapSizeOfDecoded() const;
   virtual size_t OutOfProcessSizeOfDecoded() const;
 
@@ -57,17 +56,17 @@ public:
                                        nsISupports* aContext,
                                        nsresult status) MOZ_OVERRIDE;
   virtual nsresult OnNewSourceData() MOZ_OVERRIDE;
 
   // Callback for SVGRootRenderingObserver
   void InvalidateObserver();
 
 protected:
-  VectorImage(imgStatusTracker* aStatusTracker = nullptr);
+  VectorImage(imgStatusTracker* aStatusTracker = nullptr, nsIURI* aURI = nullptr);
 
   virtual nsresult StartAnimation();
   virtual nsresult StopAnimation();
   virtual bool     ShouldAnimate();
 
 private:
   nsWeakPtr                          mObserver;   //! imgIDecoderObserver
   nsRefPtr<SVGDocumentWrapper>       mSVGDocumentWrapper;
--- a/image/src/imgLoader.cpp
+++ b/image/src/imgLoader.cpp
@@ -698,17 +698,18 @@ nsresult imgLoader::CreateNewProxyForReq
      |Init()| adds the request to the loadgroup.
    */
   proxyRequest->SetLoadFlags(aLoadFlags);
 
   nsCOMPtr<nsIURI> uri;
   aRequest->GetURI(getter_AddRefs(uri));
 
   // init adds itself to imgRequest's list of observers
-  nsresult rv = proxyRequest->Init(&aRequest->GetStatusTracker(), aLoadGroup, uri, aObserver);
+  nsresult rv = proxyRequest->Init(aRequest, &aRequest->GetStatusTracker(),
+                                   aLoadGroup, uri, aObserver);
   if (NS_FAILED(rv)) {
     NS_RELEASE(proxyRequest);
     return rv;
   }
 
   // transfer reference to caller
   *_retval = proxyRequest;
 
--- a/image/src/imgRequest.cpp
+++ b/image/src/imgRequest.cpp
@@ -66,32 +66,29 @@ GetImgLog()
 NS_IMPL_ISUPPORTS5(imgRequest,
                    nsIStreamListener, nsIRequestObserver,
                    nsIChannelEventSink,
                    nsIInterfaceRequestor,
                    nsIAsyncVerifyRedirectCallback)
 
 imgRequest::imgRequest(imgLoader* aLoader)
  : mLoader(aLoader)
- , mStatusTracker(new imgStatusTracker(nullptr, this))
+ , mStatusTracker(new imgStatusTracker(nullptr))
  , mValidator(nullptr)
  , mInnerWindowId(0)
  , mCORSMode(imgIRequest::CORS_NONE)
  , mDecodeRequested(false)
  , mIsMultiPartChannel(false)
  , mGotData(false)
  , mIsInCache(false)
  , mResniffMimeType(false)
 { }
 
 imgRequest::~imgRequest()
 {
-  // The status tracker can outlive this request, and needs to know it's dying.
-  GetStatusTracker().ClearRequest();
-
   if (mURI) {
     nsAutoCString spec;
     mURI->GetSpec(spec);
     LOG_FUNC_WITH_PARAM(GetImgLog(), "imgRequest::~imgRequest()", "keyuri", spec.get());
   } else
     LOG_FUNC(GetImgLog(), "imgRequest::~imgRequest()");
 }
 
@@ -525,18 +522,20 @@ imgRequest::StartDecoding()
 
 /* void onStartRequest (in nsIRequest request, in nsISupports ctxt); */
 NS_IMETHODIMP imgRequest::OnStartRequest(nsIRequest *aRequest, nsISupports *ctxt)
 {
   LOG_SCOPE(GetImgLog(), "imgRequest::OnStartRequest");
 
   // Figure out if we're multipart
   nsCOMPtr<nsIMultiPartChannel> mpchan(do_QueryInterface(aRequest));
-  if (mpchan)
-      mIsMultiPartChannel = true;
+  if (mpchan) {
+    mIsMultiPartChannel = true;
+    GetStatusTracker().SetIsMultipart();
+  }
 
   // If we're not multipart, we shouldn't have an image yet
   NS_ABORT_IF_FALSE(mIsMultiPartChannel || !mImage,
                     "Already have an image for non-multipart request");
 
   // If we're multipart and about to load another image, signal so we can
   // detect the mime type in OnDataAvailable.
   if (mIsMultiPartChannel && mImage) {
@@ -731,17 +730,17 @@ imgRequest::OnDataAvailable(nsIRequest *
       mContentType = newType;
 
       // If we've resniffed our MIME type and it changed, we need to create a
       // new status tracker to give to the image, because we don't have one of
       // our own any more.
       if (mResniffMimeType) {
         NS_ABORT_IF_FALSE(mIsMultiPartChannel, "Resniffing a non-multipart image");
 
-        imgStatusTracker* freshTracker = new imgStatusTracker(nullptr, this);
+        imgStatusTracker* freshTracker = new imgStatusTracker(nullptr);
         freshTracker->AdoptConsumers(&GetStatusTracker());
         mStatusTracker = freshTracker;
 
         mResniffMimeType = false;
       }
 
       /* set our mimetype as a property */
       nsCOMPtr<nsISupportsCString> contentType(do_CreateInstance("@mozilla.org/supports-cstring;1"));
@@ -762,19 +761,22 @@ imgRequest::OnDataAvailable(nsIRequest *
           mProperties->Set("content-disposition", contentDisposition);
         }
       }
 
       LOG_MSG_WITH_PARAM(GetImgLog(), "imgRequest::OnDataAvailable", "content type", mContentType.get());
 
       // Now we can create a new image to hold the data. If we don't have a decoder
       // for this mimetype we'll find out about it here.
-      mImage = ImageFactory::CreateImage(aRequest, mStatusTracker.forget(), mContentType,
+      mImage = ImageFactory::CreateImage(aRequest, mStatusTracker, mContentType,
                                          mURI, mIsMultiPartChannel, mInnerWindowId);
 
+      // Release our copy of the status tracker since the image owns it now.
+      mStatusTracker = nullptr;
+
       // Notify listeners that we have an image.
       // XXX(seth): The name of this notification method is pretty misleading.
       GetStatusTracker().OnDataAvailable();
 
       if (mImage->HasError() && !mIsMultiPartChannel) { // Probably bad mimetype
         // We allow multipart images to fail to initialize without cancelling the
         // load because subsequent images might be fine; thus only single part
         // images end up here.
--- a/image/src/imgRequest.h
+++ b/image/src/imgRequest.h
@@ -193,17 +193,17 @@ private:
   nsCOMPtr<nsIURI> mURI;
   // The URI of the resource we ended up loading after all redirects, etc.
   nsCOMPtr<nsIURI> mCurrentURI;
   // The principal of the document which loaded this image. Used when validating for CORS.
   nsCOMPtr<nsIPrincipal> mLoadingPrincipal;
   // The principal of this image.
   nsCOMPtr<nsIPrincipal> mPrincipal;
   // Status-tracker -- transferred to mImage, when it gets instantiated
-  nsAutoPtr<imgStatusTracker> mStatusTracker;
+  nsRefPtr<imgStatusTracker> mStatusTracker;
   nsRefPtr<mozilla::image::Image> mImage;
   nsCOMPtr<nsIProperties> mProperties;
   nsCOMPtr<nsISupports> mSecurityInfo;
   nsCOMPtr<nsIChannel> mChannel;
   nsCOMPtr<nsIInterfaceRequestor> mPrevChannelSink;
   nsCOMPtr<nsIApplicationCache> mApplicationCache;
 
   nsCOMPtr<nsITimedChannel> mTimedChannel;
--- a/image/src/imgRequestProxy.cpp
+++ b/image/src/imgRequestProxy.cpp
@@ -143,27 +143,29 @@ imgRequestProxy::~imgRequestProxy()
        the last observer.  This allows the image to continue to download and
        be cached even if no one is using it currently.
     */
     mCanceled = true;
     GetOwner()->RemoveProxy(this, NS_OK);
   }
 }
 
-nsresult imgRequestProxy::Init(imgStatusTracker* aStatusTracker,
+nsresult imgRequestProxy::Init(imgRequest* aOwner,
+                               imgStatusTracker* aStatusTracker,
                                nsILoadGroup* aLoadGroup,
-                               nsIURI* aURI, imgINotificationObserver* aObserver)
+                               nsIURI* aURI,
+                               imgINotificationObserver* aObserver)
 {
   NS_PRECONDITION(!GetOwner() && !mListener, "imgRequestProxy is already initialized");
 
-  LOG_SCOPE_WITH_PARAM(GetImgLog(), "imgRequestProxy::Init", "request", aStatusTracker->GetRequest());
+  LOG_SCOPE_WITH_PARAM(GetImgLog(), "imgRequestProxy::Init", "request", aOwner);
 
   NS_ABORT_IF_FALSE(mAnimationConsumers == 0, "Cannot have animation before Init");
 
-  mBehaviour->SetOwner(aStatusTracker->GetRequest());
+  mBehaviour->SetOwner(aOwner);
   mListener = aObserver;
   // Make sure to addref mListener before the AddProxy call below, since
   // that call might well want to release it if the imgRequest has
   // already seen OnStopRequest.
   if (mListener) {
     mListenerIsStrongRef = true;
     NS_ADDREF(mListener);
   }
@@ -579,17 +581,18 @@ nsresult imgRequestProxy::PerformClone(i
 
   // It is important to call |SetLoadFlags()| before calling |Init()| because
   // |Init()| adds the request to the loadgroup.
   // When a request is added to a loadgroup, its load flags are merged
   // with the load flags of the loadgroup.
   // XXXldb That's not true anymore.  Stuff from imgLoader adds the
   // request to the loadgroup.
   clone->SetLoadFlags(mLoadFlags);
-  nsresult rv = clone->Init(&GetStatusTracker(), mLoadGroup, mURI, aObserver);
+  nsresult rv = clone->Init(mBehaviour->GetOwner(), &GetStatusTracker(),
+                            mLoadGroup, mURI, aObserver);
   if (NS_FAILED(rv))
     return rv;
 
   // Assign to *aClone before calling Notify so that if the caller expects to
   // only be notified for requests it's already holding pointers to it won't be
   // surprised.
   NS_ADDREF(*aClone = clone);
 
@@ -674,16 +677,30 @@ NS_IMETHODIMP imgRequestProxy::GetHasTra
     // The safe thing to do is to claim we have data
     *hasData = true;
   }
   return NS_OK;
 }
 
 /** imgIDecoderObserver methods **/
 
+void imgRequestProxy::OnStartDecode()
+{
+  // This notification is deliberately not propagated since there are no
+  // listeners who care about it.
+  if (GetOwner()) {
+    // In the case of streaming jpegs, it is possible to get multiple
+    // OnStartDecodes which indicates the beginning of a new decode.  The cache
+    // entry's size therefore needs to be reset to 0 here.  If we do not do
+    // this, the code in imgStatusTrackerObserver::OnStopFrame will continue to
+    // increase the data size cumulatively.
+    GetOwner()->ResetCacheEntry();
+  }
+}
+
 void imgRequestProxy::OnStartContainer()
 {
   LOG_FUNC(GetImgLog(), "imgRequestProxy::OnStartContainer");
 
   if (mListener && !mCanceled && !mSentStartContainer) {
     // Hold a ref to the listener while we call it, just in case.
     nsCOMPtr<imgINotificationObserver> kungFuDeathGrip(mListener);
     mListener->Notify(this, imgINotificationObserver::SIZE_AVAILABLE, nullptr);
@@ -718,30 +735,40 @@ void imgRequestProxy::OnStopDecode()
   LOG_FUNC(GetImgLog(), "imgRequestProxy::OnStopDecode");
 
   if (mListener && !mCanceled) {
     // Hold a ref to the listener while we call it, just in case.
     nsCOMPtr<imgINotificationObserver> kungFuDeathGrip(mListener);
     mListener->Notify(this, imgINotificationObserver::DECODE_COMPLETE, nullptr);
   }
 
-  // Multipart needs reset for next OnStartContainer
-  if (GetOwner() && GetOwner()->GetMultipart())
-    mSentStartContainer = false;
+  if (GetOwner()) {
+    // We finished the decode, and thus have the decoded frames. Update the cache
+    // entry size to take this into account.
+    GetOwner()->UpdateCacheEntrySize();
+
+    // Multipart needs reset for next OnStartContainer.
+    if (GetOwner()->GetMultipart())
+      mSentStartContainer = false;
+  }
 }
 
 void imgRequestProxy::OnDiscard()
 {
   LOG_FUNC(GetImgLog(), "imgRequestProxy::OnDiscard");
 
   if (mListener && !mCanceled) {
     // Hold a ref to the listener while we call it, just in case.
     nsCOMPtr<imgINotificationObserver> kungFuDeathGrip(mListener);
     mListener->Notify(this, imgINotificationObserver::DISCARD, nullptr);
   }
+  if (GetOwner()) {
+    // Update the cache entry size, since we just got rid of frame data.
+    GetOwner()->UpdateCacheEntrySize();
+  }
 }
 
 void imgRequestProxy::OnImageIsAnimated()
 {
   LOG_FUNC(GetImgLog(), "imgRequestProxy::OnImageIsAnimated");
   if (mListener && !mCanceled) {
     // Hold a ref to the listener while we call it, just in case.
     nsCOMPtr<imgINotificationObserver> kungFuDeathGrip(mListener);
@@ -881,33 +908,33 @@ imgRequestProxy::GetStaticRequest(imgReq
     return rv;
 
   nsRefPtr<Image> frame = static_cast<Image*>(currentFrame.get());
 
   // Create a static imgRequestProxy with our new extracted frame.
   nsCOMPtr<nsIPrincipal> currentPrincipal;
   GetImagePrincipal(getter_AddRefs(currentPrincipal));
   nsRefPtr<imgRequestProxy> req = new imgRequestProxyStatic(frame, currentPrincipal);
-  req->Init(&frame->GetStatusTracker(), nullptr, mURI, nullptr);
+  req->Init(nullptr, &frame->GetStatusTracker(), nullptr, mURI, nullptr);
 
   NS_ADDREF(*aReturn = req);
 
   return NS_OK;
 }
 
 void imgRequestProxy::NotifyListener()
 {
   // It would be nice to notify the observer directly in the status tracker
   // instead of through the proxy, but there are several places we do extra
   // processing when we receive notifications (like OnStopRequest()), and we
   // need to check mCanceled everywhere too.
 
   if (GetOwner()) {
     // Send the notifications to our listener asynchronously.
-    GetStatusTracker().Notify(GetOwner(), this);
+    GetStatusTracker().Notify(this);
   } else {
     // We don't have an imgRequest, so we can only notify the clone of our
     // current state, but we still have to do that asynchronously.
     NS_ABORT_IF_FALSE(GetImage(),
                       "if we have no imgRequest, we should have an Image");
     GetStatusTracker().NotifyCurrentState(this);
   }
 }
--- a/image/src/imgRequestProxy.h
+++ b/image/src/imgRequestProxy.h
@@ -53,19 +53,21 @@ public:
   NS_DECL_NSISECURITYINFOPROVIDER
   // nsITimedChannel declared below
 
   imgRequestProxy();
   virtual ~imgRequestProxy();
 
   // Callers to Init or ChangeOwner are required to call NotifyListener after
   // (although not immediately after) doing so.
-  nsresult Init(imgStatusTracker* aStatusTracker,
+  nsresult Init(imgRequest* aOwner,
+                imgStatusTracker* aStatusTracker,
                 nsILoadGroup *aLoadGroup,
-                nsIURI* aURI, imgINotificationObserver *aObserver);
+                nsIURI* aURI,
+                imgINotificationObserver *aObserver);
 
   nsresult ChangeOwner(imgRequest *aNewOwner); // this will change mOwner.  Do not call this if the previous
                                                // owner has already sent notifications out!
 
   void AddToLoadGroup();
   void RemoveFromLoadGroup(bool releaseLoadGroup);
 
   inline bool HasObserver() const {
@@ -131,16 +133,17 @@ protected:
       nsresult mStatus;
   };
 
   // The following notification functions are protected to ensure that (friend
   // class) imgStatusTracker is the only class allowed to send us
   // notifications.
 
   /* non-virtual imgIDecoderObserver methods */
+  void OnStartDecode     ();
   void OnStartContainer  ();
   void OnFrameUpdate     (const nsIntRect * aRect);
   void OnStopFrame       ();
   void OnStopDecode      ();
   void OnDiscard         ();
   void OnImageIsAnimated ();
 
   /* non-virtual sort-of-nsIRequestObserver methods */
--- a/image/src/imgStatusTracker.cpp
+++ b/image/src/imgStatusTracker.cpp
@@ -1,17 +1,16 @@
 /* -*- 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 "imgStatusTracker.h"
 
-#include "imgRequest.h"
 #include "imgIContainer.h"
 #include "imgRequestProxy.h"
 #include "Image.h"
 #include "ImageLogging.h"
 #include "RasterImage.h"
 #include "nsIObserverService.h"
 
 #include "mozilla/Util.h"
@@ -47,37 +46,35 @@ NS_IMETHODIMP imgStatusTrackerObserver::
 /** imgIDecoderObserver methods **/
 
 NS_IMETHODIMP imgStatusTrackerObserver::OnStartDecode()
 {
   LOG_SCOPE(GetImgLog(), "imgStatusTrackerObserver::OnStartDecode");
   NS_ABORT_IF_FALSE(mTracker->GetImage(),
                     "OnStartDecode callback before we've created our image");
 
-  if (mTracker->GetRequest() && !mTracker->GetRequest()->GetMultipart()) {
+  mTracker->RecordStartDecode();
+
+  nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mTracker->mConsumers);
+  while (iter.HasMore()) {
+    mTracker->SendStartDecode(iter.GetNext());
+  }
+
+  if (mTracker->IsMultipart()) {
     MOZ_ASSERT(!mTracker->mBlockingOnload);
     mTracker->mBlockingOnload = true;
 
     mTracker->RecordBlockOnload();
 
     nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mTracker->mConsumers);
     while (iter.HasMore()) {
       mTracker->SendBlockOnload(iter.GetNext());
     }
   }
 
-  /* In the case of streaming jpegs, it is possible to get multiple OnStartDecodes which
-     indicates the beginning of a new decode.
-     The cache entry's size therefore needs to be reset to 0 here.  If we do not do this,
-     the code in imgStatusTrackerObserver::OnStopFrame will continue to increase the data size cumulatively.
-  */
-  if (mTracker->GetRequest()) {
-    mTracker->GetRequest()->ResetCacheEntry();
-  }
-
   return NS_OK;
 }
 
 NS_IMETHODIMP imgStatusTrackerObserver::OnStartRequest()
 {
   NS_NOTREACHED("imgRequest(imgIDecoderObserver)::OnStartRequest");
   return NS_OK;
 }
@@ -130,58 +127,38 @@ NS_IMETHODIMP imgStatusTrackerObserver::
     mTracker->SendStopFrame(iter.GetNext());
   }
 
   mTracker->MaybeUnblockOnload();
 
   return NS_OK;
 }
 
-static void
-FireFailureNotification(imgRequest* aRequest)
-{
-  // Some kind of problem has happened with image decoding.
-  // Report the URI to net:failed-to-process-uri-conent observers.
-
-  nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
-  if (os) {
-    nsCOMPtr<nsIURI> uri;
-    aRequest->GetURI(getter_AddRefs(uri));
-    os->NotifyObservers(uri, "net:failed-to-process-uri-content", nullptr);
-  }
-}
-
 /* void onStopDecode (in nsresult status); */
 NS_IMETHODIMP imgStatusTrackerObserver::OnStopDecode(nsresult aStatus)
 {
   LOG_SCOPE(GetImgLog(), "imgStatusTrackerObserver::OnStopDecode");
   NS_ABORT_IF_FALSE(mTracker->GetImage(),
                     "OnStopDecode callback before we've created our image");
 
-  // We finished the decode, and thus have the decoded frames. Update the cache
-  // entry size to take this into account.
-  if (mTracker->GetRequest()) {
-    mTracker->GetRequest()->UpdateCacheEntrySize();
-  }
-
   bool preexistingError = mTracker->GetImageStatus() == imgIRequest::STATUS_ERROR;
 
   mTracker->RecordStopDecode(aStatus);
 
   nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mTracker->mConsumers);
   while (iter.HasMore()) {
     mTracker->SendStopDecode(iter.GetNext(), aStatus);
   }
 
   // This is really hacky. We need to handle the case where we start decoding,
   // block onload, but then hit an error before we get to our first frame.
   mTracker->MaybeUnblockOnload();
 
-  if (NS_FAILED(aStatus) && !preexistingError && mTracker->GetRequest()) {
-    FireFailureNotification(mTracker->GetRequest());
+  if (NS_FAILED(aStatus) && !preexistingError) {
+    mTracker->FireFailureNotification();
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP imgStatusTrackerObserver::OnStopRequest(bool aLastPart)
 {
   NS_NOTREACHED("imgRequest(imgIDecoderObserver)::OnStopRequest");
@@ -191,21 +168,16 @@ NS_IMETHODIMP imgStatusTrackerObserver::
 /* void onDiscard (); */
 NS_IMETHODIMP imgStatusTrackerObserver::OnDiscard()
 {
   NS_ABORT_IF_FALSE(mTracker->GetImage(),
                     "OnDiscard callback before we've created our image");
 
   mTracker->RecordDiscard();
 
-  // Update the cache entry size, since we just got rid of frame data
-  if (mTracker->GetRequest()) {
-    mTracker->GetRequest()->UpdateCacheEntrySize();
-  }
-
   nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mTracker->mConsumers);
   while (iter.HasMore()) {
     mTracker->SendDiscard(iter.GetNext());
   }
 
   return NS_OK;
 }
 
@@ -220,31 +192,31 @@ NS_IMETHODIMP imgStatusTrackerObserver::
     mTracker->SendImageIsAnimated(iter.GetNext());
   }
 
   return NS_OK;
 }
 
 // imgStatusTracker methods
 
-imgStatusTracker::imgStatusTracker(Image* aImage, imgRequest* aRequest)
+imgStatusTracker::imgStatusTracker(Image* aImage)
   : mImage(aImage),
-    mRequest(aRequest),
+    mTrackerObserver(new imgStatusTrackerObserver(this)),
     mState(0),
     mImageStatus(imgIRequest::STATUS_NONE),
+    mIsMultipart(false),
     mHadLastPart(false),
-    mBlockingOnload(false),
-    mTrackerObserver(new imgStatusTrackerObserver(this))
+    mBlockingOnload(false)
 {}
 
 imgStatusTracker::imgStatusTracker(const imgStatusTracker& aOther)
   : mImage(aOther.mImage),
-    mRequest(aOther.mRequest),
     mState(aOther.mState),
     mImageStatus(aOther.mImageStatus),
+    mIsMultipart(aOther.mIsMultipart),
     mHadLastPart(aOther.mHadLastPart),
     mBlockingOnload(aOther.mBlockingOnload)
     // Note: we explicitly don't copy mRequestRunnable, because it won't be
     // nulled out when the mRequestRunnable's Run function eventually gets
     // called.
 {}
 
 void
@@ -269,71 +241,69 @@ imgStatusTracker::GetImageStatus() const
 {
   return mImageStatus;
 }
 
 // A helper class to allow us to call SyncNotify asynchronously.
 class imgRequestNotifyRunnable : public nsRunnable
 {
   public:
-    imgRequestNotifyRunnable(imgRequest* request, imgRequestProxy* requestproxy)
-      : mRequest(request)
+    imgRequestNotifyRunnable(imgStatusTracker* aTracker, imgRequestProxy* aRequestProxy)
+      : mTracker(aTracker)
     {
-      mProxies.AppendElement(requestproxy);
+      mProxies.AppendElement(aRequestProxy);
     }
 
     NS_IMETHOD Run()
     {
-      imgStatusTracker& statusTracker = mRequest->GetStatusTracker();
-
       for (uint32_t i = 0; i < mProxies.Length(); ++i) {
         mProxies[i]->SetNotificationsDeferred(false);
-        statusTracker.SyncNotify(mProxies[i]);
+        mTracker->SyncNotify(mProxies[i]);
       }
 
-      statusTracker.mRequestRunnable = nullptr;
+      mTracker->mRequestRunnable = nullptr;
       return NS_OK;
     }
 
     void AddProxy(imgRequestProxy* aRequestProxy)
     {
       mProxies.AppendElement(aRequestProxy);
     }
 
   private:
     friend class imgStatusTracker;
 
-    nsRefPtr<imgRequest> mRequest;
-    nsTArray<nsRefPtr<imgRequestProxy> > mProxies;
+    nsRefPtr<imgStatusTracker> mTracker;
+    nsTArray< nsRefPtr<imgRequestProxy> > mProxies;
 };
 
 void
-imgStatusTracker::Notify(imgRequest* request, imgRequestProxy* proxy)
+imgStatusTracker::Notify(imgRequestProxy* proxy)
 {
 #ifdef PR_LOGGING
-  nsCOMPtr<nsIURI> uri;
-  request->GetURI(getter_AddRefs(uri));
-  nsAutoCString spec;
-  uri->GetSpec(spec);
-  LOG_FUNC_WITH_PARAM(GetImgLog(), "imgStatusTracker::Notify async", "uri", spec.get());
+  if (GetImage() && GetImage()->GetURI()) {
+    nsCOMPtr<nsIURI> uri(GetImage()->GetURI());
+    nsAutoCString spec;
+    uri->GetSpec(spec);
+    LOG_FUNC_WITH_PARAM(GetImgLog(), "imgStatusTracker::Notify async", "uri", spec.get());
+  } else {
+    LOG_FUNC_WITH_PARAM(GetImgLog(), "imgStatusTracker::Notify async", "uri", "<unknown>");
+  }
 #endif
 
   proxy->SetNotificationsDeferred(true);
 
   // If we have an existing runnable that we can use, we just append this proxy
   // to its list of proxies to be notified. This ensures we don't unnecessarily
   // delay onload.
   imgRequestNotifyRunnable* runnable = static_cast<imgRequestNotifyRunnable*>(mRequestRunnable.get());
-  if (runnable && runnable->mRequest == request) {
+  if (runnable) {
     runnable->AddProxy(proxy);
   } else {
-    // It's okay to overwrite an existing mRequestRunnable, because adding a
-    // new proxy is strictly a performance optimization. The notification will
-    // always happen, regardless of whether we hold a reference to a runnable.
-    mRequestRunnable = new imgRequestNotifyRunnable(request, proxy);
+    mRequestRunnable = new imgRequestNotifyRunnable(this, proxy);
     NS_DispatchToCurrentThread(mRequestRunnable);
   }
 }
 
 // A helper class to allow us to call SyncNotify asynchronously for a given,
 // fixed, state.
 class imgStatusNotifyRunnable : public nsRunnable
 {
@@ -393,16 +363,20 @@ imgStatusTracker::SyncNotify(imgRequestP
   // OnStartRequest
   if (mState & stateRequestStarted)
     proxy->OnStartRequest();
 
   // OnStartContainer
   if (mState & stateHasSize)
     proxy->OnStartContainer();
 
+  // OnStartDecode
+  if (mState & stateDecodeStarted)
+    proxy->OnStartDecode();
+
   // BlockOnload
   if (mState & stateBlockingOnload)
     proxy->BlockOnload();
 
   if (mImage) {
     // OnDataAvailable
     // XXX - Should only send partial rects here, but that needs to
     // wait until we fix up the observer interface
@@ -493,21 +467,35 @@ imgStatusTracker::RecordLoaded()
   mImageStatus |= imgIRequest::STATUS_SIZE_AVAILABLE | imgIRequest::STATUS_LOAD_COMPLETE;
   mHadLastPart = true;
 }
 
 void
 imgStatusTracker::RecordDecoded()
 {
   NS_ABORT_IF_FALSE(mImage, "RecordDecoded called before we have an Image");
-  mState |= stateDecodeStopped | stateFrameStopped;
+  mState |= stateDecodeStarted | stateDecodeStopped | stateFrameStopped;
   mImageStatus |= imgIRequest::STATUS_FRAME_COMPLETE | imgIRequest::STATUS_DECODE_COMPLETE;
 }
 
 void
+imgStatusTracker::RecordStartDecode()
+{
+  NS_ABORT_IF_FALSE(mImage, "RecordStartDecode without an Image");
+  mState |= stateDecodeStarted;
+}
+
+void
+imgStatusTracker::SendStartDecode(imgRequestProxy* aProxy)
+{
+  if (!aProxy->NotificationsDeferred())
+    aProxy->OnStartDecode();
+}
+
+void
 imgStatusTracker::RecordStartContainer(imgIContainer* aContainer)
 {
   NS_ABORT_IF_FALSE(mImage,
                     "RecordStartContainer called before we have an Image");
   NS_ABORT_IF_FALSE(mImage == aContainer,
                     "RecordStartContainer called with wrong Image");
   mState |= stateHasSize;
   mImageStatus |= imgIRequest::STATUS_SIZE_AVAILABLE;
@@ -638,16 +626,17 @@ void
 imgStatusTracker::RecordStartRequest()
 {
   // We're starting a new load, so clear any status and state bits indicating
   // load/decode
   mImageStatus &= ~imgIRequest::STATUS_LOAD_PARTIAL;
   mImageStatus &= ~imgIRequest::STATUS_LOAD_COMPLETE;
   mImageStatus &= ~imgIRequest::STATUS_FRAME_COMPLETE;
   mState &= ~stateRequestStarted;
+  mState &= ~stateDecodeStarted;
   mState &= ~stateDecodeStopped;
   mState &= ~stateRequestStopped;
   mState &= ~stateBlockingOnload;
 
   mState |= stateRequestStarted;
 }
 
 void
@@ -699,18 +688,18 @@ imgStatusTracker::OnStopRequest(bool aLa
 
   RecordStopRequest(aLastPart, aStatus);
   /* notify the kids */
   nsTObserverArray<imgRequestProxy*>::ForwardIterator srIter(mConsumers);
   while (srIter.HasMore()) {
     SendStopRequest(srIter.GetNext(), aLastPart, aStatus);
   }
 
-  if (NS_FAILED(aStatus) && !preexistingError && GetRequest()) {
-    FireFailureNotification(GetRequest());
+  if (NS_FAILED(aStatus) && !preexistingError) {
+    FireFailureNotification();
   }
 }
 
 void
 imgStatusTracker::OnDataAvailable()
 {
   // Notify any imgRequestProxys that are observing us that we have an Image.
   nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mConsumers);
@@ -762,12 +751,20 @@ imgStatusTracker::MaybeUnblockOnload()
 
   nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mConsumers);
   while (iter.HasMore()) {
     SendUnblockOnload(iter.GetNext());
   }
 }
 
 void
-imgStatusTracker::ClearRequest()
+imgStatusTracker::FireFailureNotification()
 {
-  mRequest = nullptr;
+  // Some kind of problem has happened with image decoding.
+  // Report the URI to net:failed-to-process-uri-conent observers.
+  nsIURI* uri = GetImage()->GetURI();
+  if (uri) {
+    nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
+    if (os) {
+      os->NotifyObservers(uri, "net:failed-to-process-uri-content", nullptr);
+    }
+  }
 }
--- a/image/src/imgStatusTracker.h
+++ b/image/src/imgStatusTracker.h
@@ -3,40 +3,41 @@
  * 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 imgStatusTracker_h__
 #define imgStatusTracker_h__
 
 class imgIContainer;
-class imgRequest;
 class imgRequestProxy;
 class imgStatusNotifyRunnable;
 class imgRequestNotifyRunnable;
 class imgStatusTracker;
 struct nsIntRect;
 namespace mozilla {
 namespace image {
 class Image;
 } // namespace image
 } // namespace mozilla
 
 
+#include "mozilla/RefPtr.h"
 #include "nsCOMPtr.h"
 #include "nsAutoPtr.h"
 #include "nsTObserverArray.h"
 #include "nsIRunnable.h"
 #include "nscore.h"
 #include "nsWeakReference.h"
 #include "imgIDecoderObserver.h"
 
 enum {
   stateRequestStarted    = 1u << 0,
   stateHasSize           = 1u << 1,
+  stateDecodeStarted     = 1u << 2,
   stateDecodeStopped     = 1u << 3,
   stateFrameStopped      = 1u << 4,
   stateRequestStopped    = 1u << 5,
   stateBlockingOnload    = 1u << 6
 };
 
 class imgStatusTrackerObserver : public imgIDecoderObserver,
                                  public nsSupportsWeakReference
@@ -65,37 +66,40 @@ private:
  * to imgRequestProxys, both synchronously (i.e., the status now) and
  * asynchronously (the status later).
  *
  * When a new proxy needs to be notified of the current state of an image, call
  * the Notify() method on this class with the relevant proxy as its argument,
  * and the notifications will be replayed to the proxy asynchronously.
  */
 
-class imgStatusTracker
+class imgStatusTracker : public mozilla::RefCounted<imgStatusTracker>
 {
 public:
   // aImage is the image that this status tracker will pass to the
   // imgRequestProxys in SyncNotify() and EmulateRequestFinished(), and must be
   // alive as long as this instance is, because we hold a weak reference to it.
-  imgStatusTracker(mozilla::image::Image* aImage, imgRequest* aRequest);
+  imgStatusTracker(mozilla::image::Image* aImage);
   imgStatusTracker(const imgStatusTracker& aOther);
 
   // Image-setter, for imgStatusTrackers created by imgRequest::Init, which
   // are created before their Image is created.  This method should only
   // be called once, and only on an imgStatusTracker that was initialized
   // without an image.
   void SetImage(mozilla::image::Image* aImage);
 
+  // Inform this status tracker that it is associated with a multipart image.
+  void SetIsMultipart() { mIsMultipart = true; }
+
   // Schedule an asynchronous "replaying" of all the notifications that would
   // have to happen to put us in the current state.
   // We will also take note of any notifications that happen between the time
   // Notify() is called and when we call SyncNotify on |proxy|, and replay them
   // as well.
-  void Notify(imgRequest* request, imgRequestProxy* proxy);
+  void Notify(imgRequestProxy* proxy);
 
   // Schedule an asynchronous "replaying" of all the notifications that would
   // have to happen to put us in the state we are in right now.
   // Unlike Notify(), does *not* take into account future notifications.
   // This is only useful if you do not have an imgRequest, e.g., if you are a
   // static request returned from imgIRequest::GetStaticRequest().
   void NotifyCurrentState(imgRequestProxy* proxy);
 
@@ -143,16 +147,18 @@ public:
   // StartContainer, StopRequest.
   void RecordLoaded();
 
   // Shorthand for recording all the decode notifications: StartDecode,
   // StartFrame, DataAvailable, StopFrame, StopDecode.
   void RecordDecoded();
 
   /* non-virtual imgIDecoderObserver methods */
+  void RecordStartDecode();
+  void SendStartDecode(imgRequestProxy* aProxy);
   void RecordStartContainer(imgIContainer* aContainer);
   void SendStartContainer(imgRequestProxy* aProxy);
   void RecordDataAvailable();
   void SendDataAvailable(imgRequestProxy* aProxy, const nsIntRect* aRect);
   void RecordStopFrame();
   void SendStopFrame(imgRequestProxy* aProxy);
   void RecordStopDecode(nsresult statusg);
   void SendStopDecode(imgRequestProxy* aProxy, nsresult aStatus);
@@ -181,41 +187,41 @@ public:
   // is different from all the other notifications.
   void RecordBlockOnload();
   void SendBlockOnload(imgRequestProxy* aProxy);
   void RecordUnblockOnload();
   void SendUnblockOnload(imgRequestProxy* aProxy);
 
   void MaybeUnblockOnload();
 
-  // Null out any reference to an associated image request
-  void ClearRequest();
+  bool IsMultipart() const { return mIsMultipart; }
 
   // Weak pointer getters - no AddRefs.
   inline mozilla::image::Image* GetImage() const { return mImage; }
-  inline imgRequest* GetRequest() const { return mRequest; }
 
   inline imgIDecoderObserver* GetDecoderObserver() { return mTrackerObserver.get(); }
 
 private:
   friend class imgStatusNotifyRunnable;
   friend class imgRequestNotifyRunnable;
   friend class imgStatusTrackerObserver;
 
+  void FireFailureNotification();
+
   nsCOMPtr<nsIRunnable> mRequestRunnable;
 
-  // Weak pointers to the image and request. The request owns the image, and
-  // the image (or the request, if there's no image) owns the status tracker.
+  // Weak pointer to the image. The image owns the status tracker.
   mozilla::image::Image* mImage;
-  imgRequest* mRequest;
-  uint32_t mState;
-  uint32_t mImageStatus;
-  bool mHadLastPart;
-  bool mBlockingOnload;
 
   // List of proxies attached to the image. Each proxy represents a consumer
   // using the image.
   nsTObserverArray<imgRequestProxy*> mConsumers;
 
   nsRefPtr<imgStatusTrackerObserver> mTrackerObserver;
+
+  uint32_t mState;
+  uint32_t mImageStatus;
+  bool mIsMultipart    : 1;
+  bool mHadLastPart    : 1;
+  bool mBlockingOnload : 1;
 };
 
 #endif