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 149163 5df5c70b3002a3fc8aaef6171cb7435b89bb9b68
parent 149162 22c38ee36fe9c1f440b3965261a1dde7b1a95ed6
child 149164 bbf7f7deb76f218d050a3d57b26280bfa9bf4cd7
push id25374
push usercbook@mozilla.com
push dateSun, 29 Sep 2013 09:37:16 +0000
treeherdermozilla-central@8f805d3ef377 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersseth
bugs867755
milestone27.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 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