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