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 118672 3cd2146a9d6eb1f49db9b86e74787fc03dfef6ca
parent 118671 2d223dd76a6270141031cb467a81da8d56c27e9e
child 118673 64a67cc6691bd4fc606fb649fa483c9b4267b7cd
push id273
push userlsblakk@mozilla.com
push dateThu, 14 Feb 2013 23:19:38 +0000
treeherdermozilla-release@c5e807a3f8b8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjoe
bugs505385
milestone19.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 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