Bug 867755 - Add strong refcnting for derived classes of mozilla::image::Image r=seth
authorSteve Workman <sworkman@mozilla.com>
Sat, 28 Sep 2013 11:28:44 -0700
changeset 157792 5df5c70b3002a3fc8aaef6171cb7435b89bb9b68
parent 157791 22c38ee36fe9c1f440b3965261a1dde7b1a95ed6
child 157793 bbf7f7deb76f218d050a3d57b26280bfa9bf4cd7
push idunknown
push userunknown
push dateunknown
reviewersseth
bugs867755
milestone27.0a1
Bug 867755 - Add strong refcnting for derived classes of mozilla::image::Image r=seth
image/src/Image.cpp
image/src/Image.h
image/src/RasterImage.cpp
image/src/RasterImage.h
image/src/VectorImage.cpp
image/src/VectorImage.h
image/src/imgRequest.cpp
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
@@ -6,32 +6,25 @@
 #include "nsMimeTypes.h"
 
 #include "Image.h"
 
 namespace mozilla {
 namespace image {
 
 // Constructor
-ImageResource::ImageResource(imgStatusTracker* aStatusTracker,
-                             ImageURL* aURI) :
+ImageResource::ImageResource(ImageURL* aURI) :
   mURI(aURI),
   mInnerWindowId(0),
   mAnimationConsumers(0),
   mAnimationMode(kNormalAnimMode),
   mInitialized(false),
   mAnimating(false),
   mError(false)
 {
-  if (aStatusTracker) {
-    mStatusTracker = aStatusTracker;
-    mStatusTracker->SetImage(this);
-  } else {
-    mStatusTracker = new imgStatusTracker(this);
-  }
 }
 
 uint32_t
 ImageResource::SizeOfData()
 {
   if (mError)
     return 0;
 
--- a/image/src/Image.h
+++ b/image/src/Image.h
@@ -58,16 +58,17 @@ public:
    *
    * @param aMimeType The mimetype of the image.
    * @param aFlags Initialization flags of the INIT_FLAG_* variety.
    */
   virtual nsresult Init(const char* aMimeType,
                         uint32_t aFlags) = 0;
 
   virtual already_AddRefed<imgStatusTracker> GetStatusTracker() = 0;
+  virtual void SetStatusTracker(imgStatusTracker* aStatusTracker) {}
 
   /**
    * The rectangle defining the location and size of the given frame.
    */
   virtual nsIntRect FrameRect(uint32_t aWhichFrame) = 0;
 
   /**
    * The size, in bytes, occupied by the significant data portions of the image.
@@ -132,21 +133,26 @@ public:
   virtual void SetHasError() = 0;
 
   virtual ImageURL* GetURI() = 0;
 };
 
 class ImageResource : public Image
 {
 public:
-  virtual already_AddRefed<imgStatusTracker> GetStatusTracker() MOZ_OVERRIDE {
+  already_AddRefed<imgStatusTracker> GetStatusTracker() MOZ_OVERRIDE {
     nsRefPtr<imgStatusTracker> statusTracker = mStatusTracker;
     MOZ_ASSERT(statusTracker);
     return statusTracker.forget();
   }
+  void SetStatusTracker(imgStatusTracker* aStatusTracker) MOZ_OVERRIDE MOZ_FINAL {
+    MOZ_ASSERT(aStatusTracker);
+    MOZ_ASSERT(!mStatusTracker);
+    mStatusTracker = aStatusTracker;
+  }
   virtual uint32_t SizeOfData() MOZ_OVERRIDE;
 
   virtual void IncrementAnimationConsumers() MOZ_OVERRIDE;
   virtual void DecrementAnimationConsumers() MOZ_OVERRIDE;
 #ifdef DEBUG
   virtual uint32_t GetAnimationConsumers() MOZ_OVERRIDE { return mAnimationConsumers; }
 #endif
 
@@ -160,18 +166,17 @@ public:
 
   /*
    * Returns a non-AddRefed pointer to the URI associated with this image.
    * Illegal to use off-main-thread.
    */
   virtual ImageURL* GetURI() MOZ_OVERRIDE { return mURI.get(); }
 
 protected:
-  ImageResource(imgStatusTracker* aStatusTracker,
-                ImageURL* aURI);
+  ImageResource(ImageURL* 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,
--- a/image/src/RasterImage.cpp
+++ b/image/src/RasterImage.cpp
@@ -371,17 +371,17 @@ NS_IMPL_ISUPPORTS2(RasterImage, imgICont
 #else
 NS_IMPL_ISUPPORTS3(RasterImage, imgIContainer, nsIProperties,
                               imgIContainerDebug)
 #endif
 
 //******************************************************************************
 RasterImage::RasterImage(imgStatusTracker* aStatusTracker,
                          ImageURL* aURI /* = nullptr */) :
-  ImageResource(aStatusTracker, aURI), // invoke superclass's constructor
+  ImageResource(aURI), // invoke superclass's constructor
   mSize(0,0),
   mFrameDecodeFlags(DECODE_FLAGS_DEFAULT),
   mMultipartDecodedFrame(nullptr),
   mAnim(nullptr),
   mLockCount(0),
   mDecodeCount(0),
 #ifdef DEBUG
   mFramesNotified(0),
@@ -399,16 +399,18 @@ RasterImage::RasterImage(imgStatusTracke
   mHasBeenDecoded(false),
   mAnimationFinished(false),
   mFinishing(false),
   mInUpdateImageContainer(false),
   mWantFullDecode(false),
   mPendingError(false),
   mScaleRequest(nullptr)
 {
+  mStatusTrackerInit = new imgStatusTrackerInit(this, aStatusTracker);
+
   // Set up the discard tracker node.
   mDiscardTrackerNode.img = this;
   Telemetry::GetHistogramById(Telemetry::IMAGE_DECODE_COUNT)->Add(0);
 
   // Statistics
   num_containers++;
 }
 
@@ -444,16 +446,17 @@ RasterImage::~RasterImage()
     // we didn't call ShutdownDecoder and we need to do it manually.
     if (GetNumFrames() > 0) {
       imgFrame *curframe = mFrameBlender.RawGetFrame(GetNumFrames() - 1);
       curframe->UnlockImageData();
     }
   }
 
   delete mAnim;
+  mAnim = nullptr;
   delete mMultipartDecodedFrame;
 
   // Total statistics
   num_containers--;
   total_source_bytes -= mSourceData.Length();
 
   if (NS_IsMainThread()) {
     DiscardTracker::Remove(&mDiscardTrackerNode);
--- a/image/src/RasterImage.h
+++ b/image/src/RasterImage.h
@@ -712,16 +712,19 @@ private: // data
 
   ScaleResult mScaleResult;
 
   // We hold on to a bare pointer to a ScaleRequest while it's outstanding so
   // we can mark it as stopped if necessary. The ScaleWorker/DrawWorker duo
   // will inform us when to let go of this pointer.
   ScaleRequest* mScaleRequest;
 
+  // Initializes imgStatusTracker and resets it on RasterImage destruction.
+  nsAutoPtr<imgStatusTrackerInit> mStatusTrackerInit;
+
   nsresult ShutdownDecoder(eShutdownIntent aIntent);
 
   // Error handling.
   void DoError();
 
   class HandleErrorWorker : public nsRunnable
   {
   public:
--- a/image/src/VectorImage.cpp
+++ b/image/src/VectorImage.cpp
@@ -298,23 +298,24 @@ NS_IMPL_ISUPPORTS3(VectorImage,
                    nsIStreamListener,
                    nsIRequestObserver)
 
 //------------------------------------------------------------------------------
 // Constructor / Destructor
 
 VectorImage::VectorImage(imgStatusTracker* aStatusTracker,
                          ImageURL* aURI /* = nullptr */) :
-  ImageResource(aStatusTracker, aURI), // invoke superclass's constructor
+  ImageResource(aURI), // invoke superclass's constructor
   mIsInitialized(false),
   mIsFullyLoaded(false),
   mIsDrawing(false),
   mHaveAnimations(false),
   mHasPendingInvalidation(false)
 {
+  mStatusTrackerInit = new imgStatusTrackerInit(this, aStatusTracker);
 }
 
 VectorImage::~VectorImage()
 {
   CancelAllListeners();
 }
 
 //------------------------------------------------------------------------------
--- a/image/src/VectorImage.h
+++ b/image/src/VectorImage.h
@@ -93,16 +93,19 @@ private:
   bool           mIsInitialized;          // Have we been initalized?
   bool           mIsFullyLoaded;          // Has the SVG document finished loading?
   bool           mIsDrawing;              // Are we currently drawing?
   bool           mHaveAnimations;         // Is our SVG content SMIL-animated?
                                           // (Only set after mIsFullyLoaded.)
   bool           mHasPendingInvalidation; // Invalidate observers next refresh
                                           // driver tick.
 
+  // Initializes imgStatusTracker and resets it on RasterImage destruction.
+  nsAutoPtr<imgStatusTrackerInit> mStatusTrackerInit;
+
   friend class ImageFactory;
 };
 
 inline NS_IMETHODIMP VectorImage::GetAnimationMode(uint16_t *aAnimationMode) {
   return GetAnimationModeInternal(aAnimationMode);
 }
 
 inline NS_IMETHODIMP VectorImage::SetAnimationMode(uint16_t aAnimationMode) {
--- a/image/src/imgRequest.cpp
+++ b/image/src/imgRequest.cpp
@@ -795,17 +795,17 @@ imgRequest::OnDataAvailable(nsIRequest *
       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.
         this->Cancel(NS_ERROR_FAILURE);
         return NS_BINDING_ABORTED;
       }
 
-      NS_ABORT_IF_FALSE(statusTracker->GetImage(), "Status tracker should have an image!");
+      NS_ABORT_IF_FALSE(statusTracker->HasImage(), "Status tracker should have an image!");
       NS_ABORT_IF_FALSE(mImage, "imgRequest should have an image!");
 
       if (mDecodeRequested)
         mImage->StartDecoding();
     }
   }
 
   // Notify the image that it has new data.
--- a/image/src/imgRequestProxy.cpp
+++ b/image/src/imgRequestProxy.cpp
@@ -21,40 +21,42 @@ using namespace mozilla::image;
 // certain overridden functions need to be usable in the destructor.
 // Since virtual functions can't be used in that way, this class
 // provides a behavioural trait for each class to use instead.
 class ProxyBehaviour
 {
  public:
   virtual ~ProxyBehaviour() {}
 
-  virtual mozilla::image::Image* GetImage() const = 0;
+  virtual already_AddRefed<mozilla::image::Image> GetImage() const = 0;
+  virtual bool HasImage() const = 0;
   virtual already_AddRefed<imgStatusTracker> GetStatusTracker() const = 0;
   virtual imgRequest* GetOwner() const = 0;
   virtual void SetOwner(imgRequest* aOwner) = 0;
 };
 
 class RequestBehaviour : public ProxyBehaviour
 {
  public:
   RequestBehaviour() : mOwner(nullptr), mOwnerHasImage(false) {}
 
-  virtual mozilla::image::Image* GetImage() const MOZ_OVERRIDE;
+  virtual already_AddRefed<mozilla::image::Image> GetImage() const MOZ_OVERRIDE;
+  virtual bool HasImage() const MOZ_OVERRIDE;
   virtual already_AddRefed<imgStatusTracker> GetStatusTracker() const MOZ_OVERRIDE;
 
   virtual imgRequest* GetOwner() const MOZ_OVERRIDE {
     return mOwner;
   }
 
   virtual void SetOwner(imgRequest* aOwner) MOZ_OVERRIDE {
     mOwner = aOwner;
 
     if (mOwner) {
       nsRefPtr<imgStatusTracker> ownerStatusTracker = GetStatusTracker();
-      mOwnerHasImage = ownerStatusTracker && ownerStatusTracker->GetImage();
+      mOwnerHasImage = ownerStatusTracker && ownerStatusTracker->HasImage();
     } else {
       mOwnerHasImage = false;
     }
   }
 
  private:
   // We maintain the following invariant:
   // The proxy is registered at most with a single imgRequest as an observer,
@@ -62,17 +64,17 @@ class RequestBehaviour : public ProxyBeh
   // imgRequestProxy::~imgRequestProxy unregisters the proxy as an observer
   // from whatever request it was registered with (if any). This, in turn,
   // means that imgRequest::mObservers will not have any stale pointers in it.
   nsRefPtr<imgRequest> mOwner;
 
   bool mOwnerHasImage;
 };
 
-mozilla::image::Image*
+already_AddRefed<mozilla::image::Image>
 RequestBehaviour::GetImage() const
 {
   if (!mOwnerHasImage)
     return nullptr;
   nsRefPtr<imgStatusTracker> statusTracker = GetStatusTracker();
   return statusTracker->GetImage();
 }
 
@@ -195,17 +197,17 @@ nsresult imgRequestProxy::ChangeOwner(im
 
   // If we're holding animation requests, undo them.
   uint32_t oldAnimationConsumers = mAnimationConsumers;
   ClearAnimationConsumers();
 
   // Were we decoded before?
   bool wasDecoded = false;
   nsRefPtr<imgStatusTracker> statusTracker = GetStatusTracker();
-  if (GetImage() &&
+  if (statusTracker->HasImage() &&
       statusTracker->GetImageStatus() & imgIRequest::STATUS_FRAME_COMPLETE) {
     wasDecoded = true;
   }
 
   GetOwner()->RemoveProxy(this, NS_IMAGELIB_CHANGING_OWNER);
 
   mBehaviour->SetOwner(aNewOwner);
 
@@ -376,64 +378,69 @@ imgRequestProxy::RequestDecode()
 }
 
 
 /* void lockImage (); */
 NS_IMETHODIMP
 imgRequestProxy::LockImage()
 {
   mLockCount++;
-  if (GetImage())
-    return GetImage()->LockImage();
+  nsRefPtr<Image> image = GetImage();
+  if (image)
+    return image->LockImage();
   return NS_OK;
 }
 
 /* void unlockImage (); */
 NS_IMETHODIMP
 imgRequestProxy::UnlockImage()
 {
   NS_ABORT_IF_FALSE(mLockCount > 0, "calling unlock but no locks!");
 
   mLockCount--;
-  if (GetImage())
-    return GetImage()->UnlockImage();
+  nsRefPtr<Image> image = GetImage();
+  if (image)
+    return image->UnlockImage();
   return NS_OK;
 }
 
 /* void requestDiscard (); */
 NS_IMETHODIMP
 imgRequestProxy::RequestDiscard()
 {
-  if (GetImage())
-    return GetImage()->RequestDiscard();
+  nsRefPtr<Image> image = GetImage();
+  if (image)
+    return image->RequestDiscard();
   return NS_OK;
 }
 
 NS_IMETHODIMP
 imgRequestProxy::IncrementAnimationConsumers()
 {
   mAnimationConsumers++;
-  if (GetImage())
-    GetImage()->IncrementAnimationConsumers();
+  nsRefPtr<Image> image = GetImage();
+  if (image)
+    image->IncrementAnimationConsumers();
   return NS_OK;
 }
 
 NS_IMETHODIMP
 imgRequestProxy::DecrementAnimationConsumers()
 {
   // We may get here if some responsible code called Increment,
   // then called us, but we have meanwhile called ClearAnimationConsumers
   // because we needed to get rid of them earlier (see
   // imgRequest::RemoveProxy), and hence have nothing left to
   // decrement. (In such a case we got rid of the animation consumers
   // early, but not the observer.)
   if (mAnimationConsumers > 0) {
     mAnimationConsumers--;
-    if (GetImage())
-      GetImage()->DecrementAnimationConsumers();
+    nsRefPtr<Image> image = GetImage();
+    if (image)
+      image->DecrementAnimationConsumers();
   }
   return NS_OK;
 }
 
 void
 imgRequestProxy::ClearAnimationConsumers()
 {
   while (mAnimationConsumers > 0)
@@ -474,30 +481,34 @@ NS_IMETHODIMP imgRequestProxy::SetLoadFl
 {
   mLoadFlags = flags;
   return NS_OK;
 }
 
 /**  imgIRequest methods **/
 
 /* attribute imgIContainer image; */
-NS_IMETHODIMP imgRequestProxy::GetImage(imgIContainer * *aImage)
+NS_IMETHODIMP imgRequestProxy::GetImage(imgIContainer **aImage)
 {
+  NS_ENSURE_TRUE(aImage, NS_ERROR_NULL_POINTER);
   // It's possible that our owner has an image but hasn't notified us of it -
   // that'll happen if we get Canceled before the owner instantiates its image
   // (because Canceling unregisters us as a listener on mOwner). If we're
   // in that situation, just grab the image off of mOwner.
-  imgIContainer* imageToReturn = GetImage();
+  nsRefPtr<Image> image = GetImage();
+  nsCOMPtr<imgIContainer> imageToReturn;
+  if (image)
+    imageToReturn = do_QueryInterface(image);
   if (!imageToReturn && GetOwner())
-    imageToReturn = GetOwner()->mImage.get();
+    imageToReturn = GetOwner()->mImage;
 
   if (!imageToReturn)
     return NS_ERROR_FAILURE;
 
-  NS_ADDREF(*aImage = imageToReturn);
+  imageToReturn.swap(*aImage);
 
   return NS_OK;
 }
 
 /* readonly attribute unsigned long imageStatus; */
 NS_IMETHODIMP imgRequestProxy::GetImageStatus(uint32_t *aStatus)
 {
   nsRefPtr<imgStatusTracker> statusTracker = GetStatusTracker();
@@ -552,17 +563,18 @@ static imgRequestProxy* NewProxy(imgRequ
 {
   return new imgRequestProxy();
 }
 
 imgRequestProxy* NewStaticProxy(imgRequestProxy* aThis)
 {
   nsCOMPtr<nsIPrincipal> currentPrincipal;
   aThis->GetImagePrincipal(getter_AddRefs(currentPrincipal));
-  return new imgRequestProxyStatic(aThis->GetImage(), currentPrincipal);
+  nsRefPtr<Image> image = aThis->GetImage();
+  return new imgRequestProxyStatic(image, currentPrincipal);
 }
 
 NS_IMETHODIMP imgRequestProxy::Clone(imgINotificationObserver* aObserver,
                                      imgIRequest** aClone)
 {
   nsresult result;
   imgRequestProxy* proxy;
   result = Clone(aObserver, &proxy);
@@ -901,17 +913,17 @@ imgRequestProxy::GetStaticRequest(imgIRe
   *aReturn = proxy;
   return result;
 }
 
 nsresult
 imgRequestProxy::GetStaticRequest(imgRequestProxy** aReturn)
 {
   *aReturn = nullptr;
-  mozilla::image::Image* image = GetImage();
+  nsRefPtr<Image> image = GetImage();
 
   bool animated;
   if (!image || (NS_SUCCEEDED(image->GetAnimated(&animated)) && !animated)) {
     // Early exit - we're not animated, so we don't have to do anything.
     NS_ADDREF(*aReturn = this);
     return NS_OK;
   }
 
@@ -946,17 +958,17 @@ void imgRequestProxy::NotifyListener()
 
   nsRefPtr<imgStatusTracker> statusTracker = GetStatusTracker();
   if (GetOwner()) {
     // Send the notifications to our listener asynchronously.
     statusTracker->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(),
+    NS_ABORT_IF_FALSE(HasImage(),
                       "if we have no imgRequest, we should have an Image");
     statusTracker->NotifyCurrentState(this);
   }
 }
 
 void imgRequestProxy::SyncNotifyListener()
 {
   // It would be nice to notify the observer directly in the status tracker
@@ -968,17 +980,17 @@ void imgRequestProxy::SyncNotifyListener
   statusTracker->SyncNotify(this);
 }
 
 void
 imgRequestProxy::SetHasImage()
 {
   nsRefPtr<imgStatusTracker> statusTracker = GetStatusTracker();
   MOZ_ASSERT(statusTracker);
-  Image* image = statusTracker->GetImage();
+  nsRefPtr<Image> image = statusTracker->GetImage();
   MOZ_ASSERT(image);
 
   // Force any private status related to the owner to reflect
   // the presence of an image;
   mBehaviour->SetOwner(mBehaviour->GetOwner());
 
   // Apply any locks we have
   for (uint32_t i = 0; i < mLockCount; ++i)
@@ -990,40 +1002,61 @@ imgRequestProxy::SetHasImage()
 }
 
 already_AddRefed<imgStatusTracker>
 imgRequestProxy::GetStatusTracker() const
 {
   return mBehaviour->GetStatusTracker();
 }
 
-mozilla::image::Image*
+already_AddRefed<mozilla::image::Image>
 imgRequestProxy::GetImage() const
 {
   return mBehaviour->GetImage();
 }
 
+bool
+RequestBehaviour::HasImage() const
+{
+  if (!mOwnerHasImage)
+    return false;
+  nsRefPtr<imgStatusTracker> statusTracker = GetStatusTracker();
+  return statusTracker ? statusTracker->HasImage() : false;
+}
+
+bool
+imgRequestProxy::HasImage() const
+{
+  return mBehaviour->HasImage();
+}
+
 imgRequest*
 imgRequestProxy::GetOwner() const
 {
   return mBehaviour->GetOwner();
 }
 
 ////////////////// imgRequestProxyStatic methods
 
 class StaticBehaviour : public ProxyBehaviour
 {
 public:
   StaticBehaviour(mozilla::image::Image* aImage) : mImage(aImage) {}
 
-  virtual mozilla::image::Image* GetImage() const MOZ_OVERRIDE {
+  virtual already_AddRefed<mozilla::image::Image>
+  GetImage() const MOZ_OVERRIDE {
+    nsRefPtr<mozilla::image::Image> image = mImage;
+    return image.forget();
+  }
+
+  virtual bool HasImage() const MOZ_OVERRIDE {
     return mImage;
   }
 
-  virtual already_AddRefed<imgStatusTracker> GetStatusTracker() const MOZ_OVERRIDE {
+  virtual already_AddRefed<imgStatusTracker> GetStatusTracker() const MOZ_OVERRIDE  {
     return mImage->GetStatusTracker();
   }
 
   virtual imgRequest* GetOwner() const MOZ_OVERRIDE {
     return nullptr;
   }
 
   virtual void SetOwner(imgRequest* aOwner) MOZ_OVERRIDE {
--- a/image/src/imgRequestProxy.h
+++ b/image/src/imgRequestProxy.h
@@ -175,17 +175,18 @@ protected:
 
   nsITimedChannel* TimedChannel()
   {
     if (!GetOwner())
       return nullptr;
     return GetOwner()->mTimedChannel;
   }
 
-  mozilla::image::Image* GetImage() const;
+  already_AddRefed<mozilla::image::Image> GetImage() const;
+  bool HasImage() const;
   imgRequest* GetOwner() const;
 
   nsresult PerformClone(imgINotificationObserver* aObserver,
                         imgRequestProxy* (aAllocFn)(imgRequestProxy*),
                         imgRequestProxy** aClone);
 
 public:
   NS_FORWARD_SAFE_NSITIMEDCHANNEL(TimedChannel())
--- a/image/src/imgStatusTracker.cpp
+++ b/image/src/imgStatusTracker.cpp
@@ -38,17 +38,17 @@ public:
 
   /** imgDecoderObserver methods **/
 
   virtual void OnStartDecode()
   {
     MOZ_ASSERT(NS_IsMainThread(),
                "Use imgStatusTracker::mConsumers on main thread only");
     LOG_SCOPE(GetImgLog(), "imgStatusTrackerNotifyingObserver::OnStartDecode");
-    NS_ABORT_IF_FALSE(mTracker->GetImage(),
+    NS_ABORT_IF_FALSE(mTracker->HasImage(),
                       "OnStartDecode callback before we've created our image");
 
     mTracker->RecordStartDecode();
 
     nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mTracker->mConsumers);
     while (iter.HasMore()) {
       mTracker->SendStartDecode(iter.GetNext());
     }
@@ -69,60 +69,63 @@ public:
   }
 
   virtual void OnStartContainer()
   {
     MOZ_ASSERT(NS_IsMainThread(),
                "Use imgStatusTracker::mConsumers on main thread only");
     LOG_SCOPE(GetImgLog(), "imgStatusTrackerNotifyingObserver::OnStartContainer");
 
-    NS_ABORT_IF_FALSE(mTracker->GetImage(),
+    NS_ABORT_IF_FALSE(mTracker->HasImage(),
                       "OnStartContainer callback before we've created our image");
-    mTracker->RecordStartContainer(mTracker->GetImage());
+    {
+      nsRefPtr<Image> image = mTracker->GetImage();
+      mTracker->RecordStartContainer(image);
+    }
 
     nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mTracker->mConsumers);
     while (iter.HasMore()) {
       mTracker->SendStartContainer(iter.GetNext());
     }
   }
 
   virtual void OnStartFrame()
   {
     LOG_SCOPE(GetImgLog(), "imgStatusTrackerNotifyingObserver::OnStartFrame");
-    NS_ABORT_IF_FALSE(mTracker->GetImage(),
+    NS_ABORT_IF_FALSE(mTracker->HasImage(),
                       "OnStartFrame callback before we've created our image");
 
     mTracker->RecordStartFrame();
 
     // This is not observed below the imgStatusTracker level, so we don't need
     // to SendStartFrame.
   }
 
   virtual void FrameChanged(const nsIntRect* dirtyRect)
   {
     MOZ_ASSERT(NS_IsMainThread(),
                "Use imgStatusTracker::mConsumers on main thread only");
     LOG_SCOPE(GetImgLog(), "imgStatusTrackerNotifyingObserver::FrameChanged");
-    NS_ABORT_IF_FALSE(mTracker->GetImage(),
+    NS_ABORT_IF_FALSE(mTracker->HasImage(),
                       "FrameChanged callback before we've created our image");
 
     mTracker->RecordFrameChanged(dirtyRect);
 
     nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mTracker->mConsumers);
     while (iter.HasMore()) {
       mTracker->SendFrameChanged(iter.GetNext(), dirtyRect);
     }
   }
 
   virtual void OnStopFrame()
   {
     MOZ_ASSERT(NS_IsMainThread(),
                "Use imgStatusTracker::mConsumers on main thread only");
     LOG_SCOPE(GetImgLog(), "imgStatusTrackerNotifyingObserver::OnStopFrame");
-    NS_ABORT_IF_FALSE(mTracker->GetImage(),
+    NS_ABORT_IF_FALSE(mTracker->HasImage(),
                       "OnStopFrame callback before we've created our image");
 
     mTracker->RecordStopFrame();
 
     nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mTracker->mConsumers);
     while (iter.HasMore()) {
       mTracker->SendStopFrame(iter.GetNext());
     }
@@ -130,17 +133,17 @@ public:
     mTracker->MaybeUnblockOnload();
   }
 
   virtual void OnStopDecode(nsresult aStatus)
   {
     MOZ_ASSERT(NS_IsMainThread(),
                "Use imgStatusTracker::mConsumers on main thread only");
     LOG_SCOPE(GetImgLog(), "imgStatusTrackerNotifyingObserver::OnStopDecode");
-    NS_ABORT_IF_FALSE(mTracker->GetImage(),
+    NS_ABORT_IF_FALSE(mTracker->HasImage(),
                       "OnStopDecode callback before we've created our image");
 
     bool preexistingError = mTracker->GetImageStatus() == imgIRequest::STATUS_ERROR;
 
     mTracker->RecordStopDecode(aStatus);
 
     nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mTracker->mConsumers);
     while (iter.HasMore()) {
@@ -160,46 +163,46 @@ public:
   {
     NS_NOTREACHED("imgStatusTrackerNotifyingObserver(imgDecoderObserver)::OnStopRequest");
   }
 
   virtual void OnDiscard()
   {
     MOZ_ASSERT(NS_IsMainThread(),
                "Use imgStatusTracker::mConsumers on main thread only");
-    NS_ABORT_IF_FALSE(mTracker->GetImage(),
+    NS_ABORT_IF_FALSE(mTracker->HasImage(),
                       "OnDiscard callback before we've created our image");
 
     mTracker->RecordDiscard();
 
     nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mTracker->mConsumers);
     while (iter.HasMore()) {
       mTracker->SendDiscard(iter.GetNext());
     }
   }
 
   virtual void OnUnlockedDraw()
   {
     MOZ_ASSERT(NS_IsMainThread(),
                "Use imgStatusTracker::mConsumers on main thread only");
-    NS_ABORT_IF_FALSE(mTracker->GetImage(),
+    NS_ABORT_IF_FALSE(mTracker->HasImage(),
                       "OnUnlockedDraw callback before we've created our image");
     mTracker->RecordUnlockedDraw();
 
     nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mTracker->mConsumers);
     while (iter.HasMore()) {
       mTracker->SendUnlockedDraw(iter.GetNext());
     }
   }
 
   virtual void OnImageIsAnimated()
   {
     MOZ_ASSERT(NS_IsMainThread(),
                "Use imgStatusTracker::mConsumers on main thread only");
-    NS_ABORT_IF_FALSE(mTracker->GetImage(),
+    NS_ABORT_IF_FALSE(mTracker->HasImage(),
                       "OnImageIsAnimated callback before we've created our image");
     mTracker->RecordImageIsAnimated();
 
     nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mTracker->mConsumers);
     while (iter.HasMore()) {
       mTracker->SendImageIsAnimated(iter.GetNext());
     }
   }
@@ -310,17 +313,17 @@ public:
     tracker->RecordDiscard();
   }
 
   virtual void OnUnlockedDraw() MOZ_OVERRIDE
   {
     LOG_SCOPE(GetImgLog(), "imgStatusTrackerObserver::OnUnlockedDraw");
     nsRefPtr<imgStatusTracker> tracker = mTracker.get();
     if (!tracker) { return; }
-    NS_ABORT_IF_FALSE(tracker->GetImage(),
+    NS_ABORT_IF_FALSE(tracker->HasImage(),
                       "OnUnlockedDraw callback before we've created our image");
     tracker->RecordUnlockedDraw();
   }
 
   virtual void OnImageIsAnimated() MOZ_OVERRIDE
   {
     LOG_SCOPE(GetImgLog(), "imgStatusTrackerObserver::OnImageIsAnimated");
     nsRefPtr<imgStatusTracker> tracker = mTracker.get();
@@ -370,24 +373,51 @@ imgStatusTracker::imgStatusTracker(const
     //  - mInvalidRect, because the point of it is to be fired off and reset
 {
   mTrackerObserver = new imgStatusTrackerObserver(this);
 }
 
 imgStatusTracker::~imgStatusTracker()
 {}
 
+imgStatusTrackerInit::imgStatusTrackerInit(mozilla::image::Image* aImage,
+                                           imgStatusTracker* aTracker)
+{
+  MOZ_ASSERT(aImage);
+
+  if (aTracker) {
+    mTracker = aTracker;
+    mTracker->SetImage(aImage);
+  } else {
+    mTracker = new imgStatusTracker(aImage);
+  }
+  aImage->SetStatusTracker(mTracker);
+  MOZ_ASSERT(mTracker);
+}
+
+imgStatusTrackerInit::~imgStatusTrackerInit()
+{
+  mTracker->ResetImage();
+}
+
 void
 imgStatusTracker::SetImage(Image* aImage)
 {
   NS_ABORT_IF_FALSE(aImage, "Setting null image");
   NS_ABORT_IF_FALSE(!mImage, "Setting image when we already have one");
   mImage = aImage;
 }
 
+void
+imgStatusTracker::ResetImage()
+{
+  NS_ABORT_IF_FALSE(mImage, "Resetting image when it's already null!");
+  mImage = nullptr;
+}
+
 bool
 imgStatusTracker::IsLoading() const
 {
   // Checking for whether OnStopRequest has fired allows us to say we're
   // loading before OnStartRequest gets called, letting the request properly
   // get removed from the cache in certain cases.
   return !(mState & stateRequestStopped);
 }
@@ -442,18 +472,18 @@ class imgRequestNotifyRunnable : public 
     nsTArray< nsRefPtr<imgRequestProxy> > mProxies;
 };
 
 void
 imgStatusTracker::Notify(imgRequestProxy* proxy)
 {
   MOZ_ASSERT(NS_IsMainThread(), "imgRequestProxy is not threadsafe");
 #ifdef PR_LOGGING
-  if (GetImage() && GetImage()->GetURI()) {
-    nsRefPtr<ImageURL> uri(GetImage()->GetURI());
+  if (mImage && mImage->GetURI()) {
+    nsRefPtr<ImageURL> uri(mImage->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
 
@@ -1201,21 +1231,21 @@ imgStatusTracker::RecordError()
 
 void
 imgStatusTracker::FireFailureNotification()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   // Some kind of problem has happened with image decoding.
   // Report the URI to net:failed-to-process-uri-conent observers.
-  if (GetImage()) {
+  if (mImage) {
     // Should be on main thread, so ok to create a new nsIURI.
     nsCOMPtr<nsIURI> uri;
     {
-      nsRefPtr<ImageURL> threadsafeUriData = GetImage()->GetURI();
+      nsRefPtr<ImageURL> threadsafeUriData = mImage->GetURI();
       uri = threadsafeUriData ? threadsafeUriData->ToIURI() : nullptr;
     }
     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
@@ -119,16 +119,20 @@ public:
   virtual ~imgStatusTracker();
 
   // 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);
 
+  // Image resetter, for when mImage is about to go out of scope. mImage is a
+  // weak reference, and thus must be set to null when it's object is deleted.
+  void ResetImage();
+
   // 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.
@@ -259,17 +263,21 @@ public:
   // Main thread only because mConsumers is not threadsafe.
   void MaybeUnblockOnload();
 
   void RecordError();
 
   bool IsMultipart() const { return mIsMultipart; }
 
   // Weak pointer getters - no AddRefs.
-  inline mozilla::image::Image* GetImage() const { return mImage; }
+  inline already_AddRefed<mozilla::image::Image> GetImage() const {
+    nsRefPtr<mozilla::image::Image> image = mImage;
+    return image.forget();
+  }
+  inline bool HasImage() { return mImage; }
 
   inline imgDecoderObserver* GetDecoderObserver() { return mTrackerObserver.get(); }
 
   already_AddRefed<imgStatusTracker> CloneForRecording();
 
   // Compute the difference between this status tracker and aOther.
   mozilla::image::ImageStatusDiff Difference(imgStatusTracker* aOther) const;
 
@@ -287,16 +295,17 @@ public:
 
   nsIntRect GetInvalidRect() const { return mInvalidRect; }
 
 private:
   friend class imgStatusNotifyRunnable;
   friend class imgRequestNotifyRunnable;
   friend class imgStatusTrackerObserver;
   friend class imgStatusTrackerNotifyingObserver;
+  friend class imgStatusTrackerInit;
   imgStatusTracker(const imgStatusTracker& aOther);
 
   // Main thread only because it deals with the observer service.
   void FireFailureNotification();
 
   // Main thread only, since imgRequestProxy calls are expected on the main
   // thread, and mConsumers is not threadsafe.
   static void SyncNotifyState(nsTObserverArray<imgRequestProxy*>& proxies,
@@ -304,26 +313,36 @@ private:
                               nsIntRect& dirtyRect, bool hadLastPart);
 
   nsCOMPtr<nsIRunnable> mRequestRunnable;
 
   // The invalid area of the most recent frame we know about. (All previous
   // frames are assumed to be fully valid.)
   nsIntRect mInvalidRect;
 
-  // Weak pointer to the image. The image owns the status tracker.
+  // This weak ref should be set null when the image goes out of scope.
   mozilla::image::Image* mImage;
 
   // List of proxies attached to the image. Each proxy represents a consumer
   // using the image. Array and/or individual elements should only be accessed
   // on the main thread.
   nsTObserverArray<imgRequestProxy*> mConsumers;
 
   mozilla::RefPtr<imgDecoderObserver> mTrackerObserver;
 
   uint32_t mState;
   uint32_t mImageStatus;
   bool mIsMultipart    : 1;
   bool mHadLastPart    : 1;
   bool mHasBeenDecoded : 1;
 };
 
+class imgStatusTrackerInit
+{
+public:
+  imgStatusTrackerInit(mozilla::image::Image* aImage,
+                       imgStatusTracker* aTracker);
+  ~imgStatusTrackerInit();
+private:
+  imgStatusTracker* mTracker;
+};
+
 #endif