/* -*- 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