Source code

Revision control

Other Tools

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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 */
#ifndef mozilla_dom_cache_Manager_h
#define mozilla_dom_cache_Manager_h
#include "mozilla/RefPtr.h"
#include "mozilla/dom/SafeRefPtr.h"
#include "mozilla/dom/cache/Types.h"
#include "mozilla/dom/quota/Client.h"
#include "CacheCommon.h"
#include "nsCOMPtr.h"
#include "nsISupportsImpl.h"
#include "nsString.h"
#include "nsTArray.h"
class nsIInputStream;
class nsIThread;
namespace mozilla {
class ErrorResult;
namespace dom {
namespace quota {
class DirectoryLock;
} // namespace quota
namespace cache {
class CacheOpArgs;
class CacheOpResult;
class CacheRequestResponse;
class Context;
class ManagerId;
struct SavedRequest;
struct SavedResponse;
class StreamList;
// The Manager is class is responsible for performing all of the underlying
// work for a Cache or CacheStorage operation. The DOM objects and IPC actors
// are basically just plumbing to get the request to the right Manager object
// running in the parent process.
// There should be exactly one Manager object for each origin or app using the
// Cache API. This uniqueness is defined by the ManagerId equality operator.
// The uniqueness is enforced by the Manager GetOrCreate() factory method.
// The life cycle of Manager objects is somewhat complex. While code may
// hold a strong reference to the Manager, it will invalidate itself once it
// believes it has become completely idle. This is currently determined when
// all of the following conditions occur:
// 1) There are no more Manager::Listener objects registered with the Manager
// by performing a Cache or Storage operation.
// 2) There are no more CacheId references noted via Manager::AddRefCacheId().
// 3) There are no more BodyId references noted via Manager::AddRefBodyId().
// In order to keep your Manager alive you should perform an operation to set
// a Listener, call AddRefCacheId(), or call AddRefBodyId().
// Even once a Manager becomes invalid, however, it may still continue to
// exist. This is allowed so that any in-progress Actions can gracefully
// complete.
// As an invariant, all Manager objects must cease all IO before shutdown. This
// is enforced by the Manager::Factory. If content still holds references to
// Cache DOM objects during shutdown, then all operations will begin rejecting.
class Manager final : public SafeRefCounted<Manager> {
using Client = quota::Client;
using DirectoryLock = quota::DirectoryLock;
// Callback interface implemented by clients of Manager, such as CacheParent
// and CacheStorageParent. In general, if you call a Manager method you
// should expect to receive exactly one On*() callback. For example, if
// you call Manager::CacheMatch(), then you should expect to receive
// OnCacheMatch() back in response.
// Listener objects are set on a per-operation basis. So you pass the
// Listener to a call like Manager::CacheMatch(). Once set in this way,
// the Manager will continue to reference the Listener until RemoveListener()
// is called. This is done to allow the same listener to be used for
// multiple operations simultaneously without having to maintain an exact
// count of operations-in-flight.
// Note, the Manager only holds weak references to Listener objects.
// Listeners must call Manager::RemoveListener() before they are destroyed
// to clear these weak references.
// All public methods should be invoked on the same thread used to create
// the Manager.
class Listener {
// convenience routines
void OnOpComplete(ErrorResult&& aRv, const CacheOpResult& aResult);
void OnOpComplete(ErrorResult&& aRv, const CacheOpResult& aResult,
CacheId aOpenedCacheId);
void OnOpComplete(ErrorResult&& aRv, const CacheOpResult& aResult,
const SavedResponse& aSavedResponse,
StreamList& aStreamList);
void OnOpComplete(ErrorResult&& aRv, const CacheOpResult& aResult,
const nsTArray<SavedResponse>& aSavedResponseList,
StreamList& aStreamList);
void OnOpComplete(ErrorResult&& aRv, const CacheOpResult& aResult,
const nsTArray<SavedRequest>& aSavedRequestList,
StreamList& aStreamList);
struct StreamInfo {
const nsTArray<SavedResponse>& mSavedResponseList;
const nsTArray<SavedRequest>& mSavedRequestList;
StreamList& mStreamList;
// interface to be implemented
virtual void OnOpComplete(ErrorResult&& aRv, const CacheOpResult& aResult,
CacheId aOpenedCacheId,
const Maybe<StreamInfo>& aStreamInfo) {}
~Listener() = default;
enum State { Open, Closing };
static Result<SafeRefPtr<Manager>, nsresult> AcquireCreateIfNonExistent(
const SafeRefPtr<ManagerId>& aManagerId);
static void InitiateShutdown();
static bool IsShutdownAllComplete();
static nsCString GetShutdownStatus();
// Cancel actions for given DirectoryLock ids.
static void Abort(const Client::DirectoryLockIdTable& aDirectoryLockIds);
// Cancel all actions.
static void AbortAll();
// Must be called by Listener objects before they are destroyed.
void RemoveListener(Listener* aListener);
// Must be called by Context objects before they are destroyed.
void RemoveContext(Context& aContext);
// Marks the Manager "invalid". Once the Context completes no new operations
// will be permitted with this Manager. New actors will get a new Manager.
void NoteClosing();
State GetState() const;
// If an actor represents a long term reference to a cache or body stream,
// then they must call AddRefCacheId() or AddRefBodyId(). This will
// cause the Manager to keep the backing data store alive for the given
// object. The actor must then call ReleaseCacheId() or ReleaseBodyId()
// exactly once for every AddRef*() call it made. Any delayed deletion
// will then be performed.
void AddRefCacheId(CacheId aCacheId);
void ReleaseCacheId(CacheId aCacheId);
void AddRefBodyId(const nsID& aBodyId);
void ReleaseBodyId(const nsID& aBodyId);
const ManagerId& GetManagerId() const;
Maybe<DirectoryLock&> MaybeDirectoryLockRef() const;
// Methods to allow a StreamList to register themselves with the Manager.
// StreamList objects must call RemoveStreamList() before they are destroyed.
void AddStreamList(StreamList& aStreamList);
void RemoveStreamList(StreamList& aStreamList);
void ExecuteCacheOp(Listener* aListener, CacheId aCacheId,
const CacheOpArgs& aOpArgs);
void ExecutePutAll(
Listener* aListener, CacheId aCacheId,
const nsTArray<CacheRequestResponse>& aPutList,
const nsTArray<nsCOMPtr<nsIInputStream>>& aRequestStreamList,
const nsTArray<nsCOMPtr<nsIInputStream>>& aResponseStreamList);
void ExecuteStorageOp(Listener* aListener, Namespace aNamespace,
const CacheOpArgs& aOpArgs);
void ExecuteOpenStream(Listener* aListener, InputStreamResolver&& aResolver,
const nsID& aBodyId);
void NoteStreamOpenComplete(const nsID& aBodyId, ErrorResult&& aRv,
nsCOMPtr<nsIInputStream>&& aBodyStream);
class Factory;
class BaseAction;
class DeleteOrphanedCacheAction;
class CacheMatchAction;
class CacheMatchAllAction;
class CachePutAllAction;
class CacheDeleteAction;
class CacheKeysAction;
class StorageMatchAction;
class StorageHasAction;
class StorageOpenAction;
class StorageDeleteAction;
class StorageKeysAction;
class OpenStreamAction;
using ListenerId = uint64_t;
void Init(Maybe<Manager&> aOldManager);
void Shutdown();
void Abort();
ListenerId SaveListener(Listener* aListener);
Listener* GetListener(ListenerId aListenerId) const;
bool SetCacheIdOrphanedIfRefed(CacheId aCacheId);
bool SetBodyIdOrphanedIfRefed(const nsID& aBodyId);
void NoteOrphanedBodyIdList(const nsTArray<nsID>& aDeletedBodyIdList);
void MaybeAllowContextToClose();
SafeRefPtr<ManagerId> mManagerId;
nsCOMPtr<nsIThread> mIOThread;
// Weak reference cleared by RemoveContext() in Context destructor.
Context* MOZ_NON_OWNING_REF mContext;
// Weak references cleared by RemoveListener() in Listener destructors.
struct ListenerEntry {
ListenerEntry() : mId(UINT64_MAX), mListener(nullptr) {}
ListenerEntry(ListenerId aId, Listener* aListener)
: mId(aId), mListener(aListener) {}
ListenerId mId;
Listener* mListener;
class ListenerEntryIdComparator {
bool Equals(const ListenerEntry& aA, const ListenerId& aB) const {
return aA.mId == aB;
class ListenerEntryListenerComparator {
bool Equals(const ListenerEntry& aA, const Listener* aB) const {
return aA.mListener == aB;
using ListenerList = nsTArray<ListenerEntry>;
ListenerList mListeners;
static ListenerId sNextListenerId;
// Weak references cleared by RemoveStreamList() in StreamList destructors.
nsTArray<NotNull<StreamList*>> mStreamLists;
bool mShuttingDown;
State mState;
struct CacheIdRefCounter {
CacheId mCacheId;
MozRefCountType mCount;
bool mOrphaned;
nsTArray<CacheIdRefCounter> mCacheIdRefs;
struct BodyIdRefCounter {
nsID mBodyId;
MozRefCountType mCount;
bool mOrphaned;
nsTArray<BodyIdRefCounter> mBodyIdRefs;
struct ConstructorGuard {};
Manager(SafeRefPtr<ManagerId> aManagerId, nsIThread* aIOThread,
const ConstructorGuard&);
} // namespace cache
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_cache_Manager_h