Coverage Report

Created: 2018-09-25 14:53

/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