1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314
|
/* -*- 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 "CacheCommon.h"
#include "mozilla/RefPtr.h"
#include "mozilla/dom/SafeRefPtr.h"
#include "mozilla/dom/cache/Types.h"
#include "mozilla/dom/quota/Client.h"
#include "mozilla/dom/quota/StringifyUtils.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 ClientDirectoryLock;
} // 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>, public Stringifyable {
using Client = quota::Client;
using ClientDirectoryLock = quota::ClientDirectoryLock;
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);
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) {}
protected:
~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<ClientDirectoryLock&> 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);
#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
void RecordMayNotDeleteCSCP(
mozilla::ipc::ActorId aCacheStreamControlParentId);
void RecordHaveDeletedCSCP(mozilla::ipc::ActorId aCacheStreamControlParentId);
#endif
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;
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 {
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;
}
};
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 {};
void DoStringify(nsACString& aData) override;
public:
Manager(SafeRefPtr<ManagerId> aManagerId, nsIThread* aIOThread,
const ConstructorGuard&);
~Manager();
NS_DECL_OWNINGTHREAD
MOZ_DECLARE_REFCOUNTED_TYPENAME(cache::Manager)
};
} // namespace cache
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_cache_Manager_h
|