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"
17 #include "nsTObserverArray.h"
18 #include "nsThreadUtils.h"
20 #include "IProgressObserver.h"
27 class AsyncNotifyRunnable
;
28 class AsyncNotifyCurrentStateRunnable
;
32 * Image progress bitflags.
34 * See CheckProgressConsistency() for the invariants we enforce about the
35 * ordering dependencies between these flags.
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
,
54 Progress progress
= FLAG_LOAD_COMPLETE
;
56 progress
|= FLAG_LAST_PART_COMPLETE
;
58 if (NS_FAILED(aStatus
) || aError
) {
59 progress
|= FLAG_HAS_ERROR
;
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
>> {
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());
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
102 class ProgressTracker
: public mozilla::SupportsWeakPtr
<ProgressTracker
> {
103 virtual ~ProgressTracker() {}
106 MOZ_DECLARE_WEAKREFERENCE_TYPENAME(ProgressTracker
)
107 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ProgressTracker
)
111 bool HasImage() const {
112 MutexAutoLock
lock(mMutex
);
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
132 // Should be called on the main thread only, since observers and GetURI are
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
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.
149 // Should be called on the main thread only, since observers and GetURI are
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.
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.
190 // Tell this progress tracker that it is for a multipart image.
191 void SetIsMultipart() { mIsMultipart
= true; }
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;
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
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
;
257 // Whether this is a progress tracker for a multipart image.
262 } // namespace mozilla
264 #endif // mozilla_image_ProgressTracker_h