Bug 505385 - Part 13: Remove OnStopContainer and make OnStopDecode a true decode notification. r=joe
authorJosh Matthews <josh@joshmatthews.net>
Thu, 11 Oct 2012 21:34:23 -0400
changeset 110157 3cd2146a9d6eb1f49db9b86e74787fc03dfef6ca
parent 110156 2d223dd76a6270141031cb467a81da8d56c27e9e
child 110158 64a67cc6691bd4fc606fb649fa483c9b4267b7cd
push id93
push usernmatsakis@mozilla.com
push dateWed, 31 Oct 2012 21:26:57 +0000
reviewersjoe
bugs505385
milestone19.0a1
Bug 505385 - Part 13: Remove OnStopContainer and make OnStopDecode a true decode notification. r=joe
content/base/src/nsImageLoadingContent.cpp
content/base/src/nsImageLoadingContent.h
content/html/document/src/ImageDocument.cpp
content/svg/content/src/nsSVGFilters.cpp
image/public/imgIDecoderObserver.idl
image/public/imgINotificationObserver.idl
image/public/imgIScriptedNotificationObserver.idl
image/src/Decoder.cpp
image/src/ScriptedNotificationObserver.cpp
image/src/VectorImage.cpp
image/src/imgRequestProxy.cpp
image/src/imgRequestProxy.h
image/src/imgStatusTracker.cpp
image/src/imgStatusTracker.h
image/test/mochitest/imgutils.js
image/test/unit/async_load_tests.js
image/test/unit/image_load_helpers.js
layout/generic/nsImageFrame.cpp
layout/generic/nsImageFrame.h
layout/svg/nsSVGImageFrame.cpp
layout/xul/base/src/nsImageBoxFrame.cpp
layout/xul/base/src/nsImageBoxFrame.h
--- a/content/base/src/nsImageLoadingContent.cpp
+++ b/content/base/src/nsImageLoadingContent.cpp
@@ -129,17 +129,17 @@ NS_IMETHODIMP
 nsImageLoadingContent::Notify(imgIRequest* aRequest,
                               int32_t aType,
                               const nsIntRect* aData)
 {
   if (aType == imgINotificationObserver::IS_ANIMATED) {
     return OnImageIsAnimated(aRequest);
   }
 
-  if (aType == imgINotificationObserver::STOP_DECODE) {
+  if (aType == imgINotificationObserver::STOP_REQUEST) {
     // We should definitely have a request here
     NS_ABORT_IF_FALSE(aRequest, "no request?");
 
     NS_PRECONDITION(aRequest == mCurrentRequest || aRequest == mPendingRequest,
                     "Unknown request");
   }
 
   NS_ENSURE_TRUE(nsContentUtils::IsCallerChrome(), NS_ERROR_NOT_AVAILABLE);
@@ -149,37 +149,31 @@ nsImageLoadingContent::Notify(imgIReques
   }
 
   if (aType == imgINotificationObserver::START_CONTAINER) {
     // Have to check for state changes here, since we might have been in
     // the LOADING state before.
     UpdateImageState(true);
   }
 
-  if (aType == imgINotificationObserver::STOP_DECODE) {
+  if (aType == imgINotificationObserver::STOP_REQUEST) {
     uint32_t reqStatus;
     aRequest->GetImageStatus(&reqStatus);
     nsresult status =
         reqStatus & imgIRequest::STATUS_ERROR ? NS_ERROR_FAILURE : NS_OK;
-    return OnStopDecode(aRequest, status);
+    return OnStopRequest(aRequest, status);
   }
 
   return NS_OK;
 }
 
-// Warning - This isn't actually fired when decode is complete. Rather, it's
-// fired when load is complete. See bug 505385, and in the mean time use
-// OnStopContainer.
 nsresult
-nsImageLoadingContent::OnStopDecode(imgIRequest* aRequest,
-                                    nsresult aStatus)
+nsImageLoadingContent::OnStopRequest(imgIRequest* aRequest,
+                                     nsresult aStatus)
 {
-  // XXXbholley - When we fix bug 505385,  everything here should go in
-  // OnStopRequest.
-
   // Our state may change. Watch it.
   AutoStateChanger changer(this, true);
 
   // If the pending request is loaded, switch to it.
   if (aRequest == mPendingRequest) {
     MakePendingRequestCurrent();
   }
   NS_ABORT_IF_FALSE(aRequest == mCurrentRequest,
--- a/content/base/src/nsImageLoadingContent.h
+++ b/content/base/src/nsImageLoadingContent.h
@@ -154,17 +154,17 @@ protected:
    */
   virtual mozilla::CORSMode GetCORSMode();
 
   // Subclasses are *required* to call BindToTree/UnbindFromTree.
   void BindToTree(nsIDocument* aDocument, nsIContent* aParent,
                   nsIContent* aBindingParent, bool aCompileEventHandlers);
   void UnbindFromTree(bool aDeep, bool aNullParent);
 
-  nsresult OnStopDecode(imgIRequest* aRequest, nsresult aStatus);
+  nsresult OnStopRequest(imgIRequest* aRequest, nsresult aStatus);
   nsresult OnImageIsAnimated(imgIRequest *aRequest);
 
 private:
   /**
    * Struct used to manage the image observers.
    */
   struct ImageObserver {
     ImageObserver(imgINotificationObserver* aObserver) :
--- a/content/html/document/src/ImageDocument.cpp
+++ b/content/html/document/src/ImageDocument.cpp
@@ -114,17 +114,17 @@ protected:
     return NS_MIN((float)mVisibleWidth / mImageWidth,
                   (float)mVisibleHeight / mImageHeight);
   }
 
   void ResetZoomLevel();
   float GetZoomLevel();
 
   nsresult OnStartContainer(imgIRequest* aRequest, imgIContainer* aImage);
-  nsresult OnStopDecode(imgIRequest *aRequest, nsresult aStatus);
+  nsresult OnStopRequest(imgIRequest *aRequest, nsresult aStatus);
 
   nsCOMPtr<nsIContent>          mImageContent;
 
   int32_t                       mVisibleWidth;
   int32_t                       mVisibleHeight;
   int32_t                       mImageWidth;
   int32_t                       mImageHeight;
 
@@ -509,17 +509,17 @@ NS_IMETHODIMP
 ImageDocument::Notify(imgIRequest* aRequest, int32_t aType, const nsIntRect* aData)
 {
   if (aType == imgINotificationObserver::START_CONTAINER) {
     nsCOMPtr<imgIContainer> image;
     aRequest->GetImage(getter_AddRefs(image));
     return OnStartContainer(aRequest, image);
   }
 
-  if (aType == imgINotificationObserver::STOP_CONTAINER) {
+  if (aType == imgINotificationObserver::STOP_DECODE) {
     if (mImageContent) {
       // Update the background-color of the image only after the
       // image has been decoded to prevent flashes of just the
       // background-color.
       mImageContent->SetAttr(kNameSpaceID_None, nsGkAtoms::_class,
                              NS_LITERAL_STRING("decoded"), true);
     }
   }
@@ -528,22 +528,22 @@ ImageDocument::Notify(imgIRequest* aRequ
     // mImageContent can be null if the document is already destroyed
     if (mImageContent) {
       // Remove any decoded-related styling when the image is unloaded.
       mImageContent->UnsetAttr(kNameSpaceID_None, nsGkAtoms::_class,
                                true);
     }
   }
 
-  if (aType == imgINotificationObserver::STOP_DECODE) {
+  if (aType == imgINotificationObserver::STOP_REQUEST) {
     uint32_t reqStatus;
     aRequest->GetImageStatus(&reqStatus);
     nsresult status =
         reqStatus & imgIRequest::STATUS_ERROR ? NS_ERROR_FAILURE : NS_OK;
-    return OnStopDecode(aRequest, status);
+    return OnStopRequest(aRequest, status);
   }
 
   return NS_OK;
 }
 
 nsresult
 ImageDocument::OnStartContainer(imgIRequest* aRequest, imgIContainer* aImage)
 {
@@ -553,18 +553,18 @@ ImageDocument::OnStartContainer(imgIRequ
     NS_NewRunnableMethod(this, &ImageDocument::DefaultCheckOverflowing);
   nsContentUtils::AddScriptRunner(runnable);
   UpdateTitleAndCharset();
 
   return NS_OK;
 }
 
 nsresult
-ImageDocument::OnStopDecode(imgIRequest *aRequest,
-                            nsresult aStatus)
+ImageDocument::OnStopRequest(imgIRequest *aRequest,
+                             nsresult aStatus)
 {
   UpdateTitleAndCharset();
 
   // mImageContent can be null if the document is already destroyed
   if (NS_FAILED(aStatus) && mStringBundle && mImageContent) {
     nsAutoCString src;
     mDocumentURI->GetSpec(src);
     NS_ConvertUTF8toUTF16 srcString(src);
--- a/content/svg/content/src/nsSVGFilters.cpp
+++ b/content/svg/content/src/nsSVGFilters.cpp
@@ -5766,17 +5766,17 @@ nsSVGFEImageElement::Notify(imgIRequest*
   if (aType == imgINotificationObserver::START_CONTAINER) {
     // Request a decode
     nsCOMPtr<imgIContainer> container;
     aRequest->GetImage(getter_AddRefs(container));
     NS_ABORT_IF_FALSE(container, "who sent the notification then?");
     container->StartDecoding();
   }
 
-  if (aType == imgINotificationObserver::STOP_DECODE ||
+  if (aType == imgINotificationObserver::STOP_REQUEST ||
       aType == imgINotificationObserver::FRAME_CHANGED ||
       aType == imgINotificationObserver::START_CONTAINER) {
     Invalidate();
   }
 
   return rv;
 }
 
--- a/image/public/imgIDecoderObserver.idl
+++ b/image/public/imgIDecoderObserver.idl
@@ -38,17 +38,17 @@ interface imgIContainer;
  * all, some, or none of the notifications may fire before the call returns.
  *
  * This interface will be cleaned up in bug 505385.
  *
  * @author Stuart Parmenter <pavlov@netscape.com>
  * @version 0.1
  * @see imagelib2
  */
-[scriptable, uuid(5ca71b89-1a2f-475f-881d-d76c1531c4c8)]
+[scriptable, uuid(7abf38ca-242b-413a-ab18-80532dd81133)]
 interface imgIDecoderObserver : imgIContainerObserver
 {
   /**
    * Load notification.
    *
    * called at the same time that nsIRequestObserver::onStartRequest would be
    * (used only for observers of imgIRequest objects, which are nsIRequests,
    * not imgIDecoder objects)
@@ -91,21 +91,16 @@ interface imgIDecoderObserver : imgICont
   /**
    * Decode notification.
    *
    * called when a frame is finished decoding.
    */
   void onStopFrame(in imgIRequest aRequest, in unsigned long aFrame);
 
   /**
-   * Do not implement this. It is useless and going away.
-   */
-  void onStopContainer(in imgIRequest aRequest, in imgIContainer aContainer);
-
-  /**
    * Notification for when an image is known to be animated. This should be
    * fired at the earliest possible time.
    */
   void onImageIsAnimated(in imgIRequest aRequest);
 
   /**
    * In theory a decode notification, but currently a load notification.
    *
@@ -113,18 +108,17 @@ interface imgIDecoderObserver : imgICont
    * this is currently the only way to signal decoding errors to consumers,
    * and the only decoding errors that consumers care about (indeed, the only
    * ones that they're prepared to hear about) are failures to instantiate the
    * decoder (<img src="foo.html"> for example). Thus, currently this is just
    * a companion to onStopDecode to signal success or failure. This will be
    * revisited in bug 505385. If you're thinking of doing something new with
    * this, please talk to bholley first.
    */
-  void onStopDecode(in imgIRequest aRequest, in nsresult status,
-                    in wstring statusArg);
+  void onStopDecode(in imgIRequest aRequest, in nsresult status);
 
   /**
    * Load notification.
    *
    * called at the same time that nsIRequestObserver::onStopRequest would be
    * (used only for observers of imgIRequest objects, which are nsIRequests,
    * not imgIDecoder objects)
    */
--- a/image/public/imgINotificationObserver.idl
+++ b/image/public/imgINotificationObserver.idl
@@ -9,25 +9,24 @@
 interface imgIRequest;
 
 %{C++
 #include "nsRect.h"
 %}
 
 [ptr] native nsIntRect(nsIntRect);
 
-[scriptable, builtinclass, uuid(bf9ed307-02a5-4732-b3eb-659bde5de84f)]
+[scriptable, builtinclass, uuid(9c606ef4-a8e9-45c0-9721-9d6171227ca5)]
 interface imgINotificationObserver : nsISupports
 {
   const long START_REQUEST = 1;
   const long START_CONTAINER = 2;
   const long START_FRAME = 3;
   const long DATA_AVAILABLE = 4;
   const long STOP_FRAME = 5;
-  const long STOP_CONTAINER = 6;
   const long STOP_DECODE = 7;
   const long DISCARD = 8;
   const long IS_ANIMATED = 9;
   const long FRAME_CHANGED = 10;
   const long STOP_REQUEST = 11;
   const long START_DECODE = 12;
 
   [noscript] void notify(in imgIRequest aProxy, in long aType, [const] in nsIntRect aRect);
--- a/image/public/imgIScriptedNotificationObserver.idl
+++ b/image/public/imgIScriptedNotificationObserver.idl
@@ -3,24 +3,23 @@
  * 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 "nsISupports.idl"
 
 interface imgIRequest;
 
-[scriptable, uuid(1296bf6c-6067-424b-ba8e-389ec89ee48b)]
+[scriptable, uuid(c5a58b5f-cd1f-4c47-a5ed-5013e0b53e95)]
 interface imgIScriptedNotificationObserver : nsISupports
 {
   void startRequest(in imgIRequest aRequest);
   void startContainer(in imgIRequest aRequest);
   void startFrame(in imgIRequest aRequest);
   void startDecode(in imgIRequest aRequest);
   void dataAvailable(in imgIRequest aRequest);
   void stopFrame(in imgIRequest aRequest);
-  void stopContainer(in imgIRequest aRequest);
   void stopDecode(in imgIRequest aRequest);
   void stopRequest(in imgIRequest aRequest);
   void discard(in imgIRequest aRequest);
   void isAnimated(in imgIRequest aRequest);
   void frameChanged(in imgIRequest aRequest);
 };
--- a/image/src/Decoder.cpp
+++ b/image/src/Decoder.cpp
@@ -119,18 +119,17 @@ Decoder::Finish()
     bool salvage = !HasDecoderError() && mImage.GetNumFrames();
 
     // If we're salvaging, say we finished decoding
     if (salvage)
       mImage.DecodingComplete();
 
     // Fire teardown notifications
     if (mObserver) {
-      mObserver->OnStopContainer(nullptr, &mImage);
-      mObserver->OnStopDecode(nullptr, salvage ? NS_OK : NS_ERROR_FAILURE, nullptr);
+      mObserver->OnStopDecode(nullptr, salvage ? NS_OK : NS_ERROR_FAILURE);
     }
   }
 }
 
 void
 Decoder::FinishSharedDecoder()
 {
   if (!HasError()) {
@@ -273,18 +272,17 @@ Decoder::PostDecodeDone()
   bool isNonPremult = GetDecodeFlags() & DECODER_NO_PREMULTIPLY_ALPHA;
   for (int i = 0; i < frames; i++) {
     mImage.SetFrameAsNonPremult(i, isNonPremult);
   }
 
   // Notify
   mImage.DecodingComplete();
   if (mObserver) {
-    mObserver->OnStopContainer(nullptr, &mImage);
-    mObserver->OnStopDecode(nullptr, NS_OK, nullptr);
+    mObserver->OnStopDecode(nullptr, NS_OK);
   }
 }
 
 void
 Decoder::PostDataError()
 {
   mDataError = true;
 }
--- a/image/src/ScriptedNotificationObserver.cpp
+++ b/image/src/ScriptedNotificationObserver.cpp
@@ -38,22 +38,20 @@ ScriptedNotificationObserver::Notify(img
   if (aType == imgINotificationObserver::START_FRAME)
     return mInner->StartFrame(aRequest);
   if (aType == imgINotificationObserver::START_DECODE)
     return mInner->StartDecode(aRequest);
   if (aType == imgINotificationObserver::DATA_AVAILABLE)
     return mInner->DataAvailable(aRequest);
   if (aType == imgINotificationObserver::STOP_FRAME)
     return mInner->StopFrame(aRequest);
-  if (aType == imgINotificationObserver::STOP_CONTAINER)
-    return mInner->StopContainer(aRequest);
   if (aType == imgINotificationObserver::STOP_DECODE)
     return mInner->StopDecode(aRequest);
   if (aType == imgINotificationObserver::STOP_REQUEST)
     return mInner->StopRequest(aRequest);
   if (aType == imgINotificationObserver::DISCARD)
-    return mInner->StopRequest(aRequest);
+    return mInner->Discard(aRequest);
   if (aType == imgINotificationObserver::IS_ANIMATED)
     return mInner->IsAnimated(aRequest);
   if (aType == imgINotificationObserver::FRAME_CHANGED)
     return mInner->FrameChanged(aRequest);
   return NS_OK;
 }
--- a/image/src/VectorImage.cpp
+++ b/image/src/VectorImage.cpp
@@ -680,17 +680,17 @@ VectorImage::OnStopRequest(nsIRequest* a
   // Tell *our* observers that we're done loading
   nsCOMPtr<imgIDecoderObserver> observer = do_QueryReferent(mObserver);
   if (observer) {
     // NOTE: This signals that width/height are available.
     observer->OnStartContainer(nullptr, this);
 
     observer->FrameChanged(nullptr, this, &nsIntRect::GetMaxSizedIntRect());
     observer->OnStopFrame(nullptr, 0);
-    observer->OnStopDecode(nullptr, NS_OK, nullptr);
+    observer->OnStopDecode(nullptr, NS_OK);
   }
   EvaluateAnimation();
 
   return rv;
 }
 
 //------------------------------------------------------------------------------
 // nsIStreamListener method
--- a/image/src/imgRequestProxy.cpp
+++ b/image/src/imgRequestProxy.cpp
@@ -677,40 +677,29 @@ void imgRequestProxy::OnStopFrame(uint32
 
   if (mListener && !mCanceled) {
     // Hold a ref to the listener while we call it, just in case.
     nsCOMPtr<imgINotificationObserver> kungFuDeathGrip(mListener);
     mListener->Notify(this, imgINotificationObserver::STOP_FRAME, nullptr);
   }
 }
 
-void imgRequestProxy::OnStopContainer(imgIContainer *image)
-{
-  LOG_FUNC(gImgLog, "imgRequestProxy::OnStopContainer");
-
-  if (mListener && !mCanceled) {
-    // Hold a ref to the listener while we call it, just in case.
-    nsCOMPtr<imgINotificationObserver> kungFuDeathGrip(mListener);
-    mListener->Notify(this, imgINotificationObserver::STOP_CONTAINER, nullptr);
-  }
-
-  // Multipart needs reset for next OnStartContainer
-  if (mOwner && mOwner->GetMultipart())
-    mSentStartContainer = false;
-}
-
-void imgRequestProxy::OnStopDecode(nsresult status, const PRUnichar *statusArg)
+void imgRequestProxy::OnStopDecode()
 {
   LOG_FUNC(gImgLog, "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::STOP_DECODE, nullptr);
   }
+
+  // Multipart needs reset for next OnStartContainer
+  if (mOwner && mOwner->GetMultipart())
+    mSentStartContainer = false;
 }
 
 void imgRequestProxy::OnDiscard()
 {
   LOG_FUNC(gImgLog, "imgRequestProxy::OnDiscard");
 
   if (mListener && !mCanceled) {
     // Hold a ref to the listener while we call it, just in case.
--- a/image/src/imgRequestProxy.h
+++ b/image/src/imgRequestProxy.h
@@ -132,18 +132,17 @@ protected:
   // notifications.
 
   /* non-virtual imgIDecoderObserver methods */
   void OnStartDecode     ();
   void OnStartContainer  (imgIContainer *aContainer);
   void OnStartFrame      (uint32_t aFrame);
   void OnDataAvailable   (bool aCurrentFrame, const nsIntRect * aRect);
   void OnStopFrame       (uint32_t aFrame);
-  void OnStopContainer   (imgIContainer *aContainer);
-  void OnStopDecode      (nsresult status, const PRUnichar *statusArg);
+  void OnStopDecode      ();
   void OnDiscard         ();
   void OnImageIsAnimated ();
 
   /* non-virtual imgIContainerObserver methods */
   void FrameChanged(imgIContainer *aContainer,
                     const nsIntRect *aDirtyRect);
 
   /* non-virtual sort-of-nsIRequestObserver methods */
--- a/image/src/imgStatusTracker.cpp
+++ b/image/src/imgStatusTracker.cpp
@@ -167,87 +167,51 @@ NS_IMETHODIMP imgStatusTrackerObserver::
     mTracker->SendStopFrame(iter.GetNext(), frame);
   }
 
   mTracker->MaybeUnblockOnload();
 
   return NS_OK;
 }
 
-/* void onStopContainer (in imgIRequest request, in imgIContainer image); */
-NS_IMETHODIMP imgStatusTrackerObserver::OnStopContainer(imgIRequest *request,
-                                          imgIContainer *image)
-{
-  LOG_SCOPE(gImgLog, "imgStatusTrackerObserver::OnStopContainer");
-  NS_ABORT_IF_FALSE(mTracker->GetImage(),
-                    "OnDataContainer callback before we've created our image");
-
-  mTracker->RecordStopContainer(image);
-
-  nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mTracker->mConsumers);
-  while (iter.HasMore()) {
-    mTracker->SendStopContainer(iter.GetNext(), image);
-  }
-
-  // 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. In
-  // theory we would just hook in at OnStopDecode, but OnStopDecode is broken
-  // until we fix bug 505385. OnStopContainer is actually going away at that
-  // point. So for now we take advantage of the fact that OnStopContainer is
-  // always fired in the decoders at the same time as OnStopDecode.
-  mTracker->MaybeUnblockOnload();
-
-  return NS_OK;
-}
-
 /* void onStopDecode (in imgIRequest request, in nsresult status, in wstring statusArg); */
 NS_IMETHODIMP imgStatusTrackerObserver::OnStopDecode(imgIRequest *aRequest,
-                                       nsresult aStatus,
-                                       const PRUnichar *aStatusArg)
+                                                     nsresult aStatus)
 {
   LOG_SCOPE(gImgLog, "imgStatusTrackerObserver::OnStopDecode");
   NS_ABORT_IF_FALSE(mTracker->GetImage(),
-                    "OnDataDecode callback before we've created our image");
+                    "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.
   mTracker->GetRequest()->UpdateCacheEntrySize();
 
-  mTracker->RecordStopDecode(aStatus, aStatusArg);
+  mTracker->RecordStopDecode(aStatus);
 
   nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mTracker->mConsumers);
   while (iter.HasMore()) {
-    mTracker->SendStopDecode(iter.GetNext(), aStatus, aStatusArg);
+    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)) {
     // 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;
       mTracker->GetRequest()->GetURI(getter_AddRefs(uri));
       os->NotifyObservers(uri, "net:failed-to-process-uri-content", nullptr);
     }
   }
 
-  // RasterImage and everything below it is completely correct and
-  // bulletproof about its handling of decoder notifications.
-  // Unfortunately, here and above we have to make some gross and
-  // inappropriate use of things to get things to work without
-  // completely overhauling the decoder observer interface (this will,
-  // thankfully, happen in bug 505385). From imgRequest and above (for
-  // the time being), OnStopDecode is just a companion to OnStopRequest
-  // that signals success or failure of the _load_ (not the _decode_).
-  // Within imgStatusTracker, we ignore OnStopDecode notifications from the
-  // decoder and RasterImage and generate our own every time we send
-  // OnStopRequest. From within SendStopDecode, we actually send
-  // OnStopContainer.  For more information, see bug 435296.
-
   return NS_OK;
 }
 
 NS_IMETHODIMP imgStatusTrackerObserver::OnStopRequest(imgIRequest *aRequest,
                                         bool aLastPart)
 {
   NS_NOTREACHED("imgRequest(imgIDecoderObserver)::OnStopRequest");
   return NS_OK;
@@ -508,26 +472,22 @@ imgStatusTracker::SyncNotify(imgRequestP
     bool isAnimated = false;
 
     nsresult rv = mImage->GetAnimated(&isAnimated);
     if (NS_SUCCEEDED(rv) && isAnimated) {
       proxy->OnImageIsAnimated();
     }
   }
 
-  // See bug 505385 and imgStatusTrackerObserver::OnStopDecode for more information on why we
-  // call OnStopContainer based on stateDecodeStopped, and why OnStopDecode is
-  // called with OnStopRequest.
   if (mState & stateDecodeStopped) {
     NS_ABORT_IF_FALSE(mImage, "stopped decoding without ever having an image?");
-    proxy->OnStopContainer(mImage);
+    proxy->OnStopDecode();
   }
 
   if (mState & stateRequestStopped) {
-    proxy->OnStopDecode(GetResultFromImageStatus(mImageStatus), nullptr);
     proxy->OnStopRequest(mHadLastPart);
   }
 }
 
 void
 imgStatusTracker::EmulateRequestFinished(imgRequestProxy* aProxy,
                                          nsresult aStatus)
 {
@@ -665,52 +625,34 @@ imgStatusTracker::RecordStopFrame(uint32
 void
 imgStatusTracker::SendStopFrame(imgRequestProxy* aProxy, uint32_t aFrame)
 {
   if (!aProxy->NotificationsDeferred())
     aProxy->OnStopFrame(aFrame);
 }
 
 void
-imgStatusTracker::RecordStopContainer(imgIContainer* aContainer)
-{
-  NS_ABORT_IF_FALSE(mImage,
-                    "RecordStopContainer called before we have an Image");
-  // No-op: see imgStatusTrackerObserver::OnStopDecode for more information
-}
-
-void
-imgStatusTracker::SendStopContainer(imgRequestProxy* aProxy, imgIContainer* aContainer)
-{
-  // No-op: see imgStatusTrackerObserver::OnStopDecode for more information
-}
-
-void
-imgStatusTracker::RecordStopDecode(nsresult aStatus, const PRUnichar* statusArg)
+imgStatusTracker::RecordStopDecode(nsresult aStatus)
 {
   NS_ABORT_IF_FALSE(mImage,
                     "RecordStopDecode called before we have an Image");
   mState |= stateDecodeStopped;
 
   if (NS_SUCCEEDED(aStatus))
     mImageStatus |= imgIRequest::STATUS_DECODE_COMPLETE;
   // If we weren't successful, clear all success status bits and set error.
   else
     mImageStatus = imgIRequest::STATUS_ERROR;
 }
 
 void
-imgStatusTracker::SendStopDecode(imgRequestProxy* aProxy, nsresult aStatus,
-                                 const PRUnichar* statusArg)
+imgStatusTracker::SendStopDecode(imgRequestProxy* aProxy, nsresult aStatus)
 {
-  // See imgStatusTrackerObserver::OnStopDecode for more information on why we call
-  // OnStopContainer from here this, and why imgRequestProxy::OnStopDecode() is
-  // called from OnStopRequest() and SyncNotify().
   if (!aProxy->NotificationsDeferred())
-    aProxy->OnStopContainer(mImage);
+    aProxy->OnStopDecode();
 }
 
 void
 imgStatusTracker::RecordDiscard()
 {
   NS_ABORT_IF_FALSE(mImage,
                     "RecordDiscard called before we have an Image");
   // Clear the state bits we no longer deserve.
@@ -811,20 +753,17 @@ imgStatusTracker::RecordStopRequest(bool
   // If we were successful in loading, note that the image is complete.
   if (NS_SUCCEEDED(aStatus))
     mImageStatus |= imgIRequest::STATUS_LOAD_COMPLETE;
 }
 
 void
 imgStatusTracker::SendStopRequest(imgRequestProxy* aProxy, bool aLastPart, nsresult aStatus)
 {
-  // See bug 505385 and imgStatusTrackerObserver::OnStopDecode for more information on why
-  // OnStopDecode is called with OnStopRequest.
   if (!aProxy->NotificationsDeferred()) {
-    aProxy->OnStopDecode(GetResultFromImageStatus(mImageStatus), nullptr);
     aProxy->OnStopRequest(aLastPart);
   }
 }
 
 void
 imgStatusTracker::OnStopRequest(bool aLastPart, nsresult aStatus)
 {
   RecordStopRequest(aLastPart, aStatus);
--- a/image/src/imgStatusTracker.h
+++ b/image/src/imgStatusTracker.h
@@ -140,34 +140,32 @@ public:
   // Call when the request is being cancelled.
   void RecordCancel();
 
   // Shorthand for recording all the load notifications: StartRequest,
   // StartContainer, StopRequest.
   void RecordLoaded();
 
   // Shorthand for recording all the decode notifications: StartDecode,
-  // StartFrame, DataAvailable, StopFrame, StopContainer, StopDecode.
+  // StartFrame, DataAvailable, StopFrame, StopDecode.
   void RecordDecoded();
 
   /* non-virtual imgIDecoderObserver methods */
   void RecordStartDecode();
   void SendStartDecode(imgRequestProxy* aProxy);
   void RecordStartContainer(imgIContainer* aContainer);
   void SendStartContainer(imgRequestProxy* aProxy, imgIContainer* aContainer);
   void RecordStartFrame(uint32_t aFrame);
   void SendStartFrame(imgRequestProxy* aProxy, uint32_t aFrame);
   void RecordDataAvailable(bool aCurrentFrame, const nsIntRect* aRect);
   void SendDataAvailable(imgRequestProxy* aProxy, bool aCurrentFrame, const nsIntRect* aRect);
   void RecordStopFrame(uint32_t aFrame);
   void SendStopFrame(imgRequestProxy* aProxy, uint32_t aFrame);
-  void RecordStopContainer(imgIContainer* aContainer);
-  void SendStopContainer(imgRequestProxy* aProxy, imgIContainer* aContainer);
-  void RecordStopDecode(nsresult status, const PRUnichar* statusArg);
-  void SendStopDecode(imgRequestProxy* aProxy, nsresult aStatus, const PRUnichar* statusArg);
+  void RecordStopDecode(nsresult statusg);
+  void SendStopDecode(imgRequestProxy* aProxy, nsresult aStatus);
   void RecordDiscard();
   void SendDiscard(imgRequestProxy* aProxy);
   void RecordImageIsAnimated();
   void SendImageIsAnimated(imgRequestProxy *aProxy);
 
   /* non-virtual imgIContainerObserver methods */
   void RecordFrameChanged(imgIContainer* aContainer,
                           const nsIntRect* aDirtyRect);
--- a/image/test/mochitest/imgutils.js
+++ b/image/test/mochitest/imgutils.js
@@ -124,16 +124,15 @@ function getImagePref(pref)
 // JS implementation of imgIScriptedNotificationObserver with stubs for all of its methods.
 function ImageDecoderObserverStub()
 {
   this.startRequest = function startRequest(aRequest)     {}
   this.startDecode = function startDecode(aRequest)       {}
   this.startContainer = function startContainer(aRequest) {}
   this.startFrame = function startFrame(aRequest)         {}
   this.stopFrame = function stopFrame(aRequest)           {}
-  this.stopContainer = function stopContainer(aRequest)   {}
   this.stopDecode = function stopDecode(aRequest)         {}
   this.stopRequest = function stopRequest(aRequest)       {}
   this.dataAvailable = function dataAvailable(aRequest)   {}
   this.discard = function discard(aRequest)               {}
   this.isAnimated = function isAnimated(aRequest)         {}
   this.frameChanged = function frameChanged(aRequest)     {}
 }
--- a/image/test/unit/async_load_tests.js
+++ b/image/test/unit/async_load_tests.js
@@ -53,17 +53,16 @@ function checkClone(other_listener, aReq
 // Ensure that all the callbacks were called on aRequest.
 function checkAllCallbacks(listener, aRequest)
 {
   do_check_neq(listener.state & START_REQUEST, 0);
   do_check_neq(listener.state & START_DECODE, 0);
   do_check_neq(listener.state & START_CONTAINER, 0);
   do_check_neq(listener.state & START_FRAME, 0);
   do_check_neq(listener.state & STOP_FRAME, 0);
-  do_check_neq(listener.state & STOP_CONTAINER, 0);
   do_check_neq(listener.state & STOP_DECODE, 0);
   do_check_neq(listener.state & STOP_REQUEST, 0);
   do_check_eq(listener.state, ALL_BITS);
 
   do_test_finished();
 }
 
 function secondLoadDone(oldlistener, aRequest)
--- a/image/test/unit/image_load_helpers.js
+++ b/image/test/unit/image_load_helpers.js
@@ -6,20 +6,20 @@
 // ImageListener.state.
 // START_REQUEST and STOP_REQUEST are also reused by ChannelListener, and
 // stored in ChannelListener.requestStatus.
 const START_REQUEST = 0x01;
 const START_DECODE = 0x02;
 const START_CONTAINER = 0x04;
 const START_FRAME = 0x08;
 const STOP_FRAME = 0x10;
-const STOP_CONTAINER = 0x20;
-const STOP_DECODE = 0x40;
-const STOP_REQUEST = 0x80;
-const ALL_BITS = 0xFF;
+const STOP_DECODE = 0x20;
+const STOP_REQUEST = 0x40;
+const ALL_BITS = START_REQUEST | START_DECODE | START_CONTAINER | START_FRAME |
+                 STOP_FRAME | STOP_DECODE | STOP_REQUEST;
 
 // An implementation of imgIDecoderObserver with the ability to call specified
 // functions on onStartRequest and onStopRequest.
 function ImageListener(start_callback, stop_callback)
 {
   this.startRequest = function onStartRequest(aRequest)
   {
     do_check_false(this.synchronous);
@@ -48,36 +48,26 @@ function ImageListener(start_callback, s
     this.state |= START_FRAME;
   }
   this.stopFrame = function onStopFrame(aRequest)
   {
     do_check_false(this.synchronous);
 
     this.state |= STOP_FRAME;
   }
-  this.stopContainer = function onStopContainer(aRequest)
-  {
-    do_check_false(this.synchronous);
-
-    this.state |= STOP_CONTAINER;
-  }
   this.stopDecode = function onStopDecode(aRequest)
   {
     do_check_false(this.synchronous);
 
     this.state |= STOP_DECODE;
   }
   this.stopRequest = function onStopRequest(aRequest)
   {
     do_check_false(this.synchronous);
 
-    // onStopDecode must always be called before, and with, onStopRequest. See
-    // imgRequest::OnStopDecode for more information.
-    do_check_true(!!(this.state & STOP_DECODE));
-
     // We have to cancel the request when we're done with it to break any
     // reference loops!
     aRequest.cancelAndForgetObserver(0);
 
     this.state |= STOP_REQUEST;
 
     if (this.stop_callback)
       this.stop_callback(this, aRequest);
--- a/layout/generic/nsImageFrame.cpp
+++ b/layout/generic/nsImageFrame.cpp
@@ -533,22 +533,22 @@ nsImageFrame::Notify(imgIRequest* aReque
     aRequest->GetImage(getter_AddRefs(image));
     return OnStartContainer(aRequest, image);
   }
 
   if (aType == imgINotificationObserver::DATA_AVAILABLE) {
     return OnDataAvailable(aRequest, aData);
   }
 
-  if (aType == imgINotificationObserver::STOP_DECODE) {
+  if (aType == imgINotificationObserver::STOP_REQUEST) {
     uint32_t imgStatus;
     aRequest->GetImageStatus(&imgStatus);
     nsresult status =
         imgStatus & imgIRequest::STATUS_ERROR ? NS_ERROR_FAILURE : NS_OK;
-    return OnStopDecode(aRequest, status);
+    return OnStopRequest(aRequest, status);
   }
 
   if (aType == imgINotificationObserver::FRAME_CHANGED) {
     nsCOMPtr<imgIContainer> image;
     aRequest->GetImage(getter_AddRefs(image));
     return FrameChanged(aRequest, image);
   }
 
@@ -625,18 +625,18 @@ nsImageFrame::OnDataAvailable(imgIReques
     InvalidateFrameWithRect(invalid, nsDisplayItem::TYPE_IMAGE);
     InvalidateFrameWithRect(invalid, nsDisplayItem::TYPE_ALT_FEEDBACK);
   }
   
   return NS_OK;
 }
 
 nsresult
-nsImageFrame::OnStopDecode(imgIRequest *aRequest,
-                           nsresult aStatus)
+nsImageFrame::OnStopRequest(imgIRequest *aRequest,
+                            nsresult aStatus)
 {
   // Check what request type we're dealing with
   nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mContent);
   NS_ASSERTION(imageLoader, "Who's notifying us??");
   int32_t loadType = nsIImageLoadingContent::UNKNOWN_REQUEST;
   imageLoader->GetRequestType(aRequest, &loadType);
   if (loadType != nsIImageLoadingContent::CURRENT_REQUEST &&
       loadType != nsIImageLoadingContent::PENDING_REQUEST) {
--- a/layout/generic/nsImageFrame.h
+++ b/layout/generic/nsImageFrame.h
@@ -209,18 +209,18 @@ protected:
                   const nsRect& aDirtyRect, imgIContainer* aImage,
                   uint32_t aFlags);
 
 protected:
   friend class nsImageListener;
   friend class nsImageLoadingContent;
   nsresult OnStartContainer(imgIRequest *aRequest, imgIContainer *aImage);
   nsresult OnDataAvailable(imgIRequest *aRequest, const nsIntRect *rect);
-  nsresult OnStopDecode(imgIRequest *aRequest,
-                        nsresult aStatus);
+  nsresult OnStopRequest(imgIRequest *aRequest,
+                         nsresult aStatus);
   nsresult FrameChanged(imgIRequest *aRequest,
                         imgIContainer *aContainer);
   /**
    * Notification that aRequest will now be the current request.
    */
   void NotifyNewCurrentRequest(imgIRequest *aRequest, nsresult aStatus);
 
 private:
--- a/layout/svg/nsSVGImageFrame.cpp
+++ b/layout/svg/nsSVGImageFrame.cpp
@@ -558,17 +558,17 @@ nsSVGImageListener::nsSVGImageListener(n
 }
 
 NS_IMETHODIMP
 nsSVGImageListener::Notify(imgIRequest *aRequest, int32_t aType, const nsIntRect* aData)
 {
   if (!mFrame)
     return NS_ERROR_FAILURE;
 
-  if (aType == imgINotificationObserver::STOP_DECODE) {
+  if (aType == imgINotificationObserver::STOP_REQUEST) {
     nsSVGUtils::InvalidateAndScheduleReflowSVG(mFrame);
   }
 
   if (aType == imgINotificationObserver::FRAME_CHANGED) {
     // No new dimensions, so we don't need to call
     // nsSVGUtils::InvalidateAndScheduleBoundsUpdate.
     nsSVGEffects::InvalidateRenderingObservers(mFrame);
     nsSVGUtils::InvalidateBounds(mFrame);
--- a/layout/xul/base/src/nsImageBoxFrame.cpp
+++ b/layout/xul/base/src/nsImageBoxFrame.cpp
@@ -581,26 +581,26 @@ nsresult
 nsImageBoxFrame::Notify(imgIRequest *aRequest, int32_t aType, const nsIntRect* aData)
 {
   if (aType == imgINotificationObserver::START_CONTAINER) {
     nsCOMPtr<imgIContainer> image;
     aRequest->GetImage(getter_AddRefs(image));
     return OnStartContainer(aRequest, image);
   }
 
-  if (aType == imgINotificationObserver::STOP_CONTAINER) {
-    return OnStopContainer(aRequest);
+  if (aType == imgINotificationObserver::STOP_DECODE) {
+    return OnStopDecode(aRequest);
   }
 
-  if (aType == imgINotificationObserver::STOP_DECODE) {
+  if (aType == imgINotificationObserver::STOP_REQUEST) {
     uint32_t imgStatus;
     aRequest->GetImageStatus(&imgStatus);
     nsresult status =
         imgStatus & imgIRequest::STATUS_ERROR ? NS_ERROR_FAILURE : NS_OK;
-    return OnStopDecode(aRequest, status);
+    return OnStopRequest(aRequest, status);
   }
 
   if (aType == imgINotificationObserver::IS_ANIMATED) {
     return OnImageIsAnimated(aRequest);
   }
 
   if (aType == imgINotificationObserver::FRAME_CHANGED) {
     return FrameChanged(aRequest);
@@ -629,26 +629,26 @@ nsresult nsImageBoxFrame::OnStartContain
   if (!(GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
     PresContext()->PresShell()->
       FrameNeedsReflow(this, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY);
   }
 
   return NS_OK;
 }
 
-nsresult nsImageBoxFrame::OnStopContainer(imgIRequest *request)
+nsresult nsImageBoxFrame::OnStopDecode(imgIRequest *request)
 {
   nsBoxLayoutState state(PresContext());
   this->Redraw(state);
 
   return NS_OK;
 }
 
-nsresult nsImageBoxFrame::OnStopDecode(imgIRequest *request,
-                                       nsresult aStatus)
+nsresult nsImageBoxFrame::OnStopRequest(imgIRequest *request,
+                                        nsresult aStatus)
 {
   if (NS_SUCCEEDED(aStatus))
     // Fire an onload DOM event.
     FireImageDOMEvent(mContent, NS_LOAD);
   else {
     // Fire an onerror DOM event.
     mIntrinsicSize.SizeTo(0, 0);
     PresContext()->PresShell()->
--- a/layout/xul/base/src/nsImageBoxFrame.h
+++ b/layout/xul/base/src/nsImageBoxFrame.h
@@ -90,18 +90,18 @@ public:
   already_AddRefed<mozilla::layers::ImageContainer> GetContainer();
 protected:
   nsImageBoxFrame(nsIPresShell* aShell, nsStyleContext* aContext);
 
   virtual void GetImageSize();
 
 private:
   nsresult OnStartContainer(imgIRequest *request, imgIContainer *image);
-  nsresult OnStopContainer(imgIRequest *request);
-  nsresult OnStopDecode(imgIRequest *request, nsresult status);
+  nsresult OnStopDecode(imgIRequest *request);
+  nsresult OnStopRequest(imgIRequest *request, nsresult status);
   nsresult OnImageIsAnimated(imgIRequest* aRequest);
   nsresult FrameChanged(imgIRequest *aRequest);
 
   nsRect mSubRect; ///< If set, indicates that only the portion of the image specified by the rect should be used.
   nsSize mIntrinsicSize;
   nsSize mImageSize;
 
   // Boolean variable to determine if the current image request has been