Bug 1645920 [wpt PR 24157] - [AspectRatio] Make intrinsic sizes respect aspect-ratio...
[gecko.git] / image / ProgressTracker.h
blobc2628e9bfa626913fe42e06bd31fd6d5e6ef5998
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #ifndef mozilla_image_ProgressTracker_h
8 #define mozilla_image_ProgressTracker_h
10 #include "CopyOnWrite.h"
11 #include "mozilla/NotNull.h"
12 #include "mozilla/Mutex.h"
13 #include "mozilla/RefPtr.h"
14 #include "mozilla/WeakPtr.h"
15 #include "nsDataHashtable.h"
16 #include "nsCOMPtr.h"
17 #include "nsTObserverArray.h"
18 #include "nsThreadUtils.h"
19 #include "nsRect.h"
20 #include "IProgressObserver.h"
22 class nsIRunnable;
24 namespace mozilla {
25 namespace image {
27 class AsyncNotifyRunnable;
28 class AsyncNotifyCurrentStateRunnable;
29 class Image;
31 /**
32 * Image progress bitflags.
34 * See CheckProgressConsistency() for the invariants we enforce about the
35 * ordering dependencies between these flags.
37 enum {
38 FLAG_SIZE_AVAILABLE = 1u << 0, // STATUS_SIZE_AVAILABLE
39 FLAG_DECODE_COMPLETE = 1u << 1, // STATUS_DECODE_COMPLETE
40 FLAG_FRAME_COMPLETE = 1u << 2, // STATUS_FRAME_COMPLETE
41 FLAG_LOAD_COMPLETE = 1u << 3, // STATUS_LOAD_COMPLETE
42 FLAG_IS_ANIMATED = 1u << 6, // STATUS_IS_ANIMATED
43 FLAG_HAS_TRANSPARENCY = 1u << 7, // STATUS_HAS_TRANSPARENCY
44 FLAG_LAST_PART_COMPLETE = 1u << 8,
45 FLAG_HAS_ERROR = 1u << 9 // STATUS_ERROR
48 typedef uint32_t Progress;
50 const uint32_t NoProgress = 0;
52 inline Progress LoadCompleteProgress(bool aLastPart, bool aError,
53 nsresult aStatus) {
54 Progress progress = FLAG_LOAD_COMPLETE;
55 if (aLastPart) {
56 progress |= FLAG_LAST_PART_COMPLETE;
58 if (NS_FAILED(aStatus) || aError) {
59 progress |= FLAG_HAS_ERROR;
61 return progress;
64 /**
65 * ProgressTracker stores its observers in an ObserverTable, which is a hash
66 * table mapping raw pointers to WeakPtr's to the same objects. This sounds like
67 * unnecessary duplication of information, but it's necessary for stable hash
68 * values since WeakPtr's lose the knowledge of which object they used to point
69 * to when that object is destroyed.
71 * ObserverTable subclasses nsDataHashtable to add reference counting support
72 * and a copy constructor, both of which are needed for use with CopyOnWrite<T>.
74 class ObserverTable : public nsDataHashtable<nsPtrHashKey<IProgressObserver>,
75 WeakPtr<IProgressObserver>> {
76 public:
77 NS_INLINE_DECL_REFCOUNTING(ObserverTable);
79 ObserverTable() = default;
81 ObserverTable(const ObserverTable& aOther) {
82 NS_WARNING("Forced to copy ObserverTable due to nested notifications");
83 for (auto iter = aOther.ConstIter(); !iter.Done(); iter.Next()) {
84 this->Put(iter.Key(), iter.Data());
88 private:
89 ~ObserverTable() {}
92 /**
93 * ProgressTracker is a class that records an Image's progress through the
94 * loading and decoding process, and makes it possible to send notifications to
95 * IProgressObservers, both synchronously and asynchronously.
97 * When a new observer needs to be notified of the current progress of an image,
98 * call the Notify() method on this class with the relevant observer as its
99 * argument, and the notifications will be replayed to the observer
100 * asynchronously.
102 class ProgressTracker : public mozilla::SupportsWeakPtr<ProgressTracker> {
103 virtual ~ProgressTracker() {}
105 public:
106 MOZ_DECLARE_WEAKREFERENCE_TYPENAME(ProgressTracker)
107 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ProgressTracker)
109 ProgressTracker();
111 bool HasImage() const {
112 MutexAutoLock lock(mMutex);
113 return mImage;
115 already_AddRefed<Image> GetImage() const {
116 MutexAutoLock lock(mMutex);
117 RefPtr<Image> image = mImage;
118 return image.forget();
121 // Get the current image status (as in imgIRequest).
122 uint32_t GetImageStatus() const;
124 // Get the current Progress.
125 Progress GetProgress() const { return mProgress; }
127 // Schedule an asynchronous "replaying" of all the notifications that would
128 // have to happen to put us in the current state.
129 // We will also take note of any notifications that happen between the time
130 // Notify() is called and when we call SyncNotify on |aObserver|, and replay
131 // them as well.
132 // Should be called on the main thread only, since observers and GetURI are
133 // not threadsafe.
134 void Notify(IProgressObserver* aObserver);
136 // Schedule an asynchronous "replaying" of all the notifications that would
137 // have to happen to put us in the state we are in right now.
138 // Unlike Notify(), does *not* take into account future notifications.
139 // This is only useful if you do not have an imgRequest, e.g., if you are a
140 // static request returned from imgIRequest::GetStaticRequest().
141 // Should be called on the main thread only, since observers and GetURI are
142 // not threadsafe.
143 void NotifyCurrentState(IProgressObserver* aObserver);
145 // "Replay" all of the notifications that would have to happen to put us in
146 // the state we're currently in.
147 // Only use this if you're already servicing an asynchronous call (e.g.
148 // OnStartRequest).
149 // Should be called on the main thread only, since observers and GetURI are
150 // not threadsafe.
151 void SyncNotify(IProgressObserver* aObserver);
153 // Get this ProgressTracker ready for a new request. This resets all the
154 // state that doesn't persist between requests.
155 void ResetForNewRequest();
157 // Stateless notifications. These are dispatched and immediately forgotten
158 // about. All of these notifications are main thread only.
159 void OnDiscard();
160 void OnUnlockedDraw();
161 void OnImageAvailable();
163 // Compute the difference between this our progress and aProgress. This allows
164 // callers to predict whether SyncNotifyProgress will send any notifications.
165 Progress Difference(Progress aProgress) const {
166 return ~mProgress & aProgress;
169 // Update our state to incorporate the changes in aProgress and synchronously
170 // notify our observers.
172 // Because this may result in recursive notifications, no decoding locks may
173 // be held. Called on the main thread only.
174 void SyncNotifyProgress(Progress aProgress,
175 const nsIntRect& aInvalidRect = nsIntRect());
177 // We manage a set of observers that are using an image and thus concerned
178 // with its loading progress. Weak pointers.
179 void AddObserver(IProgressObserver* aObserver);
180 bool RemoveObserver(IProgressObserver* aObserver);
181 uint32_t ObserverCount() const;
183 // Get the event target we should currently dispatch events to.
184 already_AddRefed<nsIEventTarget> GetEventTarget() const;
186 // Resets our weak reference to our image. Image subclasses should call this
187 // in their destructor.
188 void ResetImage();
190 // Tell this progress tracker that it is for a multipart image.
191 void SetIsMultipart() { mIsMultipart = true; }
193 private:
194 friend class AsyncNotifyRunnable;
195 friend class AsyncNotifyCurrentStateRunnable;
196 friend class ImageFactory;
198 ProgressTracker(const ProgressTracker& aOther) = delete;
200 // Sets our weak reference to our image. Only ImageFactory should call this.
201 void SetImage(Image* aImage);
203 // Send some notifications that would be necessary to make |aObserver| believe
204 // the request is finished downloading and decoding. We only send
205 // FLAG_LOAD_COMPLETE and FLAG_ONLOAD_UNBLOCKED, and only if necessary.
206 void EmulateRequestFinished(IProgressObserver* aObserver);
208 // Main thread only because it deals with the observer service.
209 void FireFailureNotification();
211 // Wrapper for AsyncNotifyRunnable to make it have medium high priority like
212 // other imagelib runnables.
213 class MediumHighRunnable final : public PrioritizableRunnable {
214 explicit MediumHighRunnable(already_AddRefed<AsyncNotifyRunnable>&& aEvent);
215 virtual ~MediumHighRunnable() = default;
217 public:
218 void AddObserver(IProgressObserver* aObserver);
219 void RemoveObserver(IProgressObserver* aObserver);
221 static already_AddRefed<MediumHighRunnable> Create(
222 already_AddRefed<AsyncNotifyRunnable>&& aEvent);
225 // The runnable, if any, that we've scheduled to deliver async notifications.
226 RefPtr<MediumHighRunnable> mRunnable;
228 // mMutex protects access to mImage and mEventTarget.
229 mutable Mutex mMutex;
231 // mImage is a weak ref; it should be set to null when the image goes out of
232 // scope.
233 Image* mImage;
235 // mEventTarget is the current, best effort event target to dispatch
236 // notifications to from the decoder threads. It will change as observers are
237 // added and removed (see mObserversWithTargets).
238 NotNull<nsCOMPtr<nsIEventTarget>> mEventTarget;
240 // How many observers have been added that have an explicit event target.
241 // When the first observer is added with an explicit event target, we will
242 // default to that as long as all observers use the same target. If a new
243 // observer is added which has a different event target, we will switch to
244 // using the unlabeled main thread event target which is safe for all
245 // observers. If all observers with explicit event targets are removed, we
246 // will revert back to the initial event target (for SystemGroup). An
247 // observer without an explicit event target does not care what context it
248 // is dispatched in, and thus does not impact the state.
249 uint32_t mObserversWithTargets;
251 // Hashtable of observers attached to the image. Each observer represents a
252 // consumer using the image. Main thread only.
253 CopyOnWrite<ObserverTable> mObservers;
255 Progress mProgress;
257 // Whether this is a progress tracker for a multipart image.
258 bool mIsMultipart;
261 } // namespace image
262 } // namespace mozilla
264 #endif // mozilla_image_ProgressTracker_h