image/src/imgStatusTracker.h

branch
TOR_BUG_9701
changeset 15
b8a032363ba2
equal deleted inserted replaced
-1:000000000000 0:19c868596cec
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

mercurial