123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235 |
- /* -*- 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_image_ProgressTracker_h
- #define mozilla_image_ProgressTracker_h
- #include "CopyOnWrite.h"
- #include "mozilla/Mutex.h"
- #include "mozilla/RefPtr.h"
- #include "mozilla/WeakPtr.h"
- #include "nsDataHashtable.h"
- #include "nsCOMPtr.h"
- #include "nsTObserverArray.h"
- #include "nsThreadUtils.h"
- #include "nsRect.h"
- #include "IProgressObserver.h"
- class nsIRunnable;
- namespace mozilla {
- namespace image {
- class AsyncNotifyRunnable;
- class AsyncNotifyCurrentStateRunnable;
- class Image;
- /**
- * Image progress bitflags.
- *
- * See CheckProgressConsistency() for the invariants we enforce about the
- * ordering dependencies betweeen these flags.
- */
- enum {
- FLAG_SIZE_AVAILABLE = 1u << 0, // STATUS_SIZE_AVAILABLE
- FLAG_DECODE_COMPLETE = 1u << 1, // STATUS_DECODE_COMPLETE
- FLAG_FRAME_COMPLETE = 1u << 2, // STATUS_FRAME_COMPLETE
- FLAG_LOAD_COMPLETE = 1u << 3, // STATUS_LOAD_COMPLETE
- FLAG_ONLOAD_BLOCKED = 1u << 4,
- FLAG_ONLOAD_UNBLOCKED = 1u << 5,
- FLAG_IS_ANIMATED = 1u << 6, // STATUS_IS_ANIMATED
- FLAG_HAS_TRANSPARENCY = 1u << 7, // STATUS_HAS_TRANSPARENCY
- FLAG_LAST_PART_COMPLETE = 1u << 8,
- FLAG_HAS_ERROR = 1u << 9 // STATUS_ERROR
- };
- typedef uint32_t Progress;
- const uint32_t NoProgress = 0;
- inline Progress LoadCompleteProgress(bool aLastPart,
- bool aError,
- nsresult aStatus)
- {
- Progress progress = FLAG_LOAD_COMPLETE;
- if (aLastPart) {
- progress |= FLAG_LAST_PART_COMPLETE;
- }
- if (NS_FAILED(aStatus) || aError) {
- progress |= FLAG_HAS_ERROR;
- }
- return progress;
- }
- /**
- * ProgressTracker stores its observers in an ObserverTable, which is a hash
- * table mapping raw pointers to WeakPtr's to the same objects. This sounds like
- * unnecessary duplication of information, but it's necessary for stable hash
- * values since WeakPtr's lose the knowledge of which object they used to point
- * to when that object is destroyed.
- *
- * ObserverTable subclasses nsDataHashtable to add reference counting support
- * and a copy constructor, both of which are needed for use with CopyOnWrite<T>.
- */
- class ObserverTable
- : public nsDataHashtable<nsPtrHashKey<IProgressObserver>,
- WeakPtr<IProgressObserver>>
- {
- public:
- NS_INLINE_DECL_REFCOUNTING(ObserverTable);
- ObserverTable() = default;
- ObserverTable(const ObserverTable& aOther)
- {
- NS_WARNING("Forced to copy ObserverTable due to nested notifications");
- for (auto iter = aOther.ConstIter(); !iter.Done(); iter.Next()) {
- this->Put(iter.Key(), iter.Data());
- }
- }
- private:
- ~ObserverTable() { }
- };
- /**
- * ProgressTracker is a class that records an Image's progress through the
- * loading and decoding process, and makes it possible to send notifications to
- * IProgressObservers, both synchronously and 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_WEAKREFERENCE_TYPENAME(ProgressTracker)
- NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ProgressTracker)
- ProgressTracker()
- : mImageMutex("ProgressTracker::mImage")
- , mImage(nullptr)
- , mObservers(new ObserverTable)
- , mProgress(NoProgress)
- { }
- bool HasImage() const { MutexAutoLock lock(mImageMutex); return mImage; }
- already_AddRefed<Image> GetImage() const
- {
- MutexAutoLock lock(mImageMutex);
- RefPtr<Image> image = mImage;
- return image.forget();
- }
- // Get the current image status (as in imgIRequest).
- 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 |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 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 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 of these notifications are main thread only.
- void OnDiscard();
- void OnUnlockedDraw();
- void OnImageAvailable();
- // Compute the difference between this our progress and aProgress. This allows
- // callers to predict whether SyncNotifyProgress will send any notifications.
- Progress Difference(Progress aProgress) const
- {
- return ~mProgress & aProgress;
- }
- // 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 observers that are using an image and thus concerned
- // with its loading progress. Weak pointers.
- void AddObserver(IProgressObserver* aObserver);
- bool RemoveObserver(IProgressObserver* aObserver);
- uint32_t ObserverCount() const;
- // Resets our weak reference to our image. Image subclasses should call this
- // in their destructor.
- void ResetImage();
- private:
- friend class AsyncNotifyRunnable;
- friend class AsyncNotifyCurrentStateRunnable;
- friend class ImageFactory;
- ProgressTracker(const ProgressTracker& aOther) = delete;
- // Sets our weak reference to our image. Only ImageFactory should call this.
- void SetImage(Image* aImage);
- // Send some notifications that would be necessary to make |aObserver| believe
- // the request is finished downloading and decoding. We only send
- // FLAG_LOAD_COMPLETE and FLAG_ONLOAD_UNBLOCKED, and only if necessary.
- void EmulateRequestFinished(IProgressObserver* aObserver);
- // Main thread only because it deals with the observer service.
- void FireFailureNotification();
- // The runnable, if any, that we've scheduled to deliver async notifications.
- nsCOMPtr<nsIRunnable> mRunnable;
- // mImage is a weak ref; it should be set to null when the image goes out of
- // scope. mImageMutex protects mImage.
- mutable Mutex mImageMutex;
- Image* mImage;
- // Hashtable of observers attached to the image. Each observer represents a
- // consumer using the image. Main thread only.
- CopyOnWrite<ObserverTable> mObservers;
- Progress mProgress;
- };
- } // namespace image
- } // namespace mozilla
- #endif // mozilla_image_ProgressTracker_h
|