Bug 1682766 [wpt PR 26921] - Fix nullptr dereference accessing PolicyContainer in...
[gecko.git] / image / ProgressTracker.h
blobae07a83536e443ce1144e7a13f135713a9831a58
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 {
103 virtual ~ProgressTracker() {}
105 public:
106 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ProgressTracker)
108 ProgressTracker();
110 bool HasImage() const {
111 MutexAutoLock lock(mMutex);
112 return mImage;
114 already_AddRefed<Image> GetImage() const {
115 MutexAutoLock lock(mMutex);
116 RefPtr<Image> image = mImage;
117 return image.forget();
120 // Get the current image status (as in imgIRequest).
121 uint32_t GetImageStatus() const;
123 // Get the current Progress.
124 Progress GetProgress() const { return mProgress; }
126 // Schedule an asynchronous "replaying" of all the notifications that would
127 // have to happen to put us in the current state.
128 // We will also take note of any notifications that happen between the time
129 // Notify() is called and when we call SyncNotify on |aObserver|, and replay
130 // them as well.
131 // Should be called on the main thread only, since observers and GetURI are
132 // not threadsafe.
133 void Notify(IProgressObserver* aObserver);
135 // Schedule an asynchronous "replaying" of all the notifications that would
136 // have to happen to put us in the state we are in right now.
137 // Unlike Notify(), does *not* take into account future notifications.
138 // This is only useful if you do not have an imgRequest, e.g., if you are a
139 // static request returned from imgIRequest::GetStaticRequest().
140 // Should be called on the main thread only, since observers and GetURI are
141 // not threadsafe.
142 void NotifyCurrentState(IProgressObserver* aObserver);
144 // "Replay" all of the notifications that would have to happen to put us in
145 // the state we're currently in.
146 // Only use this if you're already servicing an asynchronous call (e.g.
147 // OnStartRequest).
148 // Should be called on the main thread only, since observers and GetURI are
149 // not threadsafe.
150 void SyncNotify(IProgressObserver* aObserver);
152 // Get this ProgressTracker ready for a new request. This resets all the
153 // state that doesn't persist between requests.
154 void ResetForNewRequest();
156 // Stateless notifications. These are dispatched and immediately forgotten
157 // about. All of these notifications are main thread only.
158 void OnDiscard();
159 void OnUnlockedDraw();
160 void OnImageAvailable();
162 // Compute the difference between this our progress and aProgress. This allows
163 // callers to predict whether SyncNotifyProgress will send any notifications.
164 Progress Difference(Progress aProgress) const {
165 return ~mProgress & aProgress;
168 // Update our state to incorporate the changes in aProgress and synchronously
169 // notify our observers.
171 // Because this may result in recursive notifications, no decoding locks may
172 // be held. Called on the main thread only.
173 void SyncNotifyProgress(Progress aProgress,
174 const nsIntRect& aInvalidRect = nsIntRect());
176 // We manage a set of observers that are using an image and thus concerned
177 // with its loading progress. Weak pointers.
178 void AddObserver(IProgressObserver* aObserver);
179 bool RemoveObserver(IProgressObserver* aObserver);
180 uint32_t ObserverCount() const;
182 // Get the event target we should currently dispatch events to.
183 already_AddRefed<nsIEventTarget> GetEventTarget() const;
185 // Resets our weak reference to our image. Image subclasses should call this
186 // in their destructor.
187 void ResetImage();
189 // Tell this progress tracker that it is for a multipart image.
190 void SetIsMultipart() { mIsMultipart = true; }
192 private:
193 friend class AsyncNotifyRunnable;
194 friend class AsyncNotifyCurrentStateRunnable;
195 friend class ImageFactory;
197 ProgressTracker(const ProgressTracker& aOther) = delete;
199 // Sets our weak reference to our image. Only ImageFactory should call this.
200 void SetImage(Image* aImage);
202 // Send some notifications that would be necessary to make |aObserver| believe
203 // the request is finished downloading and decoding. We only send
204 // FLAG_LOAD_COMPLETE and FLAG_ONLOAD_UNBLOCKED, and only if necessary.
205 void EmulateRequestFinished(IProgressObserver* aObserver);
207 // Main thread only because it deals with the observer service.
208 void FireFailureNotification();
210 // Wrapper for AsyncNotifyRunnable to make it have medium high priority like
211 // other imagelib runnables.
212 class MediumHighRunnable final : public PrioritizableRunnable {
213 explicit MediumHighRunnable(already_AddRefed<AsyncNotifyRunnable>&& aEvent);
214 virtual ~MediumHighRunnable() = default;
216 public:
217 void AddObserver(IProgressObserver* aObserver);
218 void RemoveObserver(IProgressObserver* aObserver);
220 static already_AddRefed<MediumHighRunnable> Create(
221 already_AddRefed<AsyncNotifyRunnable>&& aEvent);
224 // The runnable, if any, that we've scheduled to deliver async notifications.
225 RefPtr<MediumHighRunnable> mRunnable;
227 // mMutex protects access to mImage and mEventTarget.
228 mutable Mutex mMutex;
230 // mImage is a weak ref; it should be set to null when the image goes out of
231 // scope.
232 Image* mImage;
234 // mEventTarget is the current, best effort event target to dispatch
235 // notifications to from the decoder threads. It will change as observers are
236 // added and removed (see mObserversWithTargets).
237 NotNull<nsCOMPtr<nsIEventTarget>> mEventTarget;
239 // How many observers have been added that have an explicit event target.
240 // When the first observer is added with an explicit event target, we will
241 // default to that as long as all observers use the same target. If a new
242 // observer is added which has a different event target, we will switch to
243 // using the unlabeled main thread event target which is safe for all
244 // observers. If all observers with explicit event targets are removed, we
245 // will revert back to the initial event target (for SystemGroup). An
246 // observer without an explicit event target does not care what context it
247 // is dispatched in, and thus does not impact the state.
248 uint32_t mObserversWithTargets;
250 // Hashtable of observers attached to the image. Each observer represents a
251 // consumer using the image. Main thread only.
252 CopyOnWrite<ObserverTable> mObservers;
254 Progress mProgress;
256 // Whether this is a progress tracker for a multipart image.
257 bool mIsMultipart;
260 } // namespace image
261 } // namespace mozilla
263 #endif // mozilla_image_ProgressTracker_h