summaryrefslogtreecommitdiffstats
path: root/dom/cache/Manager.h
blob: d5ebce6d48f37e03c38609870b23237f55ed8e81 (plain)
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
/* -*- 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/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;

 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<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);

#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
  void RecordMayNotDeleteCSCP(int32_t aCacheStreamControlParentId);
  void RecordHaveDeletedCSCP(int32_t 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 {};

 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