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
{
103 virtual ~ProgressTracker() {}
106 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ProgressTracker
)
110 bool HasImage() const {
111 MutexAutoLock
lock(mMutex
);
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
131 // Should be called on the main thread only, since observers and GetURI are
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
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.
148 // Should be called on the main thread only, since observers and GetURI are
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.
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.
189 // Tell this progress tracker that it is for a multipart image.
190 void SetIsMultipart() { mIsMultipart
= true; }
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;
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
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
;
256 // Whether this is a progress tracker for a multipart image.
261 } // namespace mozilla
263 #endif // mozilla_image_ProgressTracker_h