--- 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',