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