diff options
Diffstat (limited to 'dom/cache/Manager.h')
-rw-r--r-- | dom/cache/Manager.h | 292 |
1 files changed, 292 insertions, 0 deletions
diff --git a/dom/cache/Manager.h b/dom/cache/Manager.h new file mode 100644 index 000000000..20392dad8 --- /dev/null +++ b/dom/cache/Manager.h @@ -0,0 +1,292 @@ +/* -*- 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 http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_cache_Manager_h +#define mozilla_dom_cache_Manager_h + +#include "mozilla/dom/cache/Types.h" +#include "nsCOMPtr.h" +#include "nsISupportsImpl.h" +#include "mozilla/RefPtr.h" +#include "nsString.h" +#include "nsTArray.h" + +class nsIInputStream; +class nsIThread; + +namespace mozilla { + +class ErrorResult; + +namespace dom { +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: + // 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 + { + public: + // 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); + + // interface to be implemented + virtual void + OnOpComplete(ErrorResult&& aRv, const CacheOpResult& aResult, + CacheId aOpenedCacheId, + const nsTArray<SavedResponse>& aSavedResponseList, + const nsTArray<SavedRequest>& aSavedRequestList, + StreamList* aStreamList) { } + + protected: + ~Listener() { } + }; + + enum State + { + Open, + Closing + }; + + static nsresult GetOrCreate(ManagerId* aManagerId, Manager** aManagerOut); + static already_AddRefed<Manager> Get(ManagerId* aManagerId); + + // Synchronously shutdown. This spins the event loop. + static void ShutdownAll(); + + // Cancel actions for given origin or all actions if passed string is null. + static void Abort(const nsACString& aOrigin); + + // 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); + + already_AddRefed<ManagerId> GetManagerId() 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); + +private: + 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; + + typedef uint64_t ListenerId; + + Manager(ManagerId* aManagerId, nsIThread* aIOThread); + ~Manager(); + void Init(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(); + + RefPtr<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 + { + public: + bool Equals(const ListenerEntry& aA, const ListenerId& aB) const + { + return aA.mId == aB; + } + }; + + class ListenerEntryListenerComparator + { + public: + bool Equals(const ListenerEntry& aA, const Listener* aB) const + { + return aA.mListener == aB; + } + }; + + typedef nsTArray<ListenerEntry> ListenerList; + ListenerList mListeners; + static ListenerId sNextListenerId; + + // Weak references cleared by RemoveStreamList() in StreamList destructors. + nsTArray<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; + +public: + NS_INLINE_DECL_REFCOUNTING(cache::Manager) +}; + +} // namespace cache +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_cache_Manager_h |