author | Seth Fowler <seth@mozilla.com> |
Wed, 07 Jan 2015 01:35:20 -0800 | |
changeset 222477 | 07c75e0d6f673932be4e2b17e0a8bb6bd8a37e1e |
parent 222380 | 0776988aacd6201623ce747940fc7edac89e99d9 |
child 222478 | a94ab57658c06c2e7f70ae352369d406baafbbdb |
push id | 28067 |
push user | kwierso@gmail.com |
push date | Wed, 07 Jan 2015 23:41:38 +0000 |
treeherder | mozilla-central@70de2960aa87 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | tn |
bugs | 1112956 |
milestone | 37.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
|
--- 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',