ProgressTracker.h 7.9 KB

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