/src/mozilla-central/image/ProgressTracker.h
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- |
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/. */ |
6 | | |
7 | | #ifndef mozilla_image_ProgressTracker_h |
8 | | #define mozilla_image_ProgressTracker_h |
9 | | |
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" |
21 | | |
22 | | class nsIRunnable; |
23 | | |
24 | | namespace mozilla { |
25 | | namespace image { |
26 | | |
27 | | class AsyncNotifyRunnable; |
28 | | class AsyncNotifyCurrentStateRunnable; |
29 | | class Image; |
30 | | |
31 | | /** |
32 | | * Image progress bitflags. |
33 | | * |
34 | | * See CheckProgressConsistency() for the invariants we enforce about the |
35 | | * ordering dependencies betweeen these flags. |
36 | | */ |
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 |
46 | | }; |
47 | | |
48 | | typedef uint32_t Progress; |
49 | | |
50 | | const uint32_t NoProgress = 0; |
51 | | |
52 | | inline Progress LoadCompleteProgress(bool aLastPart, |
53 | | bool aError, |
54 | | nsresult aStatus) |
55 | 0 | { |
56 | 0 | Progress progress = FLAG_LOAD_COMPLETE; |
57 | 0 | if (aLastPart) { |
58 | 0 | progress |= FLAG_LAST_PART_COMPLETE; |
59 | 0 | } |
60 | 0 | if (NS_FAILED(aStatus) || aError) { |
61 | 0 | progress |= FLAG_HAS_ERROR; |
62 | 0 | } |
63 | 0 | return progress; |
64 | 0 | } |
65 | | |
66 | | /** |
67 | | * ProgressTracker stores its observers in an ObserverTable, which is a hash |
68 | | * table mapping raw pointers to WeakPtr's to the same objects. This sounds like |
69 | | * unnecessary duplication of information, but it's necessary for stable hash |
70 | | * values since WeakPtr's lose the knowledge of which object they used to point |
71 | | * to when that object is destroyed. |
72 | | * |
73 | | * ObserverTable subclasses nsDataHashtable to add reference counting support |
74 | | * and a copy constructor, both of which are needed for use with CopyOnWrite<T>. |
75 | | */ |
76 | | class ObserverTable |
77 | | : public nsDataHashtable<nsPtrHashKey<IProgressObserver>, |
78 | | WeakPtr<IProgressObserver>> |
79 | | { |
80 | | public: |
81 | | NS_INLINE_DECL_REFCOUNTING(ObserverTable); |
82 | | |
83 | 0 | ObserverTable() = default; |
84 | | |
85 | | ObserverTable(const ObserverTable& aOther) |
86 | 0 | { |
87 | 0 | NS_WARNING("Forced to copy ObserverTable due to nested notifications"); |
88 | 0 | for (auto iter = aOther.ConstIter(); !iter.Done(); iter.Next()) { |
89 | 0 | this->Put(iter.Key(), iter.Data()); |
90 | 0 | } |
91 | 0 | } |
92 | | |
93 | | private: |
94 | 0 | ~ObserverTable() { } |
95 | | }; |
96 | | |
97 | | /** |
98 | | * ProgressTracker is a class that records an Image's progress through the |
99 | | * loading and decoding process, and makes it possible to send notifications to |
100 | | * IProgressObservers, both synchronously and asynchronously. |
101 | | * |
102 | | * When a new observer needs to be notified of the current progress of an image, |
103 | | * call the Notify() method on this class with the relevant observer as its |
104 | | * argument, and the notifications will be replayed to the observer |
105 | | * asynchronously. |
106 | | */ |
107 | | class ProgressTracker : public mozilla::SupportsWeakPtr<ProgressTracker> |
108 | | { |
109 | 0 | virtual ~ProgressTracker() { } |
110 | | |
111 | | public: |
112 | | MOZ_DECLARE_WEAKREFERENCE_TYPENAME(ProgressTracker) |
113 | | NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ProgressTracker) |
114 | | |
115 | | ProgressTracker(); |
116 | | |
117 | 0 | bool HasImage() const { MutexAutoLock lock(mMutex); return mImage; } |
118 | | already_AddRefed<Image> GetImage() const |
119 | 0 | { |
120 | 0 | MutexAutoLock lock(mMutex); |
121 | 0 | RefPtr<Image> image = mImage; |
122 | 0 | return image.forget(); |
123 | 0 | } |
124 | | |
125 | | // Get the current image status (as in imgIRequest). |
126 | | uint32_t GetImageStatus() const; |
127 | | |
128 | | // Get the current Progress. |
129 | 0 | Progress GetProgress() const { return mProgress; } |
130 | | |
131 | | // Schedule an asynchronous "replaying" of all the notifications that would |
132 | | // have to happen to put us in the current state. |
133 | | // We will also take note of any notifications that happen between the time |
134 | | // Notify() is called and when we call SyncNotify on |aObserver|, and replay |
135 | | // them as well. |
136 | | // Should be called on the main thread only, since observers and GetURI are |
137 | | // not threadsafe. |
138 | | void Notify(IProgressObserver* aObserver); |
139 | | |
140 | | // Schedule an asynchronous "replaying" of all the notifications that would |
141 | | // have to happen to put us in the state we are in right now. |
142 | | // Unlike Notify(), does *not* take into account future notifications. |
143 | | // This is only useful if you do not have an imgRequest, e.g., if you are a |
144 | | // static request returned from imgIRequest::GetStaticRequest(). |
145 | | // Should be called on the main thread only, since observers and GetURI are |
146 | | // not threadsafe. |
147 | | void NotifyCurrentState(IProgressObserver* aObserver); |
148 | | |
149 | | // "Replay" all of the notifications that would have to happen to put us in |
150 | | // the state we're currently in. |
151 | | // Only use this if you're already servicing an asynchronous call (e.g. |
152 | | // OnStartRequest). |
153 | | // Should be called on the main thread only, since observers and GetURI are |
154 | | // not threadsafe. |
155 | | void SyncNotify(IProgressObserver* aObserver); |
156 | | |
157 | | // Get this ProgressTracker ready for a new request. This resets all the |
158 | | // state that doesn't persist between requests. |
159 | | void ResetForNewRequest(); |
160 | | |
161 | | // Stateless notifications. These are dispatched and immediately forgotten |
162 | | // about. All of these notifications are main thread only. |
163 | | void OnDiscard(); |
164 | | void OnUnlockedDraw(); |
165 | | void OnImageAvailable(); |
166 | | |
167 | | // Compute the difference between this our progress and aProgress. This allows |
168 | | // callers to predict whether SyncNotifyProgress will send any notifications. |
169 | | Progress Difference(Progress aProgress) const |
170 | 0 | { |
171 | 0 | return ~mProgress & aProgress; |
172 | 0 | } |
173 | | |
174 | | // Update our state to incorporate the changes in aProgress and synchronously |
175 | | // notify our observers. |
176 | | // |
177 | | // Because this may result in recursive notifications, no decoding locks may |
178 | | // be held. Called on the main thread only. |
179 | | void SyncNotifyProgress(Progress aProgress, |
180 | | const nsIntRect& aInvalidRect = nsIntRect()); |
181 | | |
182 | | // We manage a set of observers that are using an image and thus concerned |
183 | | // with its loading progress. Weak pointers. |
184 | | void AddObserver(IProgressObserver* aObserver); |
185 | | bool RemoveObserver(IProgressObserver* aObserver); |
186 | | uint32_t ObserverCount() const; |
187 | | |
188 | | // Get the event target we should currently dispatch events to. |
189 | | already_AddRefed<nsIEventTarget> GetEventTarget() const; |
190 | | |
191 | | // Resets our weak reference to our image. Image subclasses should call this |
192 | | // in their destructor. |
193 | | void ResetImage(); |
194 | | |
195 | | // Tell this progress tracker that it is for a multipart image. |
196 | 0 | void SetIsMultipart() { mIsMultipart = true; } |
197 | | |
198 | | private: |
199 | | friend class AsyncNotifyRunnable; |
200 | | friend class AsyncNotifyCurrentStateRunnable; |
201 | | friend class ImageFactory; |
202 | | |
203 | | ProgressTracker(const ProgressTracker& aOther) = delete; |
204 | | |
205 | | // Sets our weak reference to our image. Only ImageFactory should call this. |
206 | | void SetImage(Image* aImage); |
207 | | |
208 | | // Send some notifications that would be necessary to make |aObserver| believe |
209 | | // the request is finished downloading and decoding. We only send |
210 | | // FLAG_LOAD_COMPLETE and FLAG_ONLOAD_UNBLOCKED, and only if necessary. |
211 | | void EmulateRequestFinished(IProgressObserver* aObserver); |
212 | | |
213 | | // Main thread only because it deals with the observer service. |
214 | | void FireFailureNotification(); |
215 | | |
216 | | // The runnable, if any, that we've scheduled to deliver async notifications. |
217 | | nsCOMPtr<nsIRunnable> mRunnable; |
218 | | |
219 | | // mMutex protects access to mImage and mEventTarget. |
220 | | mutable Mutex mMutex; |
221 | | |
222 | | // mImage is a weak ref; it should be set to null when the image goes out of |
223 | | // scope. |
224 | | Image* mImage; |
225 | | |
226 | | // mEventTarget is the current, best effort event target to dispatch |
227 | | // notifications to from the decoder threads. It will change as observers are |
228 | | // added and removed (see mObserversWithTargets). |
229 | | NotNull<nsCOMPtr<nsIEventTarget>> mEventTarget; |
230 | | |
231 | | // How many observers have been added that have an explicit event target. |
232 | | // When the first observer is added with an explicit event target, we will |
233 | | // default to that as long as all observers use the same target. If a new |
234 | | // observer is added which has a different event target, we will switch to |
235 | | // using the unlabeled main thread event target which is safe for all |
236 | | // observers. If all observers with explicit event targets are removed, we |
237 | | // will revert back to the initial event target (for SystemGroup). An |
238 | | // observer without an explicit event target does not care what context it |
239 | | // is dispatched in, and thus does not impact the state. |
240 | | uint32_t mObserversWithTargets; |
241 | | |
242 | | // Hashtable of observers attached to the image. Each observer represents a |
243 | | // consumer using the image. Main thread only. |
244 | | CopyOnWrite<ObserverTable> mObservers; |
245 | | |
246 | | Progress mProgress; |
247 | | |
248 | | // Whether this is a progress tracker for a multipart image. |
249 | | bool mIsMultipart; |
250 | | }; |
251 | | |
252 | | } // namespace image |
253 | | } // namespace mozilla |
254 | | |
255 | | #endif // mozilla_image_ProgressTracker_h |