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 "nsTHashMap.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 between 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 
LoadCompleteProgress(bool aLastPart,bool aError,nsresult aStatus)52 inline Progress LoadCompleteProgress(bool aLastPart, bool aError,
53                                      nsresult aStatus) {
54   Progress progress = FLAG_LOAD_COMPLETE;
55   if (aLastPart) {
56     progress |= FLAG_LAST_PART_COMPLETE;
57   }
58   if (NS_FAILED(aStatus) || aError) {
59     progress |= FLAG_HAS_ERROR;
60   }
61   return progress;
62 }
63 
64 /**
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.
70  *
71  * ObserverTable subclasses nsTHashMap to add reference counting
72  * support and a copy constructor, both of which are needed for use with
73  * CopyOnWrite<T>.
74  */
75 class ObserverTable : public nsTHashMap<nsPtrHashKey<IProgressObserver>,
76                                         WeakPtr<IProgressObserver>> {
77  public:
78   NS_INLINE_DECL_REFCOUNTING(ObserverTable);
79 
80   ObserverTable() = default;
81 
ObserverTable(const ObserverTable & aOther)82   ObserverTable(const ObserverTable& aOther)
83       : nsTHashMap<nsPtrHashKey<IProgressObserver>, WeakPtr<IProgressObserver>>(
84             aOther.Clone()) {
85     NS_WARNING("Forced to copy ObserverTable due to nested notifications");
86   }
87 
88  private:
~ObserverTable()89   ~ObserverTable() {}
90 };
91 
92 /**
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.
96  *
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
100  * asynchronously.
101  */
102 class ProgressTracker : public mozilla::SupportsWeakPtr {
~ProgressTracker()103   virtual ~ProgressTracker() {}
104 
105  public:
106   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ProgressTracker)
107 
108   ProgressTracker();
109 
HasImage()110   bool HasImage() const {
111     MutexAutoLock lock(mMutex);
112     return mImage;
113   }
GetImage()114   already_AddRefed<Image> GetImage() const {
115     MutexAutoLock lock(mMutex);
116     RefPtr<Image> image = mImage;
117     return image.forget();
118   }
119 
120   // Get the current image status (as in imgIRequest).
121   uint32_t GetImageStatus() const;
122 
123   // Get the current Progress.
GetProgress()124   Progress GetProgress() const { return mProgress; }
125 
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
130   // them as well.
131   // Should be called on the main thread only, since observers and GetURI are
132   // not threadsafe.
133   void Notify(IProgressObserver* aObserver);
134 
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
141   // not threadsafe.
142   void NotifyCurrentState(IProgressObserver* aObserver);
143 
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.
147   // OnStartRequest).
148   // Should be called on the main thread only, since observers and GetURI are
149   // not threadsafe.
150   void SyncNotify(IProgressObserver* aObserver);
151 
152   // Get this ProgressTracker ready for a new request. This resets all the
153   // state that doesn't persist between requests.
154   void ResetForNewRequest();
155 
156   // Stateless notifications. These are dispatched and immediately forgotten
157   // about. All of these notifications are main thread only.
158   void OnDiscard();
159   void OnUnlockedDraw();
160   void OnImageAvailable();
161 
162   // Compute the difference between this our progress and aProgress. This allows
163   // callers to predict whether SyncNotifyProgress will send any notifications.
Difference(Progress aProgress)164   Progress Difference(Progress aProgress) const {
165     return ~mProgress & aProgress;
166   }
167 
168   // Update our state to incorporate the changes in aProgress and synchronously
169   // notify our observers.
170   //
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());
175 
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;
181 
182   // Get the event target we should currently dispatch events to.
183   already_AddRefed<nsIEventTarget> GetEventTarget() const;
184 
185   // Resets our weak reference to our image. Image subclasses should call this
186   // in their destructor.
187   void ResetImage();
188 
189   // Tell this progress tracker that it is for a multipart image.
SetIsMultipart()190   void SetIsMultipart() { mIsMultipart = true; }
191 
192  private:
193   friend class AsyncNotifyRunnable;
194   friend class AsyncNotifyCurrentStateRunnable;
195   friend class ImageFactory;
196 
197   ProgressTracker(const ProgressTracker& aOther) = delete;
198 
199   // Sets our weak reference to our image. Only ImageFactory should call this.
200   void SetImage(Image* aImage);
201 
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);
206 
207   // Main thread only because it deals with the observer service.
208   void FireFailureNotification();
209 
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;
215 
216    public:
217     void AddObserver(IProgressObserver* aObserver);
218     void RemoveObserver(IProgressObserver* aObserver);
219 
220     static already_AddRefed<MediumHighRunnable> Create(
221         already_AddRefed<AsyncNotifyRunnable>&& aEvent);
222   };
223 
224   // The runnable, if any, that we've scheduled to deliver async notifications.
225   RefPtr<MediumHighRunnable> mRunnable;
226 
227   // mMutex protects access to mImage and mEventTarget.
228   mutable Mutex mMutex;
229 
230   // mImage is a weak ref; it should be set to null when the image goes out of
231   // scope.
232   Image* mImage;
233 
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;
238 
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;
249 
250   // Hashtable of observers attached to the image. Each observer represents a
251   // consumer using the image. Main thread only.
252   CopyOnWrite<ObserverTable> mObservers;
253 
254   Progress mProgress;
255 
256   // Whether this is a progress tracker for a multipart image.
257   bool mIsMultipart;
258 };
259 
260 }  // namespace image
261 }  // namespace mozilla
262 
263 #endif  // mozilla_image_ProgressTracker_h
264