|
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 imgStatusTracker_h__ |
|
8 #define imgStatusTracker_h__ |
|
9 |
|
10 class imgDecoderObserver; |
|
11 class imgIContainer; |
|
12 class imgStatusNotifyRunnable; |
|
13 class imgRequestNotifyRunnable; |
|
14 class imgStatusTrackerObserver; |
|
15 class nsIRunnable; |
|
16 |
|
17 #include "mozilla/RefPtr.h" |
|
18 #include "mozilla/WeakPtr.h" |
|
19 #include "nsCOMPtr.h" |
|
20 #include "nsTObserverArray.h" |
|
21 #include "nsThreadUtils.h" |
|
22 #include "nsRect.h" |
|
23 #include "imgRequestProxy.h" |
|
24 |
|
25 namespace mozilla { |
|
26 namespace image { |
|
27 |
|
28 class Image; |
|
29 |
|
30 struct ImageStatusDiff |
|
31 { |
|
32 ImageStatusDiff() |
|
33 : invalidRect() |
|
34 , diffState(0) |
|
35 , diffImageStatus(0) |
|
36 , unblockedOnload(false) |
|
37 , unsetDecodeStarted(false) |
|
38 , foundError(false) |
|
39 , foundIsMultipart(false) |
|
40 , foundLastPart(false) |
|
41 , gotDecoded(false) |
|
42 { } |
|
43 |
|
44 static ImageStatusDiff NoChange() { return ImageStatusDiff(); } |
|
45 bool IsNoChange() const { return *this == NoChange(); } |
|
46 |
|
47 bool operator!=(const ImageStatusDiff& aOther) const { return !(*this == aOther); } |
|
48 bool operator==(const ImageStatusDiff& aOther) const { |
|
49 return aOther.invalidRect == invalidRect |
|
50 && aOther.diffState == diffState |
|
51 && aOther.diffImageStatus == diffImageStatus |
|
52 && aOther.unblockedOnload == unblockedOnload |
|
53 && aOther.unsetDecodeStarted == unsetDecodeStarted |
|
54 && aOther.foundError == foundError |
|
55 && aOther.foundIsMultipart == foundIsMultipart |
|
56 && aOther.foundLastPart == foundLastPart |
|
57 && aOther.gotDecoded == gotDecoded; |
|
58 } |
|
59 |
|
60 void Combine(const ImageStatusDiff& aOther) { |
|
61 invalidRect = invalidRect.Union(aOther.invalidRect); |
|
62 diffState |= aOther.diffState; |
|
63 diffImageStatus |= aOther.diffImageStatus; |
|
64 unblockedOnload = unblockedOnload || aOther.unblockedOnload; |
|
65 unsetDecodeStarted = unsetDecodeStarted || aOther.unsetDecodeStarted; |
|
66 foundError = foundError || aOther.foundError; |
|
67 foundIsMultipart = foundIsMultipart || aOther.foundIsMultipart; |
|
68 foundLastPart = foundLastPart || aOther.foundLastPart; |
|
69 gotDecoded = gotDecoded || aOther.gotDecoded; |
|
70 } |
|
71 |
|
72 nsIntRect invalidRect; |
|
73 uint32_t diffState; |
|
74 uint32_t diffImageStatus; |
|
75 bool unblockedOnload : 1; |
|
76 bool unsetDecodeStarted : 1; |
|
77 bool foundError : 1; |
|
78 bool foundIsMultipart : 1; |
|
79 bool foundLastPart : 1; |
|
80 bool gotDecoded : 1; |
|
81 }; |
|
82 |
|
83 enum { |
|
84 stateRequestStarted = 1u << 0, |
|
85 stateHasSize = 1u << 1, |
|
86 stateDecodeStarted = 1u << 2, |
|
87 stateDecodeStopped = 1u << 3, |
|
88 stateFrameStopped = 1u << 4, |
|
89 stateRequestStopped = 1u << 5, |
|
90 stateBlockingOnload = 1u << 6, |
|
91 stateImageIsAnimated = 1u << 7 |
|
92 }; |
|
93 |
|
94 } // namespace image |
|
95 } // namespace mozilla |
|
96 |
|
97 /* |
|
98 * The image status tracker is a class that encapsulates all the loading and |
|
99 * decoding status about an Image, and makes it possible to send notifications |
|
100 * to imgRequestProxys, both synchronously (i.e., the status now) and |
|
101 * asynchronously (the status later). |
|
102 * |
|
103 * When a new proxy needs to be notified of the current state of an image, call |
|
104 * the Notify() method on this class with the relevant proxy as its argument, |
|
105 * and the notifications will be replayed to the proxy asynchronously. |
|
106 */ |
|
107 |
|
108 |
|
109 class imgStatusTracker : public mozilla::SupportsWeakPtr<imgStatusTracker> |
|
110 { |
|
111 public: |
|
112 MOZ_DECLARE_REFCOUNTED_TYPENAME(imgStatusTracker) |
|
113 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(imgStatusTracker) |
|
114 |
|
115 // aImage is the image that this status tracker will pass to the |
|
116 // imgRequestProxys in SyncNotify() and EmulateRequestFinished(), and must be |
|
117 // alive as long as this instance is, because we hold a weak reference to it. |
|
118 imgStatusTracker(mozilla::image::Image* aImage); |
|
119 virtual ~imgStatusTracker(); |
|
120 |
|
121 // Image-setter, for imgStatusTrackers created by imgRequest::Init, which |
|
122 // are created before their Image is created. This method should only |
|
123 // be called once, and only on an imgStatusTracker that was initialized |
|
124 // without an image. |
|
125 void SetImage(mozilla::image::Image* aImage); |
|
126 |
|
127 // Image resetter, for when mImage is about to go out of scope. mImage is a |
|
128 // weak reference, and thus must be set to null when it's object is deleted. |
|
129 void ResetImage(); |
|
130 |
|
131 // Inform this status tracker that it is associated with a multipart image. |
|
132 void SetIsMultipart() { mIsMultipart = true; } |
|
133 |
|
134 // Schedule an asynchronous "replaying" of all the notifications that would |
|
135 // have to happen to put us in the current state. |
|
136 // We will also take note of any notifications that happen between the time |
|
137 // Notify() is called and when we call SyncNotify on |proxy|, and replay them |
|
138 // as well. |
|
139 // Should be called on the main thread only, since imgRequestProxy and GetURI |
|
140 // are not threadsafe. |
|
141 void Notify(imgRequestProxy* proxy); |
|
142 |
|
143 // Schedule an asynchronous "replaying" of all the notifications that would |
|
144 // have to happen to put us in the state we are in right now. |
|
145 // Unlike Notify(), does *not* take into account future notifications. |
|
146 // This is only useful if you do not have an imgRequest, e.g., if you are a |
|
147 // static request returned from imgIRequest::GetStaticRequest(). |
|
148 // Should be called on the main thread only, since imgRequestProxy and GetURI |
|
149 // are not threadsafe. |
|
150 void NotifyCurrentState(imgRequestProxy* proxy); |
|
151 |
|
152 // "Replay" all of the notifications that would have to happen to put us in |
|
153 // the state we're currently in. |
|
154 // Only use this if you're already servicing an asynchronous call (e.g. |
|
155 // OnStartRequest). |
|
156 // Should be called on the main thread only, since imgRequestProxy and GetURI |
|
157 // are not threadsafe. |
|
158 void SyncNotify(imgRequestProxy* proxy); |
|
159 |
|
160 // Send some notifications that would be necessary to make |proxy| believe |
|
161 // the request is finished downloading and decoding. We only send |
|
162 // OnStopRequest and UnblockOnload, and only if necessary. |
|
163 void EmulateRequestFinished(imgRequestProxy* proxy, nsresult aStatus); |
|
164 |
|
165 // We manage a set of consumers that are using an image and thus concerned |
|
166 // with its status. Weak pointers. |
|
167 void AddConsumer(imgRequestProxy* aConsumer); |
|
168 bool RemoveConsumer(imgRequestProxy* aConsumer, nsresult aStatus); |
|
169 size_t ConsumerCount() const { |
|
170 MOZ_ASSERT(NS_IsMainThread(), "Use mConsumers on main thread only"); |
|
171 return mConsumers.Length(); |
|
172 } |
|
173 |
|
174 // This is intentionally non-general because its sole purpose is to support an |
|
175 // some obscure network priority logic in imgRequest. That stuff could probably |
|
176 // be improved, but it's too scary to mess with at the moment. |
|
177 bool FirstConsumerIs(imgRequestProxy* aConsumer); |
|
178 |
|
179 void AdoptConsumers(imgStatusTracker* aTracker) { |
|
180 MOZ_ASSERT(NS_IsMainThread(), "Use mConsumers on main thread only"); |
|
181 MOZ_ASSERT(aTracker); |
|
182 mConsumers = aTracker->mConsumers; |
|
183 } |
|
184 |
|
185 // Returns whether we are in the process of loading; that is, whether we have |
|
186 // not received OnStopRequest. |
|
187 bool IsLoading() const; |
|
188 |
|
189 // Get the current image status (as in imgIRequest). |
|
190 uint32_t GetImageStatus() const; |
|
191 |
|
192 // Following are all the notification methods. You must call the Record |
|
193 // variant on this status tracker, then call the Send variant for each proxy |
|
194 // you want to notify. |
|
195 |
|
196 // Call when the request is being cancelled. |
|
197 void RecordCancel(); |
|
198 |
|
199 // Shorthand for recording all the load notifications: StartRequest, |
|
200 // StartContainer, StopRequest. |
|
201 void RecordLoaded(); |
|
202 |
|
203 // Shorthand for recording all the decode notifications: StartDecode, |
|
204 // StartFrame, DataAvailable, StopFrame, StopDecode. |
|
205 void RecordDecoded(); |
|
206 |
|
207 /* non-virtual imgDecoderObserver methods */ |
|
208 // Functions with prefix Send- are main thread only, since they contain calls |
|
209 // to imgRequestProxy functions, which are expected on the main thread. |
|
210 void RecordStartDecode(); |
|
211 void SendStartDecode(imgRequestProxy* aProxy); |
|
212 void RecordStartContainer(imgIContainer* aContainer); |
|
213 void SendStartContainer(imgRequestProxy* aProxy); |
|
214 void RecordStartFrame(); |
|
215 // No SendStartFrame since it's not observed below us. |
|
216 void RecordFrameChanged(const nsIntRect* aDirtyRect); |
|
217 void SendFrameChanged(imgRequestProxy* aProxy, const nsIntRect* aDirtyRect); |
|
218 void RecordStopFrame(); |
|
219 void SendStopFrame(imgRequestProxy* aProxy); |
|
220 void RecordStopDecode(nsresult statusg); |
|
221 void SendStopDecode(imgRequestProxy* aProxy, nsresult aStatus); |
|
222 void RecordDiscard(); |
|
223 void SendDiscard(imgRequestProxy* aProxy); |
|
224 void RecordUnlockedDraw(); |
|
225 void SendUnlockedDraw(imgRequestProxy* aProxy); |
|
226 void RecordImageIsAnimated(); |
|
227 void SendImageIsAnimated(imgRequestProxy *aProxy); |
|
228 |
|
229 /* non-virtual sort-of-nsIRequestObserver methods */ |
|
230 // Functions with prefix Send- are main thread only, since they contain calls |
|
231 // to imgRequestProxy functions, which are expected on the main thread. |
|
232 void RecordStartRequest(); |
|
233 void SendStartRequest(imgRequestProxy* aProxy); |
|
234 void RecordStopRequest(bool aLastPart, nsresult aStatus); |
|
235 void SendStopRequest(imgRequestProxy* aProxy, bool aLastPart, nsresult aStatus); |
|
236 |
|
237 // All main thread only because they call functions (like SendStartRequest) |
|
238 // which are expected to be called on the main thread. |
|
239 void OnStartRequest(); |
|
240 // OnDataAvailable will dispatch a call to itself onto the main thread if not |
|
241 // called there. |
|
242 void OnDataAvailable(); |
|
243 void OnStopRequest(bool aLastPart, nsresult aStatus); |
|
244 void OnDiscard(); |
|
245 void FrameChanged(const nsIntRect* aDirtyRect); |
|
246 void OnUnlockedDraw(); |
|
247 // This is called only by VectorImage, and only to ensure tests work |
|
248 // properly. Do not use it. |
|
249 void OnStopFrame(); |
|
250 |
|
251 /* non-virtual imgIOnloadBlocker methods */ |
|
252 // NB: If UnblockOnload is sent, and then we are asked to replay the |
|
253 // notifications, we will not send a BlockOnload/UnblockOnload pair. This |
|
254 // is different from all the other notifications. |
|
255 void RecordBlockOnload(); |
|
256 void SendBlockOnload(imgRequestProxy* aProxy); |
|
257 void RecordUnblockOnload(); |
|
258 void SendUnblockOnload(imgRequestProxy* aProxy); |
|
259 |
|
260 // Main thread only because mConsumers is not threadsafe. |
|
261 void MaybeUnblockOnload(); |
|
262 |
|
263 void RecordError(); |
|
264 |
|
265 bool IsMultipart() const { return mIsMultipart; } |
|
266 |
|
267 // Weak pointer getters - no AddRefs. |
|
268 inline already_AddRefed<mozilla::image::Image> GetImage() const { |
|
269 nsRefPtr<mozilla::image::Image> image = mImage; |
|
270 return image.forget(); |
|
271 } |
|
272 inline bool HasImage() { return mImage; } |
|
273 |
|
274 inline imgDecoderObserver* GetDecoderObserver() { return mTrackerObserver.get(); } |
|
275 |
|
276 already_AddRefed<imgStatusTracker> CloneForRecording(); |
|
277 |
|
278 // Compute the difference between this status tracker and aOther. |
|
279 mozilla::image::ImageStatusDiff Difference(imgStatusTracker* aOther) const; |
|
280 |
|
281 // Captures all of the decode notifications (i.e., not OnStartRequest / |
|
282 // OnStopRequest) so far as an ImageStatusDiff. |
|
283 mozilla::image::ImageStatusDiff DecodeStateAsDifference() const; |
|
284 |
|
285 // Update our state to incorporate the changes in aDiff. |
|
286 void ApplyDifference(const mozilla::image::ImageStatusDiff& aDiff); |
|
287 |
|
288 // Notify for the changes captured in an ImageStatusDiff. Because this may |
|
289 // result in recursive notifications, no decoding locks may be held. |
|
290 // Called on the main thread only. |
|
291 void SyncNotifyDifference(const mozilla::image::ImageStatusDiff& aDiff); |
|
292 |
|
293 nsIntRect GetInvalidRect() const { return mInvalidRect; } |
|
294 |
|
295 private: |
|
296 typedef nsTObserverArray<mozilla::WeakPtr<imgRequestProxy>> ProxyArray; |
|
297 friend class imgStatusNotifyRunnable; |
|
298 friend class imgRequestNotifyRunnable; |
|
299 friend class imgStatusTrackerObserver; |
|
300 friend class imgStatusTrackerInit; |
|
301 imgStatusTracker(const imgStatusTracker& aOther); |
|
302 |
|
303 // Main thread only because it deals with the observer service. |
|
304 void FireFailureNotification(); |
|
305 |
|
306 // Main thread only, since imgRequestProxy calls are expected on the main |
|
307 // thread, and mConsumers is not threadsafe. |
|
308 static void SyncNotifyState(ProxyArray& proxies, |
|
309 bool hasImage, uint32_t state, |
|
310 nsIntRect& dirtyRect, bool hadLastPart); |
|
311 |
|
312 nsCOMPtr<nsIRunnable> mRequestRunnable; |
|
313 |
|
314 // The invalid area of the most recent frame we know about. (All previous |
|
315 // frames are assumed to be fully valid.) |
|
316 nsIntRect mInvalidRect; |
|
317 |
|
318 // This weak ref should be set null when the image goes out of scope. |
|
319 mozilla::image::Image* mImage; |
|
320 |
|
321 // List of proxies attached to the image. Each proxy represents a consumer |
|
322 // using the image. Array and/or individual elements should only be accessed |
|
323 // on the main thread. |
|
324 ProxyArray mConsumers; |
|
325 |
|
326 mozilla::RefPtr<imgDecoderObserver> mTrackerObserver; |
|
327 |
|
328 uint32_t mState; |
|
329 uint32_t mImageStatus; |
|
330 bool mIsMultipart : 1; |
|
331 bool mHadLastPart : 1; |
|
332 bool mHasBeenDecoded : 1; |
|
333 }; |
|
334 |
|
335 class imgStatusTrackerInit |
|
336 { |
|
337 public: |
|
338 imgStatusTrackerInit(mozilla::image::Image* aImage, |
|
339 imgStatusTracker* aTracker); |
|
340 ~imgStatusTrackerInit(); |
|
341 private: |
|
342 imgStatusTracker* mTracker; |
|
343 }; |
|
344 |
|
345 #endif |