Bug 1112956 - Add IProgressObserver to permit more than one class to observe ProgressTracker. r=tn
authorSeth Fowler <seth@mozilla.com>
Wed, 07 Jan 2015 01:35:20 -0800
changeset 222477 07c75e0d6f673932be4e2b17e0a8bb6bd8a37e1e
parent 222380 0776988aacd6201623ce747940fc7edac89e99d9
child 222478 a94ab57658c06c2e7f70ae352369d406baafbbdb
push id28067
push userkwierso@gmail.com
push dateWed, 07 Jan 2015 23:41:38 +0000
treeherdermozilla-central@70de2960aa87 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstn
bugs1112956
milestone37.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 1112956 - Add IProgressObserver to permit more than one class to observe ProgressTracker. r=tn
image/src/DecodePool.cpp
image/src/IProgressObserver.h
image/src/Image.h
image/src/ProgressTracker.cpp
image/src/ProgressTracker.h
image/src/imgRequest.cpp
image/src/imgRequestProxy.cpp
image/src/imgRequestProxy.h
image/src/moz.build
--- a/image/src/DecodePool.cpp
+++ b/image/src/DecodePool.cpp
@@ -7,16 +7,17 @@
 
 #include <algorithm>
 
 #include "mozilla/ClearOnShutdown.h"
 #include "nsAutoPtr.h"
 #include "nsCOMPtr.h"
 #include "nsIObserverService.h"
 #include "nsIThreadPool.h"
+#include "nsProxyRelease.h"
 #include "nsXPCOMCIDInternal.h"
 #include "prsystem.h"
 
 #ifdef MOZ_NUWA_PROCESS
 #include "ipc/Nuwa.h"
 #endif
 
 #include "gfxPrefs.h"
new file mode 100644
--- /dev/null
+++ b/image/src/IProgressObserver.h
@@ -0,0 +1,59 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+#ifndef MOZILLA_IMAGELIB_PROGRESSOBSERVER_H_
+#define MOZILLA_IMAGELIB_PROGRESSOBSERVER_H_
+
+#include "mozilla/WeakPtr.h"
+#include "nsISupports.h"
+#include "nsRect.h"
+
+namespace mozilla {
+namespace image {
+
+/**
+ * An interface for observing changes to image state, as reported by
+ * ProgressTracker.
+ * 
+ * This is the ImageLib-internal version of imgINotificationObserver,
+ * essentially, with implementation details that code outside of ImageLib
+ * shouldn't see.
+ *
+ * XXX(seth): It's preferable to avoid adding anything to this interface if
+ * possible.  In the long term, it would be ideal to get to a place where we can
+ * just use the imgINotificationObserver interface internally as well.
+ */
+class IProgressObserver : public SupportsWeakPtr<IProgressObserver>
+{
+public:
+  MOZ_DECLARE_REFCOUNTED_TYPENAME(IProgressObserver)
+
+  // Subclasses may or may not be XPCOM classes, so we just require that they
+  // implement AddRef and Release.
+  NS_IMETHOD_(MozExternalRefCountType) AddRef(void) = 0;
+  NS_IMETHOD_(MozExternalRefCountType) Release(void) = 0;
+
+  // imgINotificationObserver methods:
+  virtual void Notify(int32_t aType, const nsIntRect* aRect = nullptr) = 0;
+  virtual void OnLoadComplete(bool aLastPart) = 0;
+
+  // imgIOnloadBlocker methods:
+  virtual void BlockOnload() = 0;
+  virtual void UnblockOnload() = 0;
+
+  // Other, internal-only methods:
+  virtual void SetHasImage() = 0;
+  virtual void OnStartDecode() = 0;
+  virtual bool NotificationsDeferred() const = 0;
+  virtual void SetNotificationsDeferred(bool aDeferNotifications) = 0;
+
+protected:
+  virtual ~IProgressObserver() { }
+};
+
+} // namespace image
+} // namespace mozilla
+
+#endif // MOZILLA_IMAGELIB_PROGRESSOBSERVER_H_
--- a/image/src/Image.h
+++ b/image/src/Image.h
@@ -2,16 +2,17 @@
 /* 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/. */
 
 #ifndef MOZILLA_IMAGELIB_IMAGE_H_
 #define MOZILLA_IMAGELIB_IMAGE_H_
 
 #include "mozilla/MemoryReporting.h"
+#include "mozilla/TimeStamp.h"
 #include "gfx2DGlue.h"                // for gfxMemoryLocation
 #include "imgIContainer.h"
 #include "ProgressTracker.h"
 #include "ImageURL.h"
 #include "nsStringFwd.h"
 
 class nsIRequest;
 class nsIInputStream;
--- a/image/src/ProgressTracker.cpp
+++ b/image/src/ProgressTracker.cpp
@@ -3,17 +3,18 @@
  * 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 "ImageLogging.h"
 #include "ProgressTracker.h"
 
 #include "imgIContainer.h"
-#include "imgRequestProxy.h"
+#include "imgINotificationObserver.h"
+#include "imgIRequest.h"
 #include "Image.h"
 #include "nsNetUtil.h"
 #include "nsIObserverService.h"
 
 #include "mozilla/Assertions.h"
 #include "mozilla/Services.h"
 
 using mozilla::WeakPtr;
@@ -166,333 +167,340 @@ ProgressTracker::GetImageStatus() const
   return status;
 }
 
 // A helper class to allow us to call SyncNotify asynchronously.
 class AsyncNotifyRunnable : public nsRunnable
 {
   public:
     AsyncNotifyRunnable(ProgressTracker* aTracker,
-                        imgRequestProxy* aRequestProxy)
+                        IProgressObserver* aObserver)
       : mTracker(aTracker)
     {
       MOZ_ASSERT(NS_IsMainThread(), "Should be created on the main thread");
       MOZ_ASSERT(aTracker, "aTracker should not be null");
-      MOZ_ASSERT(aRequestProxy, "aRequestProxy should not be null");
-      mProxies.AppendElement(aRequestProxy);
+      MOZ_ASSERT(aObserver, "aObserver should not be null");
+      mObservers.AppendElement(aObserver);
     }
 
     NS_IMETHOD Run()
     {
       MOZ_ASSERT(NS_IsMainThread(), "Should be running on the main thread");
       MOZ_ASSERT(mTracker, "mTracker should not be null");
-      for (uint32_t i = 0; i < mProxies.Length(); ++i) {
-        mProxies[i]->SetNotificationsDeferred(false);
-        mTracker->SyncNotify(mProxies[i]);
+      for (uint32_t i = 0; i < mObservers.Length(); ++i) {
+        mObservers[i]->SetNotificationsDeferred(false);
+        mTracker->SyncNotify(mObservers[i]);
       }
 
       mTracker->mRunnable = nullptr;
       return NS_OK;
     }
 
-    void AddProxy(imgRequestProxy* aRequestProxy)
+    void AddObserver(IProgressObserver* aObserver)
     {
-      mProxies.AppendElement(aRequestProxy);
+      mObservers.AppendElement(aObserver);
     }
 
-    void RemoveProxy(imgRequestProxy* aRequestProxy)
+    void RemoveObserver(IProgressObserver* aObserver)
     {
-      mProxies.RemoveElement(aRequestProxy);
+      mObservers.RemoveElement(aObserver);
     }
 
   private:
     friend class ProgressTracker;
 
     nsRefPtr<ProgressTracker> mTracker;
-    nsTArray<nsRefPtr<imgRequestProxy>> mProxies;
+    nsTArray<nsRefPtr<IProgressObserver>> mObservers;
 };
 
 void
-ProgressTracker::Notify(imgRequestProxy* proxy)
+ProgressTracker::Notify(IProgressObserver* aObserver)
 {
-  MOZ_ASSERT(NS_IsMainThread(), "imgRequestProxy is not threadsafe");
+  MOZ_ASSERT(NS_IsMainThread());
+
 #ifdef PR_LOGGING
   if (mImage && mImage->GetURI()) {
     nsRefPtr<ImageURL> uri(mImage->GetURI());
     nsAutoCString spec;
     uri->GetSpec(spec);
     LOG_FUNC_WITH_PARAM(GetImgLog(),
                         "ProgressTracker::Notify async", "uri", spec.get());
   } else {
     LOG_FUNC_WITH_PARAM(GetImgLog(),
                         "ProgressTracker::Notify async", "uri", "<unknown>");
   }
 #endif
 
-  proxy->SetNotificationsDeferred(true);
+  aObserver->SetNotificationsDeferred(true);
 
-  // If we have an existing runnable that we can use, we just append this proxy
-  // to its list of proxies to be notified. This ensures we don't unnecessarily
-  // delay onload.
+  // If we have an existing runnable that we can use, we just append this
+  // observer to its list of observers to be notified. This ensures we don't
+  // unnecessarily delay onload.
   AsyncNotifyRunnable* runnable =
     static_cast<AsyncNotifyRunnable*>(mRunnable.get());
 
   if (runnable) {
-    runnable->AddProxy(proxy);
+    runnable->AddObserver(aObserver);
   } else {
-    mRunnable = new AsyncNotifyRunnable(this, proxy);
+    mRunnable = new AsyncNotifyRunnable(this, aObserver);
     NS_DispatchToCurrentThread(mRunnable);
   }
 }
 
 // A helper class to allow us to call SyncNotify asynchronously for a given,
 // fixed, state.
 class AsyncNotifyCurrentStateRunnable : public nsRunnable
 {
   public:
     AsyncNotifyCurrentStateRunnable(ProgressTracker* aProgressTracker,
-                                    imgRequestProxy* aProxy)
+                                    IProgressObserver* aObserver)
       : mProgressTracker(aProgressTracker)
-      , mProxy(aProxy)
+      , mObserver(aObserver)
     {
       MOZ_ASSERT(NS_IsMainThread(), "Should be created on the main thread");
       MOZ_ASSERT(mProgressTracker, "mProgressTracker should not be null");
-      MOZ_ASSERT(mProxy, "mProxy should not be null");
+      MOZ_ASSERT(mObserver, "mObserver should not be null");
       mImage = mProgressTracker->GetImage();
     }
 
     NS_IMETHOD Run()
     {
       MOZ_ASSERT(NS_IsMainThread(), "Should be running on the main thread");
-      mProxy->SetNotificationsDeferred(false);
+      mObserver->SetNotificationsDeferred(false);
 
-      mProgressTracker->SyncNotify(mProxy);
+      mProgressTracker->SyncNotify(mObserver);
       return NS_OK;
     }
 
   private:
     nsRefPtr<ProgressTracker> mProgressTracker;
-    nsRefPtr<imgRequestProxy> mProxy;
+    nsRefPtr<IProgressObserver> mObserver;
 
     // We have to hold on to a reference to the tracker's image, just in case
     // it goes away while we're in the event queue.
     nsRefPtr<Image> mImage;
 };
 
 void
-ProgressTracker::NotifyCurrentState(imgRequestProxy* proxy)
+ProgressTracker::NotifyCurrentState(IProgressObserver* aObserver)
 {
-  MOZ_ASSERT(NS_IsMainThread(), "imgRequestProxy is not threadsafe");
+  MOZ_ASSERT(NS_IsMainThread());
+
 #ifdef PR_LOGGING
-  nsRefPtr<ImageURL> uri;
-  proxy->GetURI(getter_AddRefs(uri));
   nsAutoCString spec;
-  uri->GetSpec(spec);
+  if (mImage && mImage->GetURI()) {
+    mImage->GetURI()->GetSpec(spec);
+  }
   LOG_FUNC_WITH_PARAM(GetImgLog(),
                       "ProgressTracker::NotifyCurrentState", "uri", spec.get());
 #endif
 
-  proxy->SetNotificationsDeferred(true);
+  aObserver->SetNotificationsDeferred(true);
 
-  nsCOMPtr<nsIRunnable> ev = new AsyncNotifyCurrentStateRunnable(this, proxy);
+  nsCOMPtr<nsIRunnable> ev = new AsyncNotifyCurrentStateRunnable(this, aObserver);
   NS_DispatchToCurrentThread(ev);
 }
 
-#define NOTIFY_IMAGE_OBSERVERS(PROXIES, FUNC) \
+#define NOTIFY_IMAGE_OBSERVERS(OBSERVERS, FUNC) \
   do { \
-    ProxyArray::ForwardIterator iter(PROXIES); \
+    ObserverArray::ForwardIterator iter(OBSERVERS); \
     while (iter.HasMore()) { \
-      nsRefPtr<imgRequestProxy> proxy = iter.GetNext().get(); \
-      if (proxy && !proxy->NotificationsDeferred()) { \
-        proxy->FUNC; \
+      nsRefPtr<IProgressObserver> observer = iter.GetNext().get(); \
+      if (observer && !observer->NotificationsDeferred()) { \
+        observer->FUNC; \
       } \
     } \
   } while (false);
 
 /* static */ void
-ProgressTracker::SyncNotifyInternal(ProxyArray& aProxies,
+ProgressTracker::SyncNotifyInternal(ObserverArray& aObservers,
                                     bool aHasImage,
                                     Progress aProgress,
                                     const nsIntRect& aDirtyRect)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
+  typedef imgINotificationObserver I;
+
   if (aProgress & FLAG_SIZE_AVAILABLE) {
-    NOTIFY_IMAGE_OBSERVERS(aProxies, OnSizeAvailable());
+    NOTIFY_IMAGE_OBSERVERS(aObservers, Notify(I::SIZE_AVAILABLE));
   }
 
   if (aProgress & FLAG_DECODE_STARTED) {
-    NOTIFY_IMAGE_OBSERVERS(aProxies, OnStartDecode());
+    NOTIFY_IMAGE_OBSERVERS(aObservers, OnStartDecode());
   }
 
   if (aProgress & FLAG_ONLOAD_BLOCKED) {
-    NOTIFY_IMAGE_OBSERVERS(aProxies, BlockOnload());
+    NOTIFY_IMAGE_OBSERVERS(aObservers, BlockOnload());
   }
 
   if (aHasImage) {
     // OnFrameUpdate
     // If there's any content in this frame at all (always true for
     // vector images, true for raster images that have decoded at
     // least one frame) then send OnFrameUpdate.
     if (!aDirtyRect.IsEmpty()) {
-      NOTIFY_IMAGE_OBSERVERS(aProxies, OnFrameUpdate(&aDirtyRect));
+      NOTIFY_IMAGE_OBSERVERS(aObservers, Notify(I::FRAME_UPDATE, &aDirtyRect));
     }
 
     if (aProgress & FLAG_FRAME_COMPLETE) {
-      NOTIFY_IMAGE_OBSERVERS(aProxies, OnFrameComplete());
+      NOTIFY_IMAGE_OBSERVERS(aObservers, Notify(I::FRAME_COMPLETE));
     }
 
     if (aProgress & FLAG_HAS_TRANSPARENCY) {
-      NOTIFY_IMAGE_OBSERVERS(aProxies, OnImageHasTransparency());
+      NOTIFY_IMAGE_OBSERVERS(aObservers, Notify(I::HAS_TRANSPARENCY));
     }
 
     if (aProgress & FLAG_IS_ANIMATED) {
-      NOTIFY_IMAGE_OBSERVERS(aProxies, OnImageIsAnimated());
+      NOTIFY_IMAGE_OBSERVERS(aObservers, Notify(I::IS_ANIMATED));
     }
   }
 
   // Send UnblockOnload before OnStopDecode and OnStopRequest. This allows
   // observers that can fire events when they receive those notifications to do
   // so then, instead of being forced to wait for UnblockOnload.
   if (aProgress & FLAG_ONLOAD_UNBLOCKED) {
-    NOTIFY_IMAGE_OBSERVERS(aProxies, UnblockOnload());
+    NOTIFY_IMAGE_OBSERVERS(aObservers, UnblockOnload());
   }
 
   if (aProgress & FLAG_DECODE_COMPLETE) {
     MOZ_ASSERT(aHasImage, "Stopped decoding without ever having an image?");
-    NOTIFY_IMAGE_OBSERVERS(aProxies, OnDecodeComplete());
+    NOTIFY_IMAGE_OBSERVERS(aObservers, Notify(I::DECODE_COMPLETE));
   }
 
   if (aProgress & FLAG_LOAD_COMPLETE) {
-    NOTIFY_IMAGE_OBSERVERS(aProxies,
+    NOTIFY_IMAGE_OBSERVERS(aObservers,
                            OnLoadComplete(aProgress & FLAG_LAST_PART_COMPLETE));
   }
 }
 
 void
 ProgressTracker::SyncNotifyProgress(Progress aProgress,
                                     const nsIntRect& aInvalidRect
                                                   /* = nsIntRect() */)
 {
-  MOZ_ASSERT(NS_IsMainThread(), "Use mConsumers on main thread only");
+  MOZ_ASSERT(NS_IsMainThread(), "Use mObservers on main thread only");
 
   // Don't unblock onload if we're not blocked.
   Progress progress = Difference(aProgress);
   if (!((mProgress | progress) & FLAG_ONLOAD_BLOCKED)) {
     progress &= ~FLAG_ONLOAD_UNBLOCKED;
   }
 
   // Apply the changes.
   mProgress |= progress;
 
   CheckProgressConsistency(mProgress);
 
   // Send notifications.
-  SyncNotifyInternal(mConsumers, !!mImage, progress, aInvalidRect);
+  SyncNotifyInternal(mObservers, !!mImage, progress, aInvalidRect);
 
   if (progress & FLAG_HAS_ERROR) {
     FireFailureNotification();
   }
 }
 
 void
-ProgressTracker::SyncNotify(imgRequestProxy* proxy)
+ProgressTracker::SyncNotify(IProgressObserver* aObserver)
 {
-  MOZ_ASSERT(NS_IsMainThread(), "imgRequestProxy is not threadsafe");
+  MOZ_ASSERT(NS_IsMainThread());
+
 #ifdef PR_LOGGING
-  nsRefPtr<ImageURL> uri;
-  proxy->GetURI(getter_AddRefs(uri));
   nsAutoCString spec;
-  uri->GetSpec(spec);
+  if (mImage && mImage->GetURI()) {
+    mImage->GetURI()->GetSpec(spec);
+  }
   LOG_SCOPE_WITH_PARAM(GetImgLog(),
                        "ProgressTracker::SyncNotify", "uri", spec.get());
 #endif
 
   nsIntRect r;
   if (mImage) {
     // XXX - Should only send partial rects here, but that needs to
     // wait until we fix up the observer interface
     r = mImage->FrameRect(imgIContainer::FRAME_CURRENT);
   }
 
-  ProxyArray array;
-  array.AppendElement(proxy);
+  ObserverArray array;
+  array.AppendElement(aObserver);
   SyncNotifyInternal(array, !!mImage, mProgress, r);
 }
 
 void
-ProgressTracker::EmulateRequestFinished(imgRequestProxy* aProxy,
+ProgressTracker::EmulateRequestFinished(IProgressObserver* aObserver,
                                         nsresult aStatus)
 {
   MOZ_ASSERT(NS_IsMainThread(),
-             "SyncNotifyState and mConsumers are not threadsafe");
-  nsCOMPtr<imgIRequest> kungFuDeathGrip(aProxy);
+             "SyncNotifyState and mObservers are not threadsafe");
+  nsRefPtr<IProgressObserver> kungFuDeathGrip(aObserver);
 
   if (mProgress & FLAG_ONLOAD_BLOCKED && !(mProgress & FLAG_ONLOAD_UNBLOCKED)) {
-    aProxy->UnblockOnload();
+    aObserver->UnblockOnload();
   }
 
   if (!(mProgress & FLAG_LOAD_COMPLETE)) {
-    aProxy->OnLoadComplete(true);
+    aObserver->OnLoadComplete(true);
   }
 }
 
 void
-ProgressTracker::AddConsumer(imgRequestProxy* aConsumer)
+ProgressTracker::AddObserver(IProgressObserver* aObserver)
 {
   MOZ_ASSERT(NS_IsMainThread());
-  mConsumers.AppendElementUnlessExists(aConsumer);
+  mObservers.AppendElementUnlessExists(aObserver);
 }
 
 // XXX - The last argument should go away.
 bool
-ProgressTracker::RemoveConsumer(imgRequestProxy* aConsumer, nsresult aStatus)
+ProgressTracker::RemoveObserver(IProgressObserver* aObserver, nsresult aStatus)
 {
   MOZ_ASSERT(NS_IsMainThread());
-  // Remove the proxy from the list.
-  bool removed = mConsumers.RemoveElement(aConsumer);
+
+  // Remove the observer from the list.
+  bool removed = mObservers.RemoveElement(aObserver);
 
-  // Consumers can get confused if they don't get all the proper teardown
+  // Observers can get confused if they don't get all the proper teardown
   // notifications. Part ways on good terms.
-  if (removed && !aConsumer->NotificationsDeferred()) {
-    EmulateRequestFinished(aConsumer, aStatus);
+  if (removed && !aObserver->NotificationsDeferred()) {
+    EmulateRequestFinished(aObserver, aStatus);
   }
 
-  // Make sure we don't give callbacks to a consumer that isn't interested in
+  // Make sure we don't give callbacks to an observer that isn't interested in
   // them any more.
   AsyncNotifyRunnable* runnable =
     static_cast<AsyncNotifyRunnable*>(mRunnable.get());
 
-  if (aConsumer->NotificationsDeferred() && runnable) {
-    runnable->RemoveProxy(aConsumer);
-    aConsumer->SetNotificationsDeferred(false);
+  if (aObserver->NotificationsDeferred() && runnable) {
+    runnable->RemoveObserver(aObserver);
+    aObserver->SetNotificationsDeferred(false);
   }
 
   return removed;
 }
 
 bool
-ProgressTracker::FirstConsumerIs(imgRequestProxy* aConsumer)
+ProgressTracker::FirstObserverIs(IProgressObserver* aObserver)
 {
-  MOZ_ASSERT(NS_IsMainThread(), "Use mConsumers on main thread only");
-  ProxyArray::ForwardIterator iter(mConsumers);
+  MOZ_ASSERT(NS_IsMainThread(), "Use mObservers on main thread only");
+  ObserverArray::ForwardIterator iter(mObservers);
   while (iter.HasMore()) {
-    nsRefPtr<imgRequestProxy> proxy = iter.GetNext().get();
-    if (proxy) {
-      return proxy.get() == aConsumer;
+    nsRefPtr<IProgressObserver> observer = iter.GetNext().get();
+    if (observer) {
+      return observer.get() == aObserver;
     }
   }
   return false;
 }
 
 void
 ProgressTracker::OnUnlockedDraw()
 {
   MOZ_ASSERT(NS_IsMainThread());
-  NOTIFY_IMAGE_OBSERVERS(mConsumers, OnUnlockedDraw());
+  NOTIFY_IMAGE_OBSERVERS(mObservers,
+                         Notify(imgINotificationObserver::UNLOCKED_DRAW));
 }
 
 void
 ProgressTracker::ResetForNewRequest()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   // We're starting a new load (and if this is called more than once, this is a
@@ -502,32 +510,33 @@ ProgressTracker::ResetForNewRequest()
 
   CheckProgressConsistency(mProgress);
 }
 
 void
 ProgressTracker::OnDiscard()
 {
   MOZ_ASSERT(NS_IsMainThread());
-  NOTIFY_IMAGE_OBSERVERS(mConsumers, OnDiscard());
+  NOTIFY_IMAGE_OBSERVERS(mObservers,
+                         Notify(imgINotificationObserver::DISCARD));
 }
 
 void
 ProgressTracker::OnImageAvailable()
 {
   if (!NS_IsMainThread()) {
     // Note: SetHasImage calls Image::Lock and Image::IncrementAnimationCounter
     // so subsequent calls or dispatches which Unlock or Decrement~ should
     // be issued after this to avoid race conditions.
     NS_DispatchToMainThread(
       NS_NewRunnableMethod(this, &ProgressTracker::OnImageAvailable));
     return;
   }
 
-  NOTIFY_IMAGE_OBSERVERS(mConsumers, SetHasImage());
+  NOTIFY_IMAGE_OBSERVERS(mObservers, SetHasImage());
 }
 
 void
 ProgressTracker::FireFailureNotification()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   // Some kind of problem has happened with image decoding.
--- a/image/src/ProgressTracker.h
+++ b/image/src/ProgressTracker.h
@@ -8,17 +8,17 @@
 #define ProgressTracker_h__
 
 #include "mozilla/RefPtr.h"
 #include "mozilla/WeakPtr.h"
 #include "nsCOMPtr.h"
 #include "nsTObserverArray.h"
 #include "nsThreadUtils.h"
 #include "nsRect.h"
-#include "imgRequestProxy.h"
+#include "IProgressObserver.h"
 
 class imgIContainer;
 class nsIRunnable;
 
 namespace mozilla {
 namespace image {
 
 class AsyncNotifyRunnable;
@@ -57,21 +57,22 @@ inline Progress LoadCompleteProgress(boo
     progress |= FLAG_HAS_ERROR;
   }
   return progress;
 }
 
 /**
  * ProgressTracker is a class that records an Image's progress through the
  * loading and decoding process, and makes it possible to send notifications to
- * imgRequestProxys, both synchronously and asynchronously.
+ * IProgressObservers, both synchronously and asynchronously.
  *
- * When a new proxy needs to be notified of the current progress of an image,
- * call the Notify() method on this class with the relevant proxy as its
- * argument, and the notifications will be replayed to the proxy asynchronously.
+ * When a new observer needs to be notified of the current progress of an image,
+ * call the Notify() method on this class with the relevant observer as its
+ * argument, and the notifications will be replayed to the observer
+ * asynchronously.
  */
 class ProgressTracker : public mozilla::SupportsWeakPtr<ProgressTracker>
 {
   virtual ~ProgressTracker() { }
 
 public:
   MOZ_DECLARE_REFCOUNTED_TYPENAME(ProgressTracker)
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ProgressTracker)
@@ -102,38 +103,38 @@ public:
   uint32_t GetImageStatus() const;
 
   // Get the current Progress.
   Progress GetProgress() const { return mProgress; }
 
   // Schedule an asynchronous "replaying" of all the notifications that would
   // have to happen to put us in the current state.
   // We will also take note of any notifications that happen between the time
-  // Notify() is called and when we call SyncNotify on |proxy|, and replay them
-  // as well.
-  // Should be called on the main thread only, since imgRequestProxy and GetURI
-  // are not threadsafe.
-  void Notify(imgRequestProxy* proxy);
+  // Notify() is called and when we call SyncNotify on |aObserver|, and replay
+  // them as well.
+  // Should be called on the main thread only, since observers and GetURI are
+  // not threadsafe.
+  void Notify(IProgressObserver* aObserver);
 
   // Schedule an asynchronous "replaying" of all the notifications that would
   // have to happen to put us in the state we are in right now.
   // Unlike Notify(), does *not* take into account future notifications.
   // This is only useful if you do not have an imgRequest, e.g., if you are a
   // static request returned from imgIRequest::GetStaticRequest().
-  // Should be called on the main thread only, since imgRequestProxy and GetURI
-  // are not threadsafe.
-  void NotifyCurrentState(imgRequestProxy* proxy);
+  // Should be called on the main thread only, since observers and GetURI are
+  // not threadsafe.
+  void NotifyCurrentState(IProgressObserver* aObserver);
 
   // "Replay" all of the notifications that would have to happen to put us in
   // the state we're currently in.
   // Only use this if you're already servicing an asynchronous call (e.g.
   // OnStartRequest).
-  // Should be called on the main thread only, since imgRequestProxy and GetURI
-  // are not threadsafe.
-  void SyncNotify(imgRequestProxy* proxy);
+  // Should be called on the main thread only, since observers and GetURI are
+  // not threadsafe.
+  void SyncNotify(IProgressObserver* aObserver);
 
   // Get this ProgressTracker ready for a new request. This resets all the
   // state that doesn't persist between requests.
   void ResetForNewRequest();
 
   // Stateless notifications. These are dispatched and immediately forgotten
   // about. All except OnImageAvailable are main thread only.
   void OnDiscard();
@@ -150,75 +151,75 @@ public:
   // Update our state to incorporate the changes in aProgress and synchronously
   // notify our observers.
   //
   // Because this may result in recursive notifications, no decoding locks may
   // be held.  Called on the main thread only.
   void SyncNotifyProgress(Progress aProgress,
                           const nsIntRect& aInvalidRect = nsIntRect());
 
-  // We manage a set of consumers that are using an image and thus concerned
+  // We manage a set of observers that are using an image and thus concerned
   // with its loading progress. Weak pointers.
-  void AddConsumer(imgRequestProxy* aConsumer);
-  bool RemoveConsumer(imgRequestProxy* aConsumer, nsresult aStatus);
-  size_t ConsumerCount() const {
-    MOZ_ASSERT(NS_IsMainThread(), "Use mConsumers on main thread only");
-    return mConsumers.Length();
+  void AddObserver(IProgressObserver* aObserver);
+  bool RemoveObserver(IProgressObserver* aObserver, nsresult aStatus);
+  size_t ObserverCount() const {
+    MOZ_ASSERT(NS_IsMainThread(), "Use mObservers on main thread only");
+    return mObservers.Length();
   }
 
   // This is intentionally non-general because its sole purpose is to support
   // some obscure network priority logic in imgRequest. That stuff could
   // probably be improved, but it's too scary to mess with at the moment.
-  bool FirstConsumerIs(imgRequestProxy* aConsumer);
+  bool FirstObserverIs(IProgressObserver* aObserver);
 
-  void AdoptConsumers(ProgressTracker* aTracker) {
-    MOZ_ASSERT(NS_IsMainThread(), "Use mConsumers on main thread only");
+  void AdoptObservers(ProgressTracker* aTracker) {
+    MOZ_ASSERT(NS_IsMainThread(), "Use mObservers on main thread only");
     MOZ_ASSERT(aTracker);
-    mConsumers = aTracker->mConsumers;
+    mObservers = aTracker->mObservers;
   }
 
 private:
-  typedef nsTObserverArray<mozilla::WeakPtr<imgRequestProxy>> ProxyArray;
+  typedef nsTObserverArray<mozilla::WeakPtr<IProgressObserver>> ObserverArray;
   friend class AsyncNotifyRunnable;
   friend class AsyncNotifyCurrentStateRunnable;
   friend class ProgressTrackerInit;
 
   ProgressTracker(const ProgressTracker& aOther) MOZ_DELETE;
 
   // This method should only be called once, and only on an ProgressTracker
   // that was initialized without an image. ProgressTrackerInit automates this.
   void SetImage(Image* aImage);
 
   // Resets our weak reference to our image, for when mImage is about to go out
   // of scope.  ProgressTrackerInit automates this.
   void ResetImage();
 
-  // Send some notifications that would be necessary to make |aProxy| believe
+  // Send some notifications that would be necessary to make |aObserver| believe
   // the request is finished downloading and decoding.  We only send
-  // FLAG_REQUEST_* and FLAG_ONLOAD_UNBLOCKED, and only if necessary.
-  void EmulateRequestFinished(imgRequestProxy* aProxy, nsresult aStatus);
+  // FLAG_LOAD_COMPLETE and FLAG_ONLOAD_UNBLOCKED, and only if necessary.
+  void EmulateRequestFinished(IProgressObserver* aObserver, nsresult aStatus);
 
   // Main thread only because it deals with the observer service.
   void FireFailureNotification();
 
-  // Main thread only, since imgRequestProxy calls are expected on the main
-  // thread, and mConsumers is not threadsafe.
-  static void SyncNotifyInternal(ProxyArray& aProxies,
+  // Main thread only, since notifications are expected on the main thread, and
+  // mObservers is not threadsafe.
+  static void SyncNotifyInternal(ObserverArray& aObservers,
                                  bool aHasImage, Progress aProgress,
                                  const nsIntRect& aInvalidRect);
 
   nsCOMPtr<nsIRunnable> mRunnable;
 
   // This weak ref should be set null when the image goes out of scope.
   Image* mImage;
 
-  // List of proxies attached to the image. Each proxy represents a consumer
-  // using the image. Array and/or individual elements should only be accessed
-  // on the main thread.
-  ProxyArray mConsumers;
+  // List of observers attached to the image. Each observer represents a
+  // consumer using the image. Array and/or individual elements should only be
+  // accessed on the main thread.
+  ObserverArray mObservers;
 
   Progress mProgress;
 };
 
 class ProgressTrackerInit
 {
 public:
   ProgressTrackerInit(Image* aImage, ProgressTracker* aTracker);
--- a/image/src/imgRequest.cpp
+++ b/image/src/imgRequest.cpp
@@ -175,44 +175,44 @@ void imgRequest::ResetCacheEntry()
 void imgRequest::AddProxy(imgRequestProxy *proxy)
 {
   NS_PRECONDITION(proxy, "null imgRequestProxy passed in");
   LOG_SCOPE_WITH_PARAM(GetImgLog(), "imgRequest::AddProxy", "proxy", proxy);
 
   // If we're empty before adding, we have to tell the loader we now have
   // proxies.
   nsRefPtr<ProgressTracker> progressTracker = GetProgressTracker();
-  if (progressTracker->ConsumerCount() == 0) {
+  if (progressTracker->ObserverCount() == 0) {
     NS_ABORT_IF_FALSE(mURI, "Trying to SetHasProxies without key uri.");
     if (mLoader) {
       mLoader->SetHasProxies(this);
     }
   }
 
-  progressTracker->AddConsumer(proxy);
+  progressTracker->AddObserver(proxy);
 }
 
 nsresult imgRequest::RemoveProxy(imgRequestProxy *proxy, nsresult aStatus)
 {
   LOG_SCOPE_WITH_PARAM(GetImgLog(), "imgRequest::RemoveProxy", "proxy", proxy);
 
   // This will remove our animation consumers, so after removing
   // this proxy, we don't end up without proxies with observers, but still
   // have animation consumers.
   proxy->ClearAnimationConsumers();
 
   // Let the status tracker do its thing before we potentially call Cancel()
   // below, because Cancel() may result in OnStopRequest being called back
   // before Cancel() returns, leaving the image in a different state then the
   // one it was in at this point.
   nsRefPtr<ProgressTracker> progressTracker = GetProgressTracker();
-  if (!progressTracker->RemoveConsumer(proxy, aStatus))
+  if (!progressTracker->RemoveObserver(proxy, aStatus))
     return NS_OK;
 
-  if (progressTracker->ConsumerCount() == 0) {
+  if (progressTracker->ObserverCount() == 0) {
     // If we have no observers, there's nothing holding us alive. If we haven't
     // been cancelled and thus removed from the cache, tell the image loader so
     // we can be evicted from the cache.
     if (mCacheEntry) {
       NS_ABORT_IF_FALSE(mURI, "Removing last observer without key uri.");
 
       if (mLoader) {
         mLoader->SetHasNoProxies(this, mCacheEntry);
@@ -412,17 +412,17 @@ void imgRequest::RemoveFromCache()
   }
 
   mCacheEntry = nullptr;
 }
 
 bool imgRequest::HasConsumers()
 {
   nsRefPtr<ProgressTracker> progressTracker = GetProgressTracker();
-  return progressTracker && progressTracker->ConsumerCount() > 0;
+  return progressTracker && progressTracker->ObserverCount() > 0;
 }
 
 int32_t imgRequest::Priority() const
 {
   int32_t priority = nsISupportsPriority::PRIORITY_NORMAL;
   nsCOMPtr<nsISupportsPriority> p = do_QueryInterface(mRequest);
   if (p)
     p->GetPriority(&priority);
@@ -434,17 +434,17 @@ void imgRequest::AdjustPriority(imgReque
   // only the first proxy is allowed to modify the priority of this image load.
   //
   // XXX(darin): this is probably not the most optimal algorithm as we may want
   // to increase the priority of requests that have a lot of proxies.  the key
   // concern though is that image loads remain lower priority than other pieces
   // of content such as link clicks, CSS, and JS.
   //
   nsRefPtr<ProgressTracker> progressTracker = GetProgressTracker();
-  if (!progressTracker->FirstConsumerIs(proxy))
+  if (!progressTracker->FirstObserverIs(proxy))
     return;
 
   nsCOMPtr<nsISupportsPriority> p = do_QueryInterface(mChannel);
   if (p)
     p->AdjustPriority(delta);
 }
 
 void imgRequest::SetIsInCache(bool incache)
@@ -696,17 +696,17 @@ NS_IMETHODIMP imgRequest::OnStartRequest
     }
   }
 
   SetCacheValidation(mCacheEntry, aRequest);
 
   mApplicationCache = GetApplicationCache(aRequest);
 
   // Shouldn't we be dead already if this gets hit?  Probably multipart/x-mixed-replace...
-  if (progressTracker->ConsumerCount() == 0) {
+  if (progressTracker->ObserverCount() == 0) {
     this->Cancel(NS_IMAGELIB_ERROR_FAILURE);
   }
 
   // Try to retarget OnDataAvailable to a decode thread.
   nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aRequest);
   nsCOMPtr<nsIThreadRetargetableRequest> retargetable =
     do_QueryInterface(aRequest);
   if (httpChannel && retargetable &&
@@ -897,17 +897,17 @@ imgRequest::OnDataAvailable(nsIRequest *
         MOZ_ASSERT(mIsMultiPartChannel, "Resniffing a non-multipart image");
 
         // Initialize a new status tracker.
         nsRefPtr<ProgressTracker> freshTracker = new ProgressTracker(nullptr);
         freshTracker->SetIsMultipart();
 
         // Replace the old status tracker with it.
         nsRefPtr<ProgressTracker> oldProgressTracker = GetProgressTracker();
-        freshTracker->AdoptConsumers(oldProgressTracker);
+        freshTracker->AdoptObservers(oldProgressTracker);
         mProgressTracker = freshTracker.forget();
       }
 
       SetProperties(chan);
 
       LOG_MSG_WITH_PARAM(GetImgLog(), "imgRequest::OnDataAvailable", "content type", mContentType.get());
 
       // XXX If server lied about mimetype and it's SVG, we may need to copy
--- a/image/src/imgRequestProxy.cpp
+++ b/image/src/imgRequestProxy.cpp
@@ -729,108 +729,62 @@ void imgRequestProxy::OnStartDecode()
     // OnStartDecodes which indicates the beginning of a new decode.  The cache
     // entry's size therefore needs to be reset to 0 here.  If we do not do
     // this, the code in ProgressTrackerObserver::OnStopFrame will continue to
     // increase the data size cumulatively.
     GetOwner()->ResetCacheEntry();
   }
 }
 
-void imgRequestProxy::OnSizeAvailable()
-{
-  LOG_FUNC(GetImgLog(), "imgRequestProxy::OnStartContainer");
-
-  if (mListener && !mCanceled) {
-    // Hold a ref to the listener while we call it, just in case.
-    nsCOMPtr<imgINotificationObserver> kungFuDeathGrip(mListener);
-    mListener->Notify(this, imgINotificationObserver::SIZE_AVAILABLE, nullptr);
-  }
-}
-
-void imgRequestProxy::OnFrameUpdate(const nsIntRect * rect)
+static const char*
+NotificationTypeToString(int32_t aType)
 {
-  LOG_FUNC(GetImgLog(), "imgRequestProxy::OnFrameUpdate");
-
-  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_UPDATE, rect);
-  }
-}
-
-void imgRequestProxy::OnFrameComplete()
-{
-  LOG_FUNC(GetImgLog(), "imgRequestProxy::OnFrameComplete");
-
-  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_COMPLETE, nullptr);
-  }
-}
-
-void imgRequestProxy::OnDecodeComplete()
-{
-  LOG_FUNC(GetImgLog(), "imgRequestProxy::OnDecodeComplete");
-
-  if (mListener && !mCanceled) {
-    // Hold a ref to the listener while we call it, just in case.
-    nsCOMPtr<imgINotificationObserver> kungFuDeathGrip(mListener);
-    mListener->Notify(this, imgINotificationObserver::DECODE_COMPLETE, nullptr);
+  switch(aType)
+  {
+    case imgINotificationObserver::SIZE_AVAILABLE: return "SIZE_AVAILABLE";
+    case imgINotificationObserver::FRAME_UPDATE: return "FRAME_UPDATE";
+    case imgINotificationObserver::FRAME_COMPLETE: return "FRAME_COMPLETE";
+    case imgINotificationObserver::LOAD_COMPLETE: return "LOAD_COMPLETE";
+    case imgINotificationObserver::DECODE_COMPLETE: return "DECODE_COMPLETE";
+    case imgINotificationObserver::DISCARD: return "DISCARD";
+    case imgINotificationObserver::UNLOCKED_DRAW: return "UNLOCKED_DRAW";
+    case imgINotificationObserver::IS_ANIMATED: return "IS_ANIMATED";
+    case imgINotificationObserver::HAS_TRANSPARENCY: return "HAS_TRANSPARENCY";
+    default:
+      NS_NOTREACHED("Notification list should be exhaustive");
+      return "(unknown notification)";
   }
 }
 
-void imgRequestProxy::OnDiscard()
+void
+imgRequestProxy::Notify(int32_t aType, const nsIntRect* aRect)
 {
-  LOG_FUNC(GetImgLog(), "imgRequestProxy::OnDiscard");
+  MOZ_ASSERT(aType != imgINotificationObserver::LOAD_COMPLETE,
+             "Should call OnLoadComplete");
 
-  if (mListener && !mCanceled) {
-    // Hold a ref to the listener while we call it, just in case.
-    nsCOMPtr<imgINotificationObserver> kungFuDeathGrip(mListener);
-    mListener->Notify(this, imgINotificationObserver::DISCARD, nullptr);
+  LOG_FUNC_WITH_PARAM(GetImgLog(), "imgRequestProxy::Notify", "type",
+                      NotificationTypeToString(aType));
+
+  if (!mListener || mCanceled) {
+    return;
   }
-}
-
-void imgRequestProxy::OnUnlockedDraw()
-{
-  LOG_FUNC(GetImgLog(), "imgRequestProxy::OnUnlockedDraw");
 
-  if (mListener && !mCanceled) {
-    // Hold a ref to the listener while we call it, just in case.
-    nsCOMPtr<imgINotificationObserver> kungFuDeathGrip(mListener);
-    mListener->Notify(this, imgINotificationObserver::UNLOCKED_DRAW, nullptr);
-  }
+  // Make sure the listener stays alive while we notify.
+  nsCOMPtr<imgINotificationObserver> listener(mListener);
+
+  mListener->Notify(this, aType, aRect);
 }
 
-void imgRequestProxy::OnImageHasTransparency()
-{
-  LOG_FUNC(GetImgLog(), "imgRequestProxy::OnImageHasTransparency");
-  if (mListener && !mCanceled) {
-    // Hold a ref to the listener while we call it, just in case.
-    nsCOMPtr<imgINotificationObserver> kungFuDeathGrip(mListener);
-    mListener->Notify(this, imgINotificationObserver::HAS_TRANSPARENCY, nullptr);
-  }
-}
-
-void imgRequestProxy::OnImageIsAnimated()
-{
-  LOG_FUNC(GetImgLog(), "imgRequestProxy::OnImageIsAnimated");
-  if (mListener && !mCanceled) {
-    // Hold a ref to the listener while we call it, just in case.
-    nsCOMPtr<imgINotificationObserver> kungFuDeathGrip(mListener);
-    mListener->Notify(this, imgINotificationObserver::IS_ANIMATED, nullptr);
-  }
-}
-
-void imgRequestProxy::OnLoadComplete(bool aLastPart)
+void
+imgRequestProxy::OnLoadComplete(bool aLastPart)
 {
 #ifdef PR_LOGGING
   nsAutoCString name;
   GetName(name);
-  LOG_FUNC_WITH_PARAM(GetImgLog(), "imgRequestProxy::OnStopRequest", "name", name.get());
+  LOG_FUNC_WITH_PARAM(GetImgLog(), "imgRequestProxy::OnLoadComplete", "name", name.get());
 #endif
   // 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 && !mCanceled) {
     // Hold a ref to the listener while we call it, just in case.
@@ -858,31 +812,33 @@ void imgRequestProxy::OnLoadComplete(boo
     // everything.  Note that this can cancel us and other fun things
     // like that.  Don't add anything in this method after this point.
     imgINotificationObserver* obs = mListener;
     mListenerIsStrongRef = false;
     NS_RELEASE(obs);
   }
 }
 
-void imgRequestProxy::BlockOnload()
+void
+imgRequestProxy::BlockOnload()
 {
 #ifdef PR_LOGGING
   nsAutoCString name;
   GetName(name);
   LOG_FUNC_WITH_PARAM(GetImgLog(), "imgRequestProxy::BlockOnload", "name", name.get());
 #endif
 
   nsCOMPtr<imgIOnloadBlocker> blocker = do_QueryInterface(mListener);
   if (blocker) {
     blocker->BlockOnload(this);
   }
 }
 
-void imgRequestProxy::UnblockOnload()
+void
+imgRequestProxy::UnblockOnload()
 {
 #ifdef PR_LOGGING
   nsAutoCString name;
   GetName(name);
   LOG_FUNC_WITH_PARAM(GetImgLog(), "imgRequestProxy::UnblockOnload", "name", name.get());
 #endif
 
   nsCOMPtr<imgIOnloadBlocker> blocker = do_QueryInterface(mListener);
--- a/image/src/imgRequestProxy.h
+++ b/image/src/imgRequestProxy.h
@@ -2,29 +2,29 @@
  *
  * 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/. */
 
 #ifndef imgRequestProxy_h__
 #define imgRequestProxy_h__
 
-#include "mozilla/WeakPtr.h"
 #include "imgIRequest.h"
 #include "nsISecurityInfoProvider.h"
 
 #include "nsILoadGroup.h"
 #include "nsISupportsPriority.h"
 #include "nsITimedChannel.h"
 #include "nsCOMPtr.h"
 #include "nsAutoPtr.h"
 #include "nsThreadUtils.h"
 #include "mozilla/TimeStamp.h"
 
 #include "imgRequest.h"
+#include "IProgressObserver.h"
 
 #define NS_IMGREQUESTPROXY_CID \
 { /* 20557898-1dd2-11b2-8f65-9c462ee2bc95 */         \
      0x20557898,                                     \
      0x1dd2,                                         \
      0x11b2,                                         \
     {0x8f, 0x65, 0x9c, 0x46, 0x2e, 0xe2, 0xbc, 0x95} \
 }
@@ -39,20 +39,20 @@ namespace mozilla {
 namespace image {
 class Image;
 class ImageURL;
 class ProgressTracker;
 } // namespace image
 } // namespace mozilla
 
 class imgRequestProxy : public imgIRequest,
+                        public mozilla::image::IProgressObserver,
                         public nsISupportsPriority,
                         public nsISecurityInfoProvider,
-                        public nsITimedChannel,
-                        public mozilla::SupportsWeakPtr<imgRequestProxy>
+                        public nsITimedChannel
 {
 protected:
   virtual ~imgRequestProxy();
 
 public:
   typedef mozilla::image::Image Image;
   typedef mozilla::image::ImageURL ImageURL;
   typedef mozilla::image::ProgressTracker ProgressTracker;
@@ -90,30 +90,40 @@ public:
   // notification is scheduled.
   void NotifyListener();
 
   // Synchronously notify this proxy's listener of the current state of the
   // image. Only use this function if you are currently servicing an
   // asynchronously-called function.
   void SyncNotifyListener();
 
+  // imgINotificationObserver methods:
+  virtual void Notify(int32_t aType,
+                      const nsIntRect* aRect = nullptr) MOZ_OVERRIDE;
+  virtual void OnLoadComplete(bool aLastPart) MOZ_OVERRIDE;
+
+  // imgIOnloadBlocker methods:
+  virtual void BlockOnload() MOZ_OVERRIDE;
+  virtual void UnblockOnload() MOZ_OVERRIDE;
+
+  // Other, internal-only methods:
+  virtual void SetHasImage() MOZ_OVERRIDE;
+  virtual void OnStartDecode() MOZ_OVERRIDE;
+
   // Whether we want notifications from ProgressTracker to be deferred until
   // an event it has scheduled has been fired.
-  bool NotificationsDeferred() const
+  virtual bool NotificationsDeferred() const MOZ_OVERRIDE
   {
     return mDeferNotifications;
   }
-  void SetNotificationsDeferred(bool aDeferNotifications)
+  virtual void SetNotificationsDeferred(bool aDeferNotifications) MOZ_OVERRIDE
   {
     mDeferNotifications = aDeferNotifications;
   }
 
-  // XXXbholley - This eventually gets folded into the new notification API.
-  void SetHasImage();
-
   // Removes all animation consumers that were created with
   // IncrementAnimationConsumers. This is necessary since we need
   // to do it before the proxy itself is destroyed. See
   // imgRequest::RemoveProxy
   void ClearAnimationConsumers();
 
   virtual nsresult Clone(imgINotificationObserver* aObserver, imgRequestProxy** aClone);
   nsresult GetStaticRequest(imgRequestProxy** aReturn);
@@ -140,37 +150,16 @@ protected:
         return NS_OK;
       }
 
     private:
       nsRefPtr<imgRequestProxy> mOwner;
       nsresult mStatus;
   };
 
-  // The following notification functions are protected to ensure that (friend
-  // class) ProgressTracker is the only class allowed to send us
-  // notifications.
-
-  void OnStartDecode();
-  void OnSizeAvailable();
-  void OnFrameUpdate(const nsIntRect* aRect);
-  void OnFrameComplete();
-  void OnDecodeComplete();
-  void OnDiscard();
-  void OnUnlockedDraw();
-  void OnImageHasTransparency();
-  void OnImageIsAnimated();
-
-  /* non-virtual sort-of-nsIRequestObserver methods */
-  void OnLoadComplete(bool aLastPart);
-
-  /* non-virtual imgIOnloadBlocker methods */
-  void BlockOnload();
-  void UnblockOnload();
-
   /* Finish up canceling ourselves */
   void DoCancel(nsresult status);
 
   /* Do the proper refcount management to null out mListener */
   void NullOutListener();
 
   void DoRemoveFromLoadGroup() {
     RemoveFromLoadGroup(true);
--- a/image/src/moz.build
+++ b/image/src/moz.build
@@ -5,18 +5,19 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 EXPORTS += [
     'ImageOps.h',
     'ImageRegion.h',
     'imgLoader.h',
     'imgRequest.h',
     'imgRequestProxy.h',
+    'IProgressObserver.h',
     'Orientation.h',
-    'SurfaceCache.h'
+    'SurfaceCache.h',
 ]
 
 UNIFIED_SOURCES += [
     'ClippedImage.cpp',
     'DecodePool.cpp',
     'Decoder.cpp',
     'DynamicImage.cpp',
     'FrameAnimator.cpp',