Bug 505385 - Part 18: Reduce number of notifications - START_REQUEST/START_CONTAINER -> SIZE_AVAILABLE, remove START_DECODE and START_FRAME. Rename remaining ones for clarity. r=joe
authorJosh Matthews <josh@joshmatthews.net>
Thu, 11 Oct 2012 21:34:24 -0400
changeset 118678 d35c748b03b0f222767442747c7eab9fa911e9e4
parent 118677 b4ec385f733b9e2e04eea14b418a9ab2aea5b673
child 118679 510e593b160b077392085b73c849654a1aa76ac1
child 118684 2adc0ce03dba4ba6548c1a0673a4c9e4d18b473a
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 18: Reduce number of notifications - START_REQUEST/START_CONTAINER -> SIZE_AVAILABLE, remove START_DECODE and START_FRAME. Rename remaining ones for clarity. r=joe
content/base/src/nsImageLoadingContent.cpp
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/imgRequestProxy.cpp
image/src/imgRequestProxy.h
image/src/imgStatusTracker.cpp
image/src/imgStatusTracker.h
image/test/mochitest/imgutils.js
image/test/mochitest/test_animSVGImage.html
image/test/unit/async_load_tests.js
image/test/unit/image_load_helpers.js
layout/generic/nsBulletFrame.cpp
layout/generic/nsImageFrame.cpp
layout/generic/nsImageFrame.h
layout/style/ImageLoader.cpp
layout/svg/nsSVGImageFrame.cpp
layout/xul/base/src/nsImageBoxFrame.cpp
layout/xul/base/src/tree/src/nsTreeImageListener.cpp
toolkit/system/gnome/nsAlertsIconListener.cpp
widget/cocoa/nsMenuItemIconX.mm
--- a/content/base/src/nsImageLoadingContent.cpp
+++ b/content/base/src/nsImageLoadingContent.cpp
@@ -129,37 +129,35 @@ NS_IMETHODIMP
 nsImageLoadingContent::Notify(imgIRequest* aRequest,
                               int32_t aType,
                               const nsIntRect* aData)
 {
   if (aType == imgINotificationObserver::IS_ANIMATED) {
     return OnImageIsAnimated(aRequest);
   }
 
-  if (aType == imgINotificationObserver::STOP_REQUEST) {
+  if (aType == imgINotificationObserver::LOAD_COMPLETE) {
     // 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);
 
-  if (aType != imgINotificationObserver::FRAME_CHANGED) {
-    LOOP_OVER_OBSERVERS(Notify(aRequest, aType, aData));
-  }
+  LOOP_OVER_OBSERVERS(Notify(aRequest, aType, aData));
 
-  if (aType == imgINotificationObserver::START_CONTAINER) {
+  if (aType == imgINotificationObserver::SIZE_AVAILABLE) {
     // Have to check for state changes here, since we might have been in
     // the LOADING state before.
     UpdateImageState(true);
   }
 
-  if (aType == imgINotificationObserver::STOP_REQUEST) {
+  if (aType == imgINotificationObserver::LOAD_COMPLETE) {
     uint32_t reqStatus;
     aRequest->GetImageStatus(&reqStatus);
     nsresult status =
         reqStatus & imgIRequest::STATUS_ERROR ? NS_ERROR_FAILURE : NS_OK;
     return OnStopRequest(aRequest, status);
   }
 
   return NS_OK;
--- a/content/html/document/src/ImageDocument.cpp
+++ b/content/html/document/src/ImageDocument.cpp
@@ -503,23 +503,23 @@ ImageDocument::ToggleImageSize()
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 ImageDocument::Notify(imgIRequest* aRequest, int32_t aType, const nsIntRect* aData)
 {
-  if (aType == imgINotificationObserver::START_CONTAINER) {
+  if (aType == imgINotificationObserver::SIZE_AVAILABLE) {
     nsCOMPtr<imgIContainer> image;
     aRequest->GetImage(getter_AddRefs(image));
     return OnStartContainer(aRequest, image);
   }
 
-  if (aType == imgINotificationObserver::STOP_DECODE) {
+  if (aType == imgINotificationObserver::DECODE_COMPLETE) {
     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,17 +528,17 @@ 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_REQUEST) {
+  if (aType == imgINotificationObserver::LOAD_COMPLETE) {
     uint32_t reqStatus;
     aRequest->GetImageStatus(&reqStatus);
     nsresult status =
         reqStatus & imgIRequest::STATUS_ERROR ? NS_ERROR_FAILURE : NS_OK;
     return OnStopRequest(aRequest, status);
   }
 
   return NS_OK;
--- a/content/svg/content/src/nsSVGFilters.cpp
+++ b/content/svg/content/src/nsSVGFilters.cpp
@@ -5758,27 +5758,27 @@ nsSVGFEImageElement::GetStringInfo()
 //----------------------------------------------------------------------
 // imgINotificationObserver methods
 
 NS_IMETHODIMP
 nsSVGFEImageElement::Notify(imgIRequest* aRequest, int32_t aType, const nsIntRect* aData)
 {
   nsresult rv = nsImageLoadingContent::Notify(aRequest, aType, aData);
 
-  if (aType == imgINotificationObserver::START_CONTAINER) {
+  if (aType == imgINotificationObserver::SIZE_AVAILABLE) {
     // 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_REQUEST ||
-      aType == imgINotificationObserver::FRAME_CHANGED ||
-      aType == imgINotificationObserver::START_CONTAINER) {
+  if (aType == imgINotificationObserver::LOAD_COMPLETE ||
+      aType == imgINotificationObserver::FRAME_UPDATE ||
+      aType == imgINotificationObserver::SIZE_AVAILABLE) {
     Invalidate();
   }
 
   return rv;
 }
 
 //----------------------------------------------------------------------
 // helper methods
--- 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(dad8093d-384b-4923-b83c-351c4e5f9dce)]
+[scriptable, uuid(0089cf0c-210c-4b44-9ab0-8099b32bcf64)]
 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)
@@ -72,23 +72,16 @@ interface imgIDecoderObserver : imgICont
    * called, the size has been set on the container and STATUS_SIZE_AVAILABLE
    * has been set on the associated imgRequest.
    */
   void onStartContainer();
 
   /**
    * Decode notification.
    *
-   * called when each frame is created.
-   */
-  void onStartFrame();
-
-  /**
-   * Decode notification.
-   *
    * called when there is more to paint.
    */
   [noscript] void onDataAvailable([const] in nsIntRect aRect);
 
   /**
    * Decode notification.
    *
    * called when a frame is finished decoding.
--- a/image/public/imgINotificationObserver.idl
+++ b/image/public/imgINotificationObserver.idl
@@ -9,25 +9,21 @@
 interface imgIRequest;
 
 %{C++
 #include "nsRect.h"
 %}
 
 [ptr] native nsIntRect(nsIntRect);
 
-[scriptable, builtinclass, uuid(9c606ef4-a8e9-45c0-9721-9d6171227ca5)]
+[scriptable, builtinclass, uuid(90b3d21c-317d-4d96-93c0-12add64a26bf)]
 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_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;
+  const long SIZE_AVAILABLE = 1;
+  const long FRAME_UPDATE = 2;
+  const long FRAME_COMPLETE = 3;
+  const long LOAD_COMPLETE = 4;
+  const long DECODE_COMPLETE = 5;
+  const long DISCARD = 6;
+  const long IS_ANIMATED = 7;
 
   [noscript] void notify(in imgIRequest aProxy, in long aType, [const] in nsIntRect aRect);
 };
--- a/image/public/imgIScriptedNotificationObserver.idl
+++ b/image/public/imgIScriptedNotificationObserver.idl
@@ -3,23 +3,19 @@
  * 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(c5a58b5f-cd1f-4c47-a5ed-5013e0b53e95)]
+[scriptable, uuid(10be55b3-2029-41a7-a975-538efed250ed)]
 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 stopDecode(in imgIRequest aRequest);
-  void stopRequest(in imgIRequest aRequest);
+  void sizeAvailable(in imgIRequest aRequest);
+  void frameUpdate(in imgIRequest aRequest);
+  void frameComplete(in imgIRequest aRequest);
+  void loadComplete(in imgIRequest aRequest);
+  void decodeComplete(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
@@ -215,20 +215,16 @@ Decoder::PostFrameStart()
   mFrameCount++;
   mInFrame = true;
 
   // Decoder implementations should only call this method if they successfully
   // appended the frame to the image. So mFrameCount should always match that
   // reported by the Image.
   NS_ABORT_IF_FALSE(mFrameCount == mImage.GetNumFrames(),
                     "Decoder frame count doesn't match image's!");
-
-  // Fire notification
-  if (mObserver)
-    mObserver->OnStartFrame();
 }
 
 void
 Decoder::PostFrameStop()
 {
   // We should be mid-frame
   NS_ABORT_IF_FALSE(mInFrame, "Stopping frame when we didn't start one!");
 
--- a/image/src/ScriptedNotificationObserver.cpp
+++ b/image/src/ScriptedNotificationObserver.cpp
@@ -26,32 +26,24 @@ ScriptedNotificationObserver::ScriptedNo
 {
 }
 
 NS_IMETHODIMP
 ScriptedNotificationObserver::Notify(imgIRequest* aRequest,
                                      int32_t aType,
                                      const nsIntRect* /*aUnused*/)
 {
-  if (aType == imgINotificationObserver::START_REQUEST)
-    return mInner->StartRequest(aRequest);
-  if (aType == imgINotificationObserver::START_CONTAINER)
-    return mInner->StartContainer(aRequest);
-  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_DECODE)
-    return mInner->StopDecode(aRequest);
-  if (aType == imgINotificationObserver::STOP_REQUEST)
-    return mInner->StopRequest(aRequest);
+  if (aType == imgINotificationObserver::SIZE_AVAILABLE)
+    return mInner->SizeAvailable(aRequest);
+  if (aType == imgINotificationObserver::FRAME_UPDATE)
+    return mInner->FrameUpdate(aRequest);
+  if (aType == imgINotificationObserver::FRAME_COMPLETE)
+    return mInner->FrameComplete(aRequest);
+  if (aType == imgINotificationObserver::DECODE_COMPLETE)
+    return mInner->DecodeComplete(aRequest);
+  if (aType == imgINotificationObserver::LOAD_COMPLETE)
+    return mInner->LoadComplete(aRequest);
   if (aType == imgINotificationObserver::DISCARD)
     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/imgRequestProxy.cpp
+++ b/image/src/imgRequestProxy.cpp
@@ -607,93 +607,60 @@ NS_IMETHODIMP imgRequestProxy::GetHasTra
     *hasData = mOwner->HasTransferredData();
   } else {
     // The safe thing to do is to claim we have data
     *hasData = true;
   }
   return NS_OK;
 }
 
-void imgRequestProxy::FrameChanged(const nsIntRect *dirtyRect)
-{
-  LOG_FUNC(gImgLog, "imgRequestProxy::FrameChanged");
-
-  if (mListener && !mCanceled) {
-    // Hold a ref to the listener while we call it, just in case.
-    nsCOMPtr<imgINotificationObserver> kungFuDeathGrip(mListener);
-    mListener->Notify(this, imgINotificationObserver::FRAME_CHANGED, dirtyRect);
-  }
-}
-
 /** imgIDecoderObserver methods **/
 
-void imgRequestProxy::OnStartDecode()
-{
-  LOG_FUNC(gImgLog, "imgRequestProxy::OnStartDecode");
-
-  if (mListener && !mCanceled) {
-    // Hold a ref to the listener while we call it, just in case.
-    nsCOMPtr<imgINotificationObserver> kungFuDeathGrip(mListener);
-    mListener->Notify(this, imgINotificationObserver::START_DECODE, nullptr);
-  }
-}
-
 void imgRequestProxy::OnStartContainer()
 {
   LOG_FUNC(gImgLog, "imgRequestProxy::OnStartContainer");
 
   if (mListener && !mCanceled && !mSentStartContainer) {
     // Hold a ref to the listener while we call it, just in case.
     nsCOMPtr<imgINotificationObserver> kungFuDeathGrip(mListener);
-    mListener->Notify(this, imgINotificationObserver::START_CONTAINER, nullptr);
+    mListener->Notify(this, imgINotificationObserver::SIZE_AVAILABLE, nullptr);
     mSentStartContainer = true;
   }
 }
 
-void imgRequestProxy::OnStartFrame()
-{
-  LOG_FUNC(gImgLog, "imgRequestProxy::OnStartFrame");
-
-  if (mListener && !mCanceled) {
-    // Hold a ref to the listener while we call it, just in case.
-    nsCOMPtr<imgINotificationObserver> kungFuDeathGrip(mListener);
-    mListener->Notify(this, imgINotificationObserver::START_FRAME, nullptr);
-  }
-}
-
-void imgRequestProxy::OnDataAvailable(const nsIntRect * rect)
+void imgRequestProxy::OnFrameUpdate(const nsIntRect * rect)
 {
   LOG_FUNC(gImgLog, "imgRequestProxy::OnDataAvailable");
 
   if (mListener && !mCanceled) {
     // Hold a ref to the listener while we call it, just in case.
     nsCOMPtr<imgINotificationObserver> kungFuDeathGrip(mListener);
-    mListener->Notify(this, imgINotificationObserver::DATA_AVAILABLE, rect);
+    mListener->Notify(this, imgINotificationObserver::FRAME_UPDATE, rect);
   }
 }
 
 void imgRequestProxy::OnStopFrame()
 {
   LOG_FUNC(gImgLog, "imgRequestProxy::OnStopFrame");
 
   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);
+    mListener->Notify(this, imgINotificationObserver::FRAME_COMPLETE, nullptr);
   }
 }
 
 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);
+    mListener->Notify(this, imgINotificationObserver::DECODE_COMPLETE, nullptr);
   }
 
   // Multipart needs reset for next OnStartContainer
   if (mOwner && mOwner->GetMultipart())
     mSentStartContainer = false;
 }
 
 void imgRequestProxy::OnDiscard()
@@ -719,24 +686,16 @@ void imgRequestProxy::OnImageIsAnimated(
 
 void imgRequestProxy::OnStartRequest()
 {
 #ifdef PR_LOGGING
   nsAutoCString name;
   GetName(name);
   LOG_FUNC_WITH_PARAM(gImgLog, "imgRequestProxy::OnStartRequest", "name", name.get());
 #endif
-
-  // Notify even if mCanceled, since OnStartRequest is guaranteed by the
-  // nsIStreamListener contract so it makes sense to do the same here.
-  if (mListener) {
-    // Hold a ref to the listener while we call it, just in case.
-    nsCOMPtr<imgINotificationObserver> kungFuDeathGrip(mListener);
-    mListener->Notify(this, imgINotificationObserver::START_REQUEST, nullptr);
-  }
 }
 
 void imgRequestProxy::OnStopRequest(bool lastPart)
 {
 #ifdef PR_LOGGING
   nsAutoCString name;
   GetName(name);
   LOG_FUNC_WITH_PARAM(gImgLog, "imgRequestProxy::OnStopRequest", "name", name.get());
@@ -744,17 +703,17 @@ void imgRequestProxy::OnStopRequest(bool
   // There's all sorts of stuff here that could kill us (the OnStopRequest call
   // on the listener, the removal from the loadgroup, the release of the
   // listener, etc).  Don't let them do it.
   nsCOMPtr<imgIRequest> kungFuDeathGrip(this);
 
   if (mListener) {
     // Hold a ref to the listener while we call it, just in case.
     nsCOMPtr<imgINotificationObserver> kungFuDeathGrip(mListener);
-    mListener->Notify(this, imgINotificationObserver::STOP_REQUEST, nullptr);
+    mListener->Notify(this, imgINotificationObserver::LOAD_COMPLETE, nullptr);
   }
 
   // If we're expecting more data from a multipart channel, re-add ourself
   // to the loadgroup so that the document doesn't lose track of the load.
   // If the request is already a background request and there's more data
   // coming, we can just leave the request in the loadgroup as-is.
   if (lastPart || (mLoadFlags & nsIRequest::LOAD_BACKGROUND) == 0) {
     RemoveFromLoadGroup(lastPart);
--- a/image/src/imgRequestProxy.h
+++ b/image/src/imgRequestProxy.h
@@ -127,28 +127,23 @@ protected:
       nsresult mStatus;
   };
 
   // The following notification functions are protected to ensure that (friend
   // class) imgStatusTracker is the only class allowed to send us
   // notifications.
 
   /* non-virtual imgIDecoderObserver methods */
-  void OnStartDecode     ();
   void OnStartContainer  ();
-  void OnStartFrame      ();
-  void OnDataAvailable   (const nsIntRect * aRect);
+  void OnFrameUpdate     (const nsIntRect * aRect);
   void OnStopFrame       ();
   void OnStopDecode      ();
   void OnDiscard         ();
   void OnImageIsAnimated ();
 
-  /* non-virtual imgIContainerObserver methods */
-  void FrameChanged(const nsIntRect *aDirtyRect);
-
   /* non-virtual sort-of-nsIRequestObserver methods */
   void OnStartRequest();
   void OnStopRequest(bool aLastPart);
 
   /* non-virtual imgIOnloadBlocker methods */
   void BlockOnload();
   void UnblockOnload();
 
--- a/image/src/imgStatusTracker.cpp
+++ b/image/src/imgStatusTracker.cpp
@@ -41,30 +41,22 @@ NS_IMETHODIMP imgStatusTrackerObserver::
     mTracker->SendFrameChanged(iter.GetNext(), dirtyRect);
   }
 
   return NS_OK;
 }
 
 /** imgIDecoderObserver methods **/
 
-/* void onStartDecode (); */
 NS_IMETHODIMP imgStatusTrackerObserver::OnStartDecode()
 {
   LOG_SCOPE(gImgLog, "imgStatusTrackerObserver::OnStartDecode");
   NS_ABORT_IF_FALSE(mTracker->GetImage(),
                     "OnStartDecode callback before we've created our image");
 
-  mTracker->RecordStartDecode();
-
-  nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mTracker->mConsumers);
-  while (iter.HasMore()) {
-    mTracker->SendStartDecode(iter.GetNext());
-  }
-
   if (!mTracker->GetRequest()->GetMultipart()) {
     MOZ_ASSERT(!mTracker->mBlockingOnload);
     mTracker->mBlockingOnload = true;
 
     mTracker->RecordBlockOnload();
 
     nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mTracker->mConsumers);
     while (iter.HasMore()) {
@@ -100,33 +92,16 @@ NS_IMETHODIMP imgStatusTrackerObserver::
   nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mTracker->mConsumers);
   while (iter.HasMore()) {
     mTracker->SendStartContainer(iter.GetNext());
   }
 
   return NS_OK;
 }
 
-/* void onStartFrame (); */
-NS_IMETHODIMP imgStatusTrackerObserver::OnStartFrame()
-{
-  LOG_SCOPE(gImgLog, "imgStatusTrackerObserver::OnStartFrame");
-  NS_ABORT_IF_FALSE(mTracker->GetImage(),
-                    "OnStartFrame callback before we've created our image");
-
-  mTracker->RecordStartFrame();
-
-  nsTObserverArray<imgRequestProxy*>::ForwardIterator iter(mTracker->mConsumers);
-  while (iter.HasMore()) {
-    mTracker->SendStartFrame(iter.GetNext());
-  }
-
-  return NS_OK;
-}
-
 /* [noscript] void onDataAvailable ([const] in nsIntRect rect); */
 NS_IMETHODIMP imgStatusTrackerObserver::OnDataAvailable(const nsIntRect * rect)
 {
   LOG_SCOPE(gImgLog, "imgStatusTrackerObserver::OnDataAvailable");
   NS_ABORT_IF_FALSE(mTracker->GetImage(),
                     "OnDataAvailable callback before we've created our image");
 
   mTracker->RecordDataAvailable();
@@ -415,38 +390,32 @@ imgStatusTracker::SyncNotify(imgRequestP
   // OnStartRequest
   if (mState & stateRequestStarted)
     proxy->OnStartRequest();
 
   // OnStartContainer
   if (mState & stateHasSize)
     proxy->OnStartContainer();
 
-  // OnStartDecode
-  if (mState & stateDecodeStarted)
-    proxy->OnStartDecode();
-
   // BlockOnload
   if (mState & stateBlockingOnload)
     proxy->BlockOnload();
 
   if (mImage) {
     int16_t imageType = mImage->GetType();
-    // Send frame messages (OnStartFrame, OnDataAvailable, OnStopFrame)
+    // Send frame messages (OnDataAvailable, OnStopFrame)
     if (imageType == imgIContainer::TYPE_VECTOR ||
         static_cast<RasterImage*>(mImage)->GetNumFrames() > 0) {
 
-      proxy->OnStartFrame();
-
       // OnDataAvailable
       // XXX - Should only send partial rects here, but that needs to
       // wait until we fix up the observer interface
       nsIntRect r;
       mImage->GetCurrentFrameRect(r);
-      proxy->OnDataAvailable(&r);
+      proxy->OnFrameUpdate(&r);
 
       if (mState & stateFrameStopped)
         proxy->OnStopFrame();
     }
 
     // OnImageIsAnimated
     bool isAnimated = false;
 
@@ -524,35 +493,20 @@ imgStatusTracker::RecordLoaded()
   mImageStatus |= imgIRequest::STATUS_SIZE_AVAILABLE | imgIRequest::STATUS_LOAD_COMPLETE;
   mHadLastPart = true;
 }
 
 void
 imgStatusTracker::RecordDecoded()
 {
   NS_ABORT_IF_FALSE(mImage, "RecordDecoded called before we have an Image");
-  mState |= stateDecodeStarted | stateDecodeStopped | stateFrameStopped;
+  mState |= stateDecodeStopped | stateFrameStopped;
   mImageStatus |= imgIRequest::STATUS_FRAME_COMPLETE | imgIRequest::STATUS_DECODE_COMPLETE;
 }
 
-/* non-virtual imgIDecoderObserver methods */
-void
-imgStatusTracker::RecordStartDecode()
-{
-  NS_ABORT_IF_FALSE(mImage, "RecordStartDecode without an Image");
-  mState |= stateDecodeStarted;
-}
-
-void
-imgStatusTracker::SendStartDecode(imgRequestProxy* aProxy)
-{
-  if (!aProxy->NotificationsDeferred())
-    aProxy->OnStartDecode();
-}
-
 void
 imgStatusTracker::RecordStartContainer(imgIContainer* aContainer)
 {
   NS_ABORT_IF_FALSE(mImage,
                     "RecordStartContainer called before we have an Image");
   NS_ABORT_IF_FALSE(mImage == aContainer,
                     "RecordStartContainer called with wrong Image");
   mState |= stateHasSize;
@@ -562,45 +516,30 @@ imgStatusTracker::RecordStartContainer(i
 void
 imgStatusTracker::SendStartContainer(imgRequestProxy* aProxy)
 {
   if (!aProxy->NotificationsDeferred())
     aProxy->OnStartContainer();
 }
 
 void
-imgStatusTracker::RecordStartFrame()
-{
-  NS_ABORT_IF_FALSE(mImage, "RecordStartFrame called before we have an Image");
-  // no bookkeeping necessary here - this is implied by imgIContainer's number
-  // of frames
-}
-
-void
-imgStatusTracker::SendStartFrame(imgRequestProxy* aProxy)
-{
-  if (!aProxy->NotificationsDeferred())
-    aProxy->OnStartFrame();
-}
-
-void
 imgStatusTracker::RecordDataAvailable()
 {
   NS_ABORT_IF_FALSE(mImage,
                     "RecordDataAvailable called before we have an Image");
   // no bookkeeping necessary here - this is implied by imgIContainer's
   // number of frames and frame rect
 }
 
 void
 imgStatusTracker::SendDataAvailable(imgRequestProxy* aProxy,
                                     const nsIntRect* aRect)
 {
   if (!aProxy->NotificationsDeferred())
-    aProxy->OnDataAvailable(aRect);
+    aProxy->OnFrameUpdate(aRect);
 }
 
 
 void
 imgStatusTracker::RecordStopFrame()
 {
   NS_ABORT_IF_FALSE(mImage, "RecordStopFrame called before we have an Image");
   mState |= stateFrameStopped;
@@ -637,17 +576,17 @@ imgStatusTracker::SendStopDecode(imgRequ
 }
 
 void
 imgStatusTracker::RecordDiscard()
 {
   NS_ABORT_IF_FALSE(mImage,
                     "RecordDiscard called before we have an Image");
   // Clear the state bits we no longer deserve.
-  uint32_t stateBitsToClear = stateDecodeStarted | stateDecodeStopped;
+  uint32_t stateBitsToClear = stateDecodeStopped;
   mState &= ~stateBitsToClear;
 
   // Clear the status bits we no longer deserve.
   uint32_t statusBitsToClear = imgIRequest::STATUS_FRAME_COMPLETE
                                | imgIRequest::STATUS_DECODE_COMPLETE;
   mImageStatus &= ~statusBitsToClear;
 }
 
@@ -686,30 +625,29 @@ imgStatusTracker::RecordFrameChanged(con
   // don't fire while we're recording
 }
 
 void
 imgStatusTracker::SendFrameChanged(imgRequestProxy* aProxy,
                                    const nsIntRect* aDirtyRect)
 {
   if (!aProxy->NotificationsDeferred())
-    aProxy->FrameChanged(aDirtyRect);
+    aProxy->OnFrameUpdate(aDirtyRect);
 }
 
 /* non-virtual sort-of-nsIRequestObserver methods */
 void
 imgStatusTracker::RecordStartRequest()
 {
   // We're starting a new load, so clear any status and state bits indicating
   // load/decode
   mImageStatus &= ~imgIRequest::STATUS_LOAD_PARTIAL;
   mImageStatus &= ~imgIRequest::STATUS_LOAD_COMPLETE;
   mImageStatus &= ~imgIRequest::STATUS_FRAME_COMPLETE;
   mState &= ~stateRequestStarted;
-  mState &= ~stateDecodeStarted;
   mState &= ~stateDecodeStopped;
   mState &= ~stateRequestStopped;
   mState &= ~stateBlockingOnload;
 
   mState |= stateRequestStarted;
 }
 
 void
--- a/image/src/imgStatusTracker.h
+++ b/image/src/imgStatusTracker.h
@@ -27,17 +27,16 @@ class Image;
 #include "nsIRunnable.h"
 #include "nscore.h"
 #include "nsWeakReference.h"
 #include "imgIDecoderObserver.h"
 
 enum {
   stateRequestStarted    = PR_BIT(0),
   stateHasSize           = PR_BIT(1),
-  stateDecodeStarted     = PR_BIT(2),
   stateDecodeStopped     = PR_BIT(3),
   stateFrameStopped      = PR_BIT(4),
   stateRequestStopped    = PR_BIT(5),
   stateBlockingOnload    = PR_BIT(6)
 };
 
 class imgStatusTrackerObserver : public imgIDecoderObserver,
                                  public nsSupportsWeakReference
@@ -144,22 +143,18 @@ public:
   // StartContainer, StopRequest.
   void RecordLoaded();
 
   // Shorthand for recording all the decode notifications: StartDecode,
   // StartFrame, DataAvailable, StopFrame, StopDecode.
   void RecordDecoded();
 
   /* non-virtual imgIDecoderObserver methods */
-  void RecordStartDecode();
-  void SendStartDecode(imgRequestProxy* aProxy);
   void RecordStartContainer(imgIContainer* aContainer);
   void SendStartContainer(imgRequestProxy* aProxy);
-  void RecordStartFrame();
-  void SendStartFrame(imgRequestProxy* aProxy);
   void RecordDataAvailable();
   void SendDataAvailable(imgRequestProxy* aProxy, const nsIntRect* aRect);
   void RecordStopFrame();
   void SendStopFrame(imgRequestProxy* aProxy);
   void RecordStopDecode(nsresult statusg);
   void SendStopDecode(imgRequestProxy* aProxy, nsresult aStatus);
   void RecordDiscard();
   void SendDiscard(imgRequestProxy* aProxy);
--- a/image/test/mochitest/imgutils.js
+++ b/image/test/mochitest/imgutils.js
@@ -119,20 +119,16 @@ function getImagePref(pref)
   }
   else
     return null;
 }
 
 // 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.stopDecode = function stopDecode(aRequest)         {}
-  this.stopRequest = function stopRequest(aRequest)       {}
-  this.dataAvailable = function dataAvailable(aRequest)   {}
+  this.sizeAvailable = function sizeAvailable(aRequest)   {}
+  this.frameComplete = function frameComplete(aRequest)   {}
+  this.decodeComplete = function decodeComplete(aRequest) {}
+  this.loadComplete = function loadComplete(aRequest)     {}
+  this.frameUpdate = function frameUpdate(aRequest)       {}
   this.discard = function discard(aRequest)               {}
   this.isAnimated = function isAnimated(aRequest)         {}
-  this.frameChanged = function frameChanged(aRequest)     {}
 }
--- a/image/test/mochitest/test_animSVGImage.html
+++ b/image/test/mochitest/test_animSVGImage.html
@@ -86,17 +86,17 @@ function cleanUpAndFinish() {
   gIsTestFinished = true;
 }
 
 function main() {
   takeReferenceSnapshot();
 
   // Create, customize & attach decoder observer
   observer = new ImageDecoderObserverStub();
-  observer.stopFrame = myOnStopFrame;
+  observer.frameComplete = myOnStopFrame;
   gMyDecoderObserver =
     Cc["@mozilla.org/image/tools;1"].getService(Ci.imgITools)
       .createScriptedObserver(observer);
   let imgLoadingContent = gImg.QueryInterface(Ci.nsIImageLoadingContent);
   imgLoadingContent.addObserver(gMyDecoderObserver);
 
   // We want to test the cold loading behavior, so clear cache in case an
   // earlier test got our image in there already.
--- a/image/test/unit/async_load_tests.js
+++ b/image/test/unit/async_load_tests.js
@@ -48,23 +48,20 @@ function checkClone(other_listener, aReq
   var outer = Cc["@mozilla.org/image/tools;1"].getService(Ci.imgITools)
                 .createScriptedObserver(listener);
   var clone = aRequest.clone(outer);
 }
 
 // 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_DECODE, 0);
-  do_check_neq(listener.state & STOP_REQUEST, 0);
+  do_check_neq(listener.state & SIZE_AVAILABLE, 0);
+  do_check_neq(listener.state & FRAME_COMPLETE, 0);
+  do_check_neq(listener.state & DECODE_COMPLETE, 0);
+  do_check_neq(listener.state & LOAD_COMPLETE, 0);
   do_check_eq(listener.state, ALL_BITS);
 
   do_test_finished();
 }
 
 function secondLoadDone(oldlistener, aRequest)
 {
   do_test_pending();
@@ -110,20 +107,20 @@ function firstLoadDone(oldlistener, aReq
   do_test_finished();
 }
 
 // Return a closure that allows us to check the stream listener's status when the
 // image starts loading.
 function getChannelLoadImageStartCallback(streamlistener)
 {
   return function channelLoadStart(imglistener, aRequest) {
-    // We must not have received any status before we get this start callback.
+    // We must not have received all status before we get this start callback.
     // If we have, we've broken people's expectations by delaying events from a
     // channel we were given.
-    do_check_eq(streamlistener.requestStatus, 0);
+    do_check_eq(streamlistener.requestStatus & STOP_REQUEST, 0);
 
     checkClone(imglistener, aRequest);
   }
 }
 
 // Return a closure that allows us to check the stream listener's status when the
 // image finishes loading.
 function getChannelLoadImageStopCallback(streamlistener, next)
--- a/image/test/unit/image_load_helpers.js
+++ b/image/test/unit/image_load_helpers.js
@@ -1,83 +1,69 @@
 /*
  * Helper structures to track callbacks from image and channel loads.
  */
 
-// One bit per callback that imageListener below implements. Stored in
-// ImageListener.state.
-// START_REQUEST and STOP_REQUEST are also reused by ChannelListener, and
+// START_REQUEST and STOP_REQUEST are used 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_DECODE = 0x20;
-const STOP_REQUEST = 0x40;
-const ALL_BITS = START_REQUEST | START_DECODE | START_CONTAINER | START_FRAME |
-                 STOP_FRAME | STOP_DECODE | STOP_REQUEST;
+const STOP_REQUEST = 0x02;
+const DATA_AVAILABLE = 0x04;
+
+// One bit per callback that imageListener below implements. Stored in
+// ImageListener.state.
+const SIZE_AVAILABLE = 0x01;
+const FRAME_UPDATE = 0x02;
+const FRAME_COMPLETE = 0x04;
+const LOAD_COMPLETE = 0x08;
+const DECODE_COMPLETE = 0x10;
+const ALL_BITS = SIZE_AVAILABLE | FRAME_COMPLETE | DECODE_COMPLETE | LOAD_COMPLETE;
 
 // 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)
+  this.sizeAvailable = function onSizeAvailable(aRequest)
   {
     do_check_false(this.synchronous);
 
-    this.state |= START_REQUEST;
+    this.state |= SIZE_AVAILABLE;
 
     if (this.start_callback)
       this.start_callback(this, aRequest);
   }
-  this.startDecode = function onStartDecode(aRequest)
-  {
-    do_check_false(this.synchronous);
-
-    this.state |= START_DECODE;
-  }
-  this.startContainer = function onStartContainer(aRequest)
-  {
-    do_check_false(this.synchronous);
-
-    this.state |= START_CONTAINER;
-  }
-  this.startFrame = function onStartFrame(aRequest)
+  this.frameComplete = function onFrameComplete(aRequest)
   {
     do_check_false(this.synchronous);
 
-    this.state |= START_FRAME;
+    this.state |= FRAME_COMPLETE;
   }
-  this.stopFrame = function onStopFrame(aRequest)
+  this.decodeComplete = function onDecodeComplete(aRequest)
   {
     do_check_false(this.synchronous);
 
-    this.state |= STOP_FRAME;
+    this.state |= DECODE_COMPLETE;
   }
-  this.stopDecode = function onStopDecode(aRequest)
-  {
-    do_check_false(this.synchronous);
-
-    this.state |= STOP_DECODE;
-  }
-  this.stopRequest = function onStopRequest(aRequest)
+  this.loadComplete = function onLoadcomplete(aRequest)
   {
     do_check_false(this.synchronous);
 
     // We have to cancel the request when we're done with it to break any
     // reference loops!
     aRequest.cancelAndForgetObserver(0);
 
-    this.state |= STOP_REQUEST;
+    this.state |= LOAD_COMPLETE;
 
     if (this.stop_callback)
       this.stop_callback(this, aRequest);
   }
-  this.dataAvailable = function onDataAvailable(aRequest)
+  this.frameUpdate = function onFrameUpdate(aRequest)
+  {
+  }
+  this.isAnimated = function onIsAnimated()
   {
   }
 
   // Initialize the synchronous flag to true to start. This must be set to
   // false before exiting to the event loop!
   this.synchronous = true;
 
   // A function to call when onStartRequest is called.
@@ -106,16 +92,18 @@ function ChannelListener()
 
     this.requestStatus |= START_REQUEST;
   }
 
   this.onDataAvailable = function onDataAvailable(aRequest, aContext, aInputStream, aOffset, aCount)
   {
     if (this.outputListener)
       this.outputListener.onDataAvailable(aRequest, aContext, aInputStream, aOffset, aCount);
+
+    this.requestStatus |= DATA_AVAILABLE;
   }
 
   this.onStopRequest = function onStopRequest(aRequest, aContext, aStatusCode)
   {
     if (this.outputListener)
       this.outputListener.onStopRequest(aRequest, aContext, aStatusCode);
 
     // If we failed (or were canceled - failure is implied if canceled),
--- a/layout/generic/nsBulletFrame.cpp
+++ b/layout/generic/nsBulletFrame.cpp
@@ -1429,24 +1429,23 @@ nsBulletFrame::GetPrefWidth(nsRenderingC
   DISPLAY_PREF_WIDTH(this, metrics.width);
   GetDesiredSize(PresContext(), aRenderingContext, metrics, 1.0f);
   return metrics.width;
 }
 
 NS_IMETHODIMP
 nsBulletFrame::Notify(imgIRequest *aRequest, int32_t aType, const nsIntRect* aData)
 {
-  if (aType == imgINotificationObserver::START_CONTAINER) {
+  if (aType == imgINotificationObserver::SIZE_AVAILABLE) {
     nsCOMPtr<imgIContainer> image;
     aRequest->GetImage(getter_AddRefs(image));
     return OnStartContainer(aRequest, image);
   }
 
-  if (aType == imgINotificationObserver::DATA_AVAILABLE ||
-      aType == imgINotificationObserver::FRAME_CHANGED) {
+  if (aType == imgINotificationObserver::FRAME_UPDATE) {
     // The image has changed.
     // Invalidate the entire content area. Maybe it's not optimal but it's simple and
     // always correct, and I'll be a stunned mullet if it ever matters for performance
     InvalidateFrame();
   }
 
   if (aType == imgINotificationObserver::IS_ANIMATED) {
     // Register the image request with the refresh driver now that we know it's
--- a/layout/generic/nsImageFrame.cpp
+++ b/layout/generic/nsImageFrame.cpp
@@ -135,17 +135,18 @@ NS_NewImageFrame(nsIPresShell* aPresShel
 
 NS_IMPL_FRAMEARENA_HELPERS(nsImageFrame)
 
 
 nsImageFrame::nsImageFrame(nsStyleContext* aContext) :
   ImageFrameSuper(aContext),
   mComputedSize(0, 0),
   mIntrinsicRatio(0, 0),
-  mDisplayingIcon(false)
+  mDisplayingIcon(false),
+  mFirstFrameComplete(false)
 {
   // We assume our size is not constrained and we haven't gotten an
   // initial reflow yet, so don't touch those flags.
   mIntrinsicSize.width.SetCoordValue(0);
   mIntrinsicSize.height.SetCoordValue(0);
 }
 
 nsImageFrame::~nsImageFrame()
@@ -523,40 +524,38 @@ nsImageFrame::ShouldCreateImageFrameFor(
   }
   
   return useSizedBox;
 }
 
 nsresult
 nsImageFrame::Notify(imgIRequest* aRequest, int32_t aType, const nsIntRect* aData)
 {
-  if (aType == imgINotificationObserver::START_CONTAINER) {
+  if (aType == imgINotificationObserver::SIZE_AVAILABLE) {
     nsCOMPtr<imgIContainer> image;
     aRequest->GetImage(getter_AddRefs(image));
     return OnStartContainer(aRequest, image);
   }
 
-  if (aType == imgINotificationObserver::DATA_AVAILABLE) {
+  if (aType == imgINotificationObserver::FRAME_UPDATE) {
     return OnDataAvailable(aRequest, aData);
   }
 
-  if (aType == imgINotificationObserver::STOP_REQUEST) {
+  if (aType == imgINotificationObserver::FRAME_COMPLETE) {
+    mFirstFrameComplete = true;
+  }
+
+  if (aType == imgINotificationObserver::LOAD_COMPLETE) {
     uint32_t imgStatus;
     aRequest->GetImageStatus(&imgStatus);
     nsresult status =
         imgStatus & imgIRequest::STATUS_ERROR ? NS_ERROR_FAILURE : NS_OK;
     return OnStopRequest(aRequest, status);
   }
 
-  if (aType == imgINotificationObserver::FRAME_CHANGED) {
-    nsCOMPtr<imgIContainer> image;
-    aRequest->GetImage(getter_AddRefs(image));
-    return FrameChanged(aRequest, image);
-  }
-
   return NS_OK;
 }
 
 nsresult
 nsImageFrame::OnStartContainer(imgIRequest *aRequest, imgIContainer *aImage)
 {
   if (!aImage) return NS_ERROR_INVALID_ARG;
 
@@ -591,16 +590,22 @@ nsImageFrame::OnStartContainer(imgIReque
 
   return NS_OK;
 }
 
 nsresult
 nsImageFrame::OnDataAvailable(imgIRequest *aRequest,
                               const nsIntRect *aRect)
 {
+  if (mFirstFrameComplete) {
+    nsCOMPtr<imgIContainer> container;
+    aRequest->GetImage(getter_AddRefs(container));
+    return FrameChanged(aRequest, container);
+  }
+
   // XXX do we need to make sure that the reflow from the
   // OnStartContainer has been processed before we start calling
   // invalidate?
 
   NS_ENSURE_ARG_POINTER(aRect);
 
   if (!(mState & IMAGE_GOTINITIALREFLOW)) {
     // Don't bother to do anything; we have a reflow coming up!
@@ -620,17 +625,17 @@ nsImageFrame::OnDataAvailable(imgIReques
   if (aRect->IsEqualInterior(nsIntRect::GetMaxSizedIntRect())) {
     InvalidateFrame(nsDisplayItem::TYPE_IMAGE);
     InvalidateFrame(nsDisplayItem::TYPE_ALT_FEEDBACK);
   } else {
     nsRect invalid = SourceRectToDest(*aRect);
     InvalidateFrameWithRect(invalid, nsDisplayItem::TYPE_IMAGE);
     InvalidateFrameWithRect(invalid, nsDisplayItem::TYPE_ALT_FEEDBACK);
   }
-  
+
   return NS_OK;
 }
 
 nsresult
 nsImageFrame::OnStopRequest(imgIRequest *aRequest,
                             nsresult aStatus)
 {
   // Check what request type we're dealing with
@@ -1971,18 +1976,18 @@ void nsImageFrame::IconLoad::GetPrefs()
 
   mPrefShowPlaceholders =
     Preferences::GetBool("browser.display.show_image_placeholders", true);
 }
 
 NS_IMETHODIMP
 nsImageFrame::IconLoad::Notify(imgIRequest *aRequest, int32_t aType, const nsIntRect* aData)
 {
-  if (aType != imgINotificationObserver::STOP_REQUEST &&
-      aType != imgINotificationObserver::FRAME_CHANGED) {
+  if (aType != imgINotificationObserver::LOAD_COMPLETE &&
+      aType != imgINotificationObserver::FRAME_UPDATE) {
     return NS_OK;
   }
 
   nsTObserverArray<nsImageFrame*>::ForwardIterator iter(mIconObservers);
   nsImageFrame *frame;
   while (iter.HasMore()) {
     frame = iter.GetNext();
     frame->InvalidateFrame();
--- a/layout/generic/nsImageFrame.h
+++ b/layout/generic/nsImageFrame.h
@@ -279,16 +279,17 @@ private:
 
   nsCOMPtr<imgINotificationObserver> mListener;
 
   nsSize mComputedSize;
   nsIFrame::IntrinsicSize mIntrinsicSize;
   nsSize mIntrinsicRatio;
 
   bool mDisplayingIcon;
+  bool mFirstFrameComplete;
 
   static nsIIOService* sIOService;
   
   /* loading / broken image icon support */
 
   // XXXbz this should be handled by the prescontext, I think; that
   // way we would have a single iconload per mozilla session instead
   // of one per document...
--- a/layout/style/ImageLoader.cpp
+++ b/layout/style/ImageLoader.cpp
@@ -336,31 +336,31 @@ NS_IMPL_RELEASE(ImageLoader)
 NS_INTERFACE_MAP_BEGIN(ImageLoader)
   NS_INTERFACE_MAP_ENTRY(imgINotificationObserver)
   NS_INTERFACE_MAP_ENTRY(imgIOnloadBlocker)
 NS_INTERFACE_MAP_END
 
 NS_IMETHODIMP
 ImageLoader::Notify(imgIRequest *aRequest, int32_t aType, const nsIntRect* aData)
 {
-  if (aType == imgINotificationObserver::START_CONTAINER) {
+  if (aType == imgINotificationObserver::SIZE_AVAILABLE) {
     nsCOMPtr<imgIContainer> image;
     aRequest->GetImage(getter_AddRefs(image));
     return OnStartContainer(aRequest, image);
   }
 
   if (aType == imgINotificationObserver::IS_ANIMATED) {
     return OnImageIsAnimated(aRequest);
   }
 
-  if (aType == imgINotificationObserver::STOP_FRAME) {
+  if (aType == imgINotificationObserver::LOAD_COMPLETE) {
     return OnStopFrame(aRequest);
   }
 
-  if (aType == imgINotificationObserver::FRAME_CHANGED) {
+  if (aType == imgINotificationObserver::FRAME_UPDATE) {
     return FrameChanged(aRequest);
   }
 
   return NS_OK;
 }
 
 nsresult
 ImageLoader::OnStartContainer(imgIRequest* aRequest, imgIContainer* aImage)
--- a/layout/svg/nsSVGImageFrame.cpp
+++ b/layout/svg/nsSVGImageFrame.cpp
@@ -558,28 +558,28 @@ 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_REQUEST) {
+  if (aType == imgINotificationObserver::LOAD_COMPLETE) {
     nsSVGUtils::InvalidateAndScheduleReflowSVG(mFrame);
   }
 
-  if (aType == imgINotificationObserver::FRAME_CHANGED) {
+  if (aType == imgINotificationObserver::FRAME_UPDATE) {
     // No new dimensions, so we don't need to call
     // nsSVGUtils::InvalidateAndScheduleBoundsUpdate.
     nsSVGEffects::InvalidateRenderingObservers(mFrame);
     nsSVGUtils::InvalidateBounds(mFrame);
   }
 
-  if (aType == imgINotificationObserver::START_CONTAINER) {
+  if (aType == imgINotificationObserver::SIZE_AVAILABLE) {
     // Called once the resource's dimensions have been obtained.
     aRequest->GetImage(getter_AddRefs(mFrame->mImageContainer));
     nsSVGUtils::InvalidateAndScheduleReflowSVG(mFrame);
   }
 
   return NS_OK;
 }
 
--- a/layout/xul/base/src/nsImageBoxFrame.cpp
+++ b/layout/xul/base/src/nsImageBoxFrame.cpp
@@ -575,39 +575,39 @@ nsImageBoxFrame::GetFrameName(nsAString&
 {
   return MakeFrameName(NS_LITERAL_STRING("ImageBox"), aResult);
 }
 #endif
 
 nsresult
 nsImageBoxFrame::Notify(imgIRequest *aRequest, int32_t aType, const nsIntRect* aData)
 {
-  if (aType == imgINotificationObserver::START_CONTAINER) {
+  if (aType == imgINotificationObserver::SIZE_AVAILABLE) {
     nsCOMPtr<imgIContainer> image;
     aRequest->GetImage(getter_AddRefs(image));
     return OnStartContainer(aRequest, image);
   }
 
-  if (aType == imgINotificationObserver::STOP_DECODE) {
+  if (aType == imgINotificationObserver::DECODE_COMPLETE) {
     return OnStopDecode(aRequest);
   }
 
-  if (aType == imgINotificationObserver::STOP_REQUEST) {
+  if (aType == imgINotificationObserver::LOAD_COMPLETE) {
     uint32_t imgStatus;
     aRequest->GetImageStatus(&imgStatus);
     nsresult status =
         imgStatus & imgIRequest::STATUS_ERROR ? NS_ERROR_FAILURE : NS_OK;
     return OnStopRequest(aRequest, status);
   }
 
   if (aType == imgINotificationObserver::IS_ANIMATED) {
     return OnImageIsAnimated(aRequest);
   }
 
-  if (aType == imgINotificationObserver::FRAME_CHANGED) {
+  if (aType == imgINotificationObserver::FRAME_UPDATE) {
     return FrameChanged(aRequest);
   }
 
   return NS_OK;
 }
 
 nsresult nsImageBoxFrame::OnStartContainer(imgIRequest *request,
                                            imgIContainer *image)
--- a/layout/xul/base/src/tree/src/nsTreeImageListener.cpp
+++ b/layout/xul/base/src/tree/src/nsTreeImageListener.cpp
@@ -24,25 +24,24 @@ nsTreeImageListener::~nsTreeImageListene
 
 NS_IMETHODIMP
 nsTreeImageListener::Notify(imgIRequest *aRequest, int32_t aType, const nsIntRect* aData)
 {
   if (aType == imgINotificationObserver::IS_ANIMATED) {
     return mTreeFrame ? mTreeFrame->OnImageIsAnimated(aRequest) : NS_OK;
   }
 
-  if (aType == imgINotificationObserver::START_CONTAINER) {
+  if (aType == imgINotificationObserver::SIZE_AVAILABLE) {
     // Ensure the animation (if any) is started. Note: There is no
     // corresponding call to Decrement for this. This Increment will be
     // 'cleaned up' by the Request when it is destroyed, but only then.
     aRequest->IncrementAnimationConsumers();
   }
 
-  if (aType == imgINotificationObserver::DATA_AVAILABLE ||
-      aType == imgINotificationObserver::FRAME_CHANGED) {
+  if (aType == imgINotificationObserver::FRAME_UPDATE) {
     Invalidate();
   }
 
   return NS_OK;
 }
 
 void
 nsTreeImageListener::AddCell(int32_t aIndex, nsITreeColumn* aCol)
--- a/toolkit/system/gnome/nsAlertsIconListener.cpp
+++ b/toolkit/system/gnome/nsAlertsIconListener.cpp
@@ -87,21 +87,21 @@ nsAlertsIconListener::~nsAlertsIconListe
   if (mIconRequest)
     mIconRequest->CancelAndForgetObserver(NS_BINDING_ABORTED);
   // Don't dlclose libnotify as it uses atexit().
 }
 
 NS_IMETHODIMP
 nsAlertsIconListener::Notify(imgIRequest *aRequest, int32_t aType, const nsIntRect* aData)
 {
-  if (aType == imgINotificationObserver::STOP_REQUEST) {
+  if (aType == imgINotificationObserver::LOAD_COMPLETE) {
     return OnStopRequest(aRequest);
   }
 
-  if (aType == imgINotificationObserver::STOP_FRAME) {
+  if (aType == imgINotificationObserver::FRAME_COMPLETE) {
     return OnStopFrame(aRequest);
   }
 
   return NS_OK;
 }
 
 nsresult
 nsAlertsIconListener::OnStopRequest(imgIRequest* aRequest)
--- a/widget/cocoa/nsMenuItemIconX.mm
+++ b/widget/cocoa/nsMenuItemIconX.mm
@@ -319,21 +319,21 @@ nsMenuItemIconX::LoadIcon(nsIURI* aIconU
 
 //
 // imgINotificationObserver
 //
 
 NS_IMETHODIMP
 nsMenuItemIconX::Notify(imgIRequest *aRequest, int32_t aType, const nsIntRect* aData)
 {
-  if (aType == imgINotificationObserver::STOP_FRAME) {
+  if (aType == imgINotificationObserver::FRAME_COMPLETE) {
     return OnStopFrame(aRequest);
   }
 
-  if (aType == imgINotificationObserver::STOP_REQUEST) {
+  if (aType == imgINotificationObserver::LOAD_COMPLETE) {
     if (mIconRequest && mIconRequest == aRequest) {
       mIconRequest->Cancel(NS_BINDING_ABORTED);
       mIconRequest = nullptr;
     }
   }
 
   return NS_OK;
 }