/* -*- 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/NotNull.h" #include "mozilla/Mutex.h" #include "mozilla/RefPtr.h" #include "mozilla/WeakPtr.h" #include "nsTHashMap.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 between 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_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 nsTHashMap to add reference counting * support and a copy constructor, both of which are needed for use with * CopyOnWrite. */ class ObserverTable : public nsTHashMap, WeakPtr> { public: NS_INLINE_DECL_REFCOUNTING(ObserverTable); ObserverTable() = default; ObserverTable(const ObserverTable& aOther) : nsTHashMap, WeakPtr>( aOther.Clone()) { NS_WARNING("Forced to copy ObserverTable due to nested notifications"); } 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 { virtual ~ProgressTracker() {} public: NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ProgressTracker) ProgressTracker(); bool HasImage() const { MutexAutoLock lock(mMutex); return mImage; } already_AddRefed GetImage() const { MutexAutoLock lock(mMutex); RefPtr 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; // Get the event target we should currently dispatch events to. already_AddRefed GetEventTarget() const; // Resets our weak reference to our image. Image subclasses should call this // in their destructor. void ResetImage(); // Tell this progress tracker that it is for a multipart image. void SetIsMultipart() { mIsMultipart = true; } 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(); // Wrapper for AsyncNotifyRunnable to make it have medium high priority like // other imagelib runnables. class MediumHighRunnable final : public PrioritizableRunnable { explicit MediumHighRunnable(already_AddRefed&& aEvent); virtual ~MediumHighRunnable() = default; public: void AddObserver(IProgressObserver* aObserver); void RemoveObserver(IProgressObserver* aObserver); static already_AddRefed Create( already_AddRefed&& aEvent); }; // The runnable, if any, that we've scheduled to deliver async notifications. RefPtr mRunnable; // mMutex protects access to mImage and mEventTarget. mutable Mutex mMutex; // mImage is a weak ref; it should be set to null when the image goes out of // scope. Image* mImage; // mEventTarget is the current, best effort event target to dispatch // notifications to from the decoder threads. It will change as observers are // added and removed (see mObserversWithTargets). NotNull> mEventTarget; // How many observers have been added that have an explicit event target. // When the first observer is added with an explicit event target, we will // default to that as long as all observers use the same target. If a new // observer is added which has a different event target, we will switch to // using the unlabeled main thread event target which is safe for all // observers. If all observers with explicit event targets are removed, we // will revert back to the initial event target (for SystemGroup). An // observer without an explicit event target does not care what context it // is dispatched in, and thus does not impact the state. uint32_t mObserversWithTargets; // Hashtable of observers attached to the image. Each observer represents a // consumer using the image. Main thread only. CopyOnWrite mObservers; Progress mProgress; // Whether this is a progress tracker for a multipart image. bool mIsMultipart; }; } // namespace image } // namespace mozilla #endif // mozilla_image_ProgressTracker_h