Blame image/ProgressTracker.h

Packit f0b94e
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
Packit f0b94e
 *
Packit f0b94e
 * This Source Code Form is subject to the terms of the Mozilla Public
Packit f0b94e
 * License, v. 2.0. If a copy of the MPL was not distributed with this
Packit f0b94e
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
Packit f0b94e
Packit f0b94e
#ifndef mozilla_image_ProgressTracker_h
Packit f0b94e
#define mozilla_image_ProgressTracker_h
Packit f0b94e
Packit f0b94e
#include "CopyOnWrite.h"
Packit f0b94e
#include "mozilla/NotNull.h"
Packit f0b94e
#include "mozilla/Mutex.h"
Packit f0b94e
#include "mozilla/RefPtr.h"
Packit f0b94e
#include "mozilla/WeakPtr.h"
Packit f0b94e
#include "nsDataHashtable.h"
Packit f0b94e
#include "nsCOMPtr.h"
Packit f0b94e
#include "nsTObserverArray.h"
Packit f0b94e
#include "nsThreadUtils.h"
Packit f0b94e
#include "nsRect.h"
Packit f0b94e
#include "IProgressObserver.h"
Packit f0b94e
Packit f0b94e
class nsIRunnable;
Packit f0b94e
Packit f0b94e
namespace mozilla {
Packit f0b94e
namespace image {
Packit f0b94e
Packit f0b94e
class AsyncNotifyRunnable;
Packit f0b94e
class AsyncNotifyCurrentStateRunnable;
Packit f0b94e
class Image;
Packit f0b94e
Packit f0b94e
/**
Packit f0b94e
 * Image progress bitflags.
Packit f0b94e
 *
Packit f0b94e
 * See CheckProgressConsistency() for the invariants we enforce about the
Packit f0b94e
 * ordering dependencies betweeen these flags.
Packit f0b94e
 */
Packit f0b94e
enum {
Packit f0b94e
  FLAG_SIZE_AVAILABLE = 1u << 0,    // STATUS_SIZE_AVAILABLE
Packit f0b94e
  FLAG_DECODE_COMPLETE = 1u << 1,   // STATUS_DECODE_COMPLETE
Packit f0b94e
  FLAG_FRAME_COMPLETE = 1u << 2,    // STATUS_FRAME_COMPLETE
Packit f0b94e
  FLAG_LOAD_COMPLETE = 1u << 3,     // STATUS_LOAD_COMPLETE
Packit f0b94e
  FLAG_IS_ANIMATED = 1u << 6,       // STATUS_IS_ANIMATED
Packit f0b94e
  FLAG_HAS_TRANSPARENCY = 1u << 7,  // STATUS_HAS_TRANSPARENCY
Packit f0b94e
  FLAG_LAST_PART_COMPLETE = 1u << 8,
Packit f0b94e
  FLAG_HAS_ERROR = 1u << 9  // STATUS_ERROR
Packit f0b94e
};
Packit f0b94e
Packit f0b94e
typedef uint32_t Progress;
Packit f0b94e
Packit f0b94e
const uint32_t NoProgress = 0;
Packit f0b94e
Packit f0b94e
inline Progress LoadCompleteProgress(bool aLastPart, bool aError,
Packit f0b94e
                                     nsresult aStatus) {
Packit f0b94e
  Progress progress = FLAG_LOAD_COMPLETE;
Packit f0b94e
  if (aLastPart) {
Packit f0b94e
    progress |= FLAG_LAST_PART_COMPLETE;
Packit f0b94e
  }
Packit f0b94e
  if (NS_FAILED(aStatus) || aError) {
Packit f0b94e
    progress |= FLAG_HAS_ERROR;
Packit f0b94e
  }
Packit f0b94e
  return progress;
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
/**
Packit f0b94e
 * ProgressTracker stores its observers in an ObserverTable, which is a hash
Packit f0b94e
 * table mapping raw pointers to WeakPtr's to the same objects. This sounds like
Packit f0b94e
 * unnecessary duplication of information, but it's necessary for stable hash
Packit f0b94e
 * values since WeakPtr's lose the knowledge of which object they used to point
Packit f0b94e
 * to when that object is destroyed.
Packit f0b94e
 *
Packit f0b94e
 * ObserverTable subclasses nsDataHashtable to add reference counting support
Packit f0b94e
 * and a copy constructor, both of which are needed for use with CopyOnWrite<T>.
Packit f0b94e
 */
Packit f0b94e
class ObserverTable : public nsDataHashtable<nsPtrHashKey<IProgressObserver>,
Packit f0b94e
                                             WeakPtr<IProgressObserver>> {
Packit f0b94e
 public:
Packit f0b94e
  NS_INLINE_DECL_REFCOUNTING(ObserverTable);
Packit f0b94e
Packit f0b94e
  ObserverTable() = default;
Packit f0b94e
Packit f0b94e
  ObserverTable(const ObserverTable& aOther) {
Packit f0b94e
    NS_WARNING("Forced to copy ObserverTable due to nested notifications");
Packit f0b94e
    for (auto iter = aOther.ConstIter(); !iter.Done(); iter.Next()) {
Packit f0b94e
      this->Put(iter.Key(), iter.Data());
Packit f0b94e
    }
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
 private:
Packit f0b94e
  ~ObserverTable() {}
Packit f0b94e
};
Packit f0b94e
Packit f0b94e
/**
Packit f0b94e
 * ProgressTracker is a class that records an Image's progress through the
Packit f0b94e
 * loading and decoding process, and makes it possible to send notifications to
Packit f0b94e
 * IProgressObservers, both synchronously and asynchronously.
Packit f0b94e
 *
Packit f0b94e
 * When a new observer needs to be notified of the current progress of an image,
Packit f0b94e
 * call the Notify() method on this class with the relevant observer as its
Packit f0b94e
 * argument, and the notifications will be replayed to the observer
Packit f0b94e
 * asynchronously.
Packit f0b94e
 */
Packit f0b94e
class ProgressTracker : public mozilla::SupportsWeakPtr<ProgressTracker> {
Packit f0b94e
  virtual ~ProgressTracker() {}
Packit f0b94e
Packit f0b94e
 public:
Packit f0b94e
  MOZ_DECLARE_WEAKREFERENCE_TYPENAME(ProgressTracker)
Packit f0b94e
  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ProgressTracker)
Packit f0b94e
Packit f0b94e
  ProgressTracker();
Packit f0b94e
Packit f0b94e
  bool HasImage() const {
Packit f0b94e
    MutexAutoLock lock(mMutex);
Packit f0b94e
    return mImage;
Packit f0b94e
  }
Packit f0b94e
  already_AddRefed<Image> GetImage() const {
Packit f0b94e
    MutexAutoLock lock(mMutex);
Packit f0b94e
    RefPtr<Image> image = mImage;
Packit f0b94e
    return image.forget();
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  // Get the current image status (as in imgIRequest).
Packit f0b94e
  uint32_t GetImageStatus() const;
Packit f0b94e
Packit f0b94e
  // Get the current Progress.
Packit f0b94e
  Progress GetProgress() const { return mProgress; }
Packit f0b94e
Packit f0b94e
  // Schedule an asynchronous "replaying" of all the notifications that would
Packit f0b94e
  // have to happen to put us in the current state.
Packit f0b94e
  // We will also take note of any notifications that happen between the time
Packit f0b94e
  // Notify() is called and when we call SyncNotify on |aObserver|, and replay
Packit f0b94e
  // them as well.
Packit f0b94e
  // Should be called on the main thread only, since observers and GetURI are
Packit f0b94e
  // not threadsafe.
Packit f0b94e
  void Notify(IProgressObserver* aObserver);
Packit f0b94e
Packit f0b94e
  // Schedule an asynchronous "replaying" of all the notifications that would
Packit f0b94e
  // have to happen to put us in the state we are in right now.
Packit f0b94e
  // Unlike Notify(), does *not* take into account future notifications.
Packit f0b94e
  // This is only useful if you do not have an imgRequest, e.g., if you are a
Packit f0b94e
  // static request returned from imgIRequest::GetStaticRequest().
Packit f0b94e
  // Should be called on the main thread only, since observers and GetURI are
Packit f0b94e
  // not threadsafe.
Packit f0b94e
  void NotifyCurrentState(IProgressObserver* aObserver);
Packit f0b94e
Packit f0b94e
  // "Replay" all of the notifications that would have to happen to put us in
Packit f0b94e
  // the state we're currently in.
Packit f0b94e
  // Only use this if you're already servicing an asynchronous call (e.g.
Packit f0b94e
  // OnStartRequest).
Packit f0b94e
  // Should be called on the main thread only, since observers and GetURI are
Packit f0b94e
  // not threadsafe.
Packit f0b94e
  void SyncNotify(IProgressObserver* aObserver);
Packit f0b94e
Packit f0b94e
  // Get this ProgressTracker ready for a new request. This resets all the
Packit f0b94e
  // state that doesn't persist between requests.
Packit f0b94e
  void ResetForNewRequest();
Packit f0b94e
Packit f0b94e
  // Stateless notifications. These are dispatched and immediately forgotten
Packit f0b94e
  // about. All of these notifications are main thread only.
Packit f0b94e
  void OnDiscard();
Packit f0b94e
  void OnUnlockedDraw();
Packit f0b94e
  void OnImageAvailable();
Packit f0b94e
Packit f0b94e
  // Compute the difference between this our progress and aProgress. This allows
Packit f0b94e
  // callers to predict whether SyncNotifyProgress will send any notifications.
Packit f0b94e
  Progress Difference(Progress aProgress) const {
Packit f0b94e
    return ~mProgress & aProgress;
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  // Update our state to incorporate the changes in aProgress and synchronously
Packit f0b94e
  // notify our observers.
Packit f0b94e
  //
Packit f0b94e
  // Because this may result in recursive notifications, no decoding locks may
Packit f0b94e
  // be held.  Called on the main thread only.
Packit f0b94e
  void SyncNotifyProgress(Progress aProgress,
Packit f0b94e
                          const nsIntRect& aInvalidRect = nsIntRect());
Packit f0b94e
Packit f0b94e
  // We manage a set of observers that are using an image and thus concerned
Packit f0b94e
  // with its loading progress. Weak pointers.
Packit f0b94e
  void AddObserver(IProgressObserver* aObserver);
Packit f0b94e
  bool RemoveObserver(IProgressObserver* aObserver);
Packit f0b94e
  uint32_t ObserverCount() const;
Packit f0b94e
Packit f0b94e
  // Get the event target we should currently dispatch events to.
Packit f0b94e
  already_AddRefed<nsIEventTarget> GetEventTarget() const;
Packit f0b94e
Packit f0b94e
  // Resets our weak reference to our image. Image subclasses should call this
Packit f0b94e
  // in their destructor.
Packit f0b94e
  void ResetImage();
Packit f0b94e
Packit f0b94e
  // Tell this progress tracker that it is for a multipart image.
Packit f0b94e
  void SetIsMultipart() { mIsMultipart = true; }
Packit f0b94e
Packit f0b94e
 private:
Packit f0b94e
  friend class AsyncNotifyRunnable;
Packit f0b94e
  friend class AsyncNotifyCurrentStateRunnable;
Packit f0b94e
  friend class ImageFactory;
Packit f0b94e
Packit f0b94e
  ProgressTracker(const ProgressTracker& aOther) = delete;
Packit f0b94e
Packit f0b94e
  // Sets our weak reference to our image. Only ImageFactory should call this.
Packit f0b94e
  void SetImage(Image* aImage);
Packit f0b94e
Packit f0b94e
  // Send some notifications that would be necessary to make |aObserver| believe
Packit f0b94e
  // the request is finished downloading and decoding.  We only send
Packit f0b94e
  // FLAG_LOAD_COMPLETE and FLAG_ONLOAD_UNBLOCKED, and only if necessary.
Packit f0b94e
  void EmulateRequestFinished(IProgressObserver* aObserver);
Packit f0b94e
Packit f0b94e
  // Main thread only because it deals with the observer service.
Packit f0b94e
  void FireFailureNotification();
Packit f0b94e
Packit f0b94e
  // The runnable, if any, that we've scheduled to deliver async notifications.
Packit f0b94e
  nsCOMPtr<nsIRunnable> mRunnable;
Packit f0b94e
Packit f0b94e
  // mMutex protects access to mImage and mEventTarget.
Packit f0b94e
  mutable Mutex mMutex;
Packit f0b94e
Packit f0b94e
  // mImage is a weak ref; it should be set to null when the image goes out of
Packit f0b94e
  // scope.
Packit f0b94e
  Image* mImage;
Packit f0b94e
Packit f0b94e
  // mEventTarget is the current, best effort event target to dispatch
Packit f0b94e
  // notifications to from the decoder threads. It will change as observers are
Packit f0b94e
  // added and removed (see mObserversWithTargets).
Packit f0b94e
  NotNull<nsCOMPtr<nsIEventTarget>> mEventTarget;
Packit f0b94e
Packit f0b94e
  // How many observers have been added that have an explicit event target.
Packit f0b94e
  // When the first observer is added with an explicit event target, we will
Packit f0b94e
  // default to that as long as all observers use the same target. If a new
Packit f0b94e
  // observer is added which has a different event target, we will switch to
Packit f0b94e
  // using the unlabeled main thread event target which is safe for all
Packit f0b94e
  // observers. If all observers with explicit event targets are removed, we
Packit f0b94e
  // will revert back to the initial event target (for SystemGroup). An
Packit f0b94e
  // observer without an explicit event target does not care what context it
Packit f0b94e
  // is dispatched in, and thus does not impact the state.
Packit f0b94e
  uint32_t mObserversWithTargets;
Packit f0b94e
Packit f0b94e
  // Hashtable of observers attached to the image. Each observer represents a
Packit f0b94e
  // consumer using the image. Main thread only.
Packit f0b94e
  CopyOnWrite<ObserverTable> mObservers;
Packit f0b94e
Packit f0b94e
  Progress mProgress;
Packit f0b94e
Packit f0b94e
  // Whether this is a progress tracker for a multipart image.
Packit f0b94e
  bool mIsMultipart;
Packit f0b94e
};
Packit f0b94e
Packit f0b94e
}  // namespace image
Packit f0b94e
}  // namespace mozilla
Packit f0b94e
Packit f0b94e
#endif  // mozilla_image_ProgressTracker_h