tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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