image/ProgressTracker.h
author Nathan Froyd <froydnj@mozilla.com>
Wed, 07 Oct 2015 15:05:02 -0400
changeset 301510 1f51d1614b9a58e41dd3a1bf2dc6aad34dbb2170
parent 293647 4797d5683bf366faca78d415e7c7d1af83e3c0cc
child 301514 91d4539e00cecb658604e021675a923c60ef3235
permissions -rw-r--r--
Bug 1207245 - part 3 - switch all uses of mozilla::RefPtr<T> to nsRefPtr<T>; r=ehsan This commit was generated using the following script, executed at the top level of a typical source code checkout. # Don't modify select files in mfbt/ because it's not worth trying to # tease out the dependencies currently. # # Don't modify anything in media/gmp-clearkey/0.1/ because those files # use their own RefPtr, defined in their own RefCounted.h. find . -name '*.cpp' -o -name '*.h' -o -name '*.mm' -o -name '*.idl'| \ grep -v 'mfbt/RefPtr.h' | \ grep -v 'mfbt/nsRefPtr.h' | \ grep -v 'mfbt/RefCounted.h' | \ grep -v 'media/gmp-clearkey/0.1/' | \ xargs perl -p -i -e ' s/mozilla::RefPtr/nsRefPtr/g; # handle declarations in headers s/\bRefPtr</nsRefPtr</g; # handle local variables in functions s#mozilla/RefPtr.h#mozilla/nsRefPtr.h#; # handle #includes s#mfbt/RefPtr.h#mfbt/nsRefPtr.h#; # handle strange #includes ' # |using mozilla::RefPtr;| is OK; |using nsRefPtr;| is invalid syntax. find . -name '*.cpp' -o -name '*.mm' | xargs sed -i -e '/using nsRefPtr/d' # RefPtr.h used |byRef| for dealing with COM-style outparams. # nsRefPtr.h uses |getter_AddRefs|. # Fixup that mismatch. find . -name '*.cpp' | \ xargs perl -p -i -e 's/byRef/getter_AddRefs/g'

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef mozilla_image_ProgressTracker_h
#define mozilla_image_ProgressTracker_h

#include "CopyOnWrite.h"
#include "mozilla/Mutex.h"
#include "mozilla/nsRefPtr.h"
#include "mozilla/WeakPtr.h"
#include "nsDataHashtable.h"
#include "nsCOMPtr.h"
#include "nsTObserverArray.h"
#include "nsThreadUtils.h"
#include "nsRect.h"
#include "IProgressObserver.h"

class nsIRunnable;

namespace mozilla {
namespace image {

class AsyncNotifyRunnable;
class AsyncNotifyCurrentStateRunnable;
class Image;

// Image progress bitflags.
enum {
  FLAG_SIZE_AVAILABLE     = 1u << 0,  // STATUS_SIZE_AVAILABLE
  FLAG_DECODE_COMPLETE    = 1u << 1,  // STATUS_DECODE_COMPLETE
  FLAG_FRAME_COMPLETE     = 1u << 2,  // STATUS_FRAME_COMPLETE
  FLAG_LOAD_COMPLETE      = 1u << 3,  // STATUS_LOAD_COMPLETE
  FLAG_ONLOAD_BLOCKED     = 1u << 4,
  FLAG_ONLOAD_UNBLOCKED   = 1u << 5,
  FLAG_IS_ANIMATED        = 1u << 6,  // STATUS_IS_ANIMATED
  FLAG_HAS_TRANSPARENCY   = 1u << 7,  // STATUS_HAS_TRANSPARENCY
  FLAG_LAST_PART_COMPLETE = 1u << 8,
  FLAG_HAS_ERROR          = 1u << 9   // STATUS_ERROR
};

typedef uint32_t Progress;

const uint32_t NoProgress = 0;

inline Progress LoadCompleteProgress(bool aLastPart,
                                     bool aError,
                                     nsresult aStatus)
{
  Progress progress = FLAG_LOAD_COMPLETE;
  if (aLastPart) {
    progress |= FLAG_LAST_PART_COMPLETE;
  }
  if (NS_FAILED(aStatus) || aError) {
    progress |= FLAG_HAS_ERROR;
  }
  return progress;
}

/**
 * ProgressTracker stores its observers in an ObserverTable, which is a hash
 * table mapping raw pointers to WeakPtr's to the same objects. This sounds like
 * unnecessary duplication of information, but it's necessary for stable hash
 * values since WeakPtr's lose the knowledge of which object they used to point
 * to when that object is destroyed.
 *
 * ObserverTable subclasses nsDataHashtable to add reference counting support
 * and a copy constructor, both of which are needed for use with CopyOnWrite<T>.
 */
class ObserverTable
  : public nsDataHashtable<nsPtrHashKey<IProgressObserver>,
                           WeakPtr<IProgressObserver>>
{
public:
  NS_INLINE_DECL_REFCOUNTING(ObserverTable);

  ObserverTable() = default;

  ObserverTable(const ObserverTable& aOther)
  {
    NS_WARNING("Forced to copy ObserverTable due to nested notifications");
    for (auto iter = aOther.ConstIter(); !iter.Done(); iter.Next()) {
      this->Put(iter.Key(), iter.Data());
    }
  }

private:
  ~ObserverTable() { }
};

/**
 * ProgressTracker is a class that records an Image's progress through the
 * loading and decoding process, and makes it possible to send notifications to
 * IProgressObservers, both synchronously and asynchronously.
 *
 * When a new observer needs to be notified of the current progress of an image,
 * call the Notify() method on this class with the relevant observer as its
 * argument, and the notifications will be replayed to the observer
 * asynchronously.
 */
class ProgressTracker : public mozilla::SupportsWeakPtr<ProgressTracker>
{
  virtual ~ProgressTracker() { }

public:
  MOZ_DECLARE_WEAKREFERENCE_TYPENAME(ProgressTracker)
  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ProgressTracker)

  ProgressTracker()
    : mImageMutex("ProgressTracker::mImage")
    , mImage(nullptr)
    , mObservers(new ObserverTable)
    , mProgress(NoProgress)
  { }

  bool HasImage() const { MutexAutoLock lock(mImageMutex); return mImage; }
  already_AddRefed<Image> GetImage() const
  {
    MutexAutoLock lock(mImageMutex);
    nsRefPtr<Image> image = mImage;
    return image.forget();
  }

  // Get the current image status (as in imgIRequest).
  uint32_t GetImageStatus() const;

  // Get the current Progress.
  Progress GetProgress() const { return mProgress; }

  // Schedule an asynchronous "replaying" of all the notifications that would
  // have to happen to put us in the current state.
  // We will also take note of any notifications that happen between the time
  // Notify() is called and when we call SyncNotify on |aObserver|, and replay
  // them as well.
  // Should be called on the main thread only, since observers and GetURI are
  // not threadsafe.
  void Notify(IProgressObserver* aObserver);

  // Schedule an asynchronous "replaying" of all the notifications that would
  // have to happen to put us in the state we are in right now.
  // Unlike Notify(), does *not* take into account future notifications.
  // This is only useful if you do not have an imgRequest, e.g., if you are a
  // static request returned from imgIRequest::GetStaticRequest().
  // Should be called on the main thread only, since observers and GetURI are
  // not threadsafe.
  void NotifyCurrentState(IProgressObserver* aObserver);

  // "Replay" all of the notifications that would have to happen to put us in
  // the state we're currently in.
  // Only use this if you're already servicing an asynchronous call (e.g.
  // OnStartRequest).
  // Should be called on the main thread only, since observers and GetURI are
  // not threadsafe.
  void SyncNotify(IProgressObserver* aObserver);

  // Get this ProgressTracker ready for a new request. This resets all the
  // state that doesn't persist between requests.
  void ResetForNewRequest();

  // Stateless notifications. These are dispatched and immediately forgotten
  // about. All of these notifications are main thread only.
  void OnDiscard();
  void OnUnlockedDraw();
  void OnImageAvailable();

  // Compute the difference between this our progress and aProgress. This allows
  // callers to predict whether SyncNotifyProgress will send any notifications.
  Progress Difference(Progress aProgress) const
  {
    return ~mProgress & aProgress;
  }

  // Update our state to incorporate the changes in aProgress and synchronously
  // notify our observers.
  //
  // Because this may result in recursive notifications, no decoding locks may
  // be held.  Called on the main thread only.
  void SyncNotifyProgress(Progress aProgress,
                          const nsIntRect& aInvalidRect = nsIntRect());

  // We manage a set of observers that are using an image and thus concerned
  // with its loading progress. Weak pointers.
  void AddObserver(IProgressObserver* aObserver);
  bool RemoveObserver(IProgressObserver* aObserver);
  uint32_t ObserverCount() const;

  // Resets our weak reference to our image. Image subclasses should call this
  // in their destructor.
  void ResetImage();

private:
  friend class AsyncNotifyRunnable;
  friend class AsyncNotifyCurrentStateRunnable;
  friend class ImageFactory;

  ProgressTracker(const ProgressTracker& aOther) = delete;

  // Sets our weak reference to our image. Only ImageFactory should call this.
  void SetImage(Image* aImage);

  // Send some notifications that would be necessary to make |aObserver| believe
  // the request is finished downloading and decoding.  We only send
  // FLAG_LOAD_COMPLETE and FLAG_ONLOAD_UNBLOCKED, and only if necessary.
  void EmulateRequestFinished(IProgressObserver* aObserver);

  // Main thread only because it deals with the observer service.
  void FireFailureNotification();

  // The runnable, if any, that we've scheduled to deliver async notifications.
  nsCOMPtr<nsIRunnable> mRunnable;

  // mImage is a weak ref; it should be set to null when the image goes out of
  // scope. mImageMutex protects mImage.
  mutable Mutex mImageMutex;
  Image* mImage;

  // Hashtable of observers attached to the image. Each observer represents a
  // consumer using the image. Main thread only.
  CopyOnWrite<ObserverTable> mObservers;

  Progress mProgress;
};

} // namespace image
} // namespace mozilla

#endif // mozilla_image_ProgressTracker_h