summaryrefslogtreecommitdiffstats
path: root/dom/storage/SessionStorageManager.h
blob: 4880f132703ce982b358cc7f8ad9d7cd35321688 (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
/* -*- 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_SessionStorageManager_h
#define mozilla_dom_SessionStorageManager_h

#include "StorageObserver.h"

#include "mozilla/dom/FlippedOnce.h"
#include "nsIDOMStorageManager.h"
#include "nsClassHashtable.h"
#include "nsCycleCollectionParticipant.h"
#include "nsHashKeys.h"

#include "mozilla/ipc/PBackgroundChild.h"
#include "mozilla/ipc/PBackgroundParent.h"

class nsIPrincipal;
class nsITimer;

namespace mozilla {
class OriginAttributesPattern;

namespace dom {

class SSCacheCopy;

bool RecvShutdownBackgroundSessionStorageManagers();
void RecvPropagateBackgroundSessionStorageManager(uint64_t aCurrentTopContextId,
                                                  uint64_t aTargetTopContextId);
bool RecvRemoveBackgroundSessionStorageManager(uint64_t aTopContextId);

bool RecvGetSessionStorageData(
    uint64_t aTopContextId, uint32_t aSizeLimit, bool aCancelSessionStoreTimer,
    ::mozilla::ipc::PBackgroundParent::GetSessionStorageManagerDataResolver&&
        aResolver);

bool RecvLoadSessionStorageData(
    uint64_t aTopContextId,
    nsTArray<mozilla::dom::SSCacheCopy>&& aCacheCopyList);

bool RecvClearStoragesForOrigin(const nsACString& aOriginAttrs,
                                const nsACString& aOriginKey);

class BrowsingContext;
class ContentParent;
class SSSetItemInfo;
class SSWriteInfo;
class SessionStorageCache;
class SessionStorageCacheChild;
class SessionStorageManagerChild;
class SessionStorageManagerParent;
class SessionStorageObserver;
struct OriginRecord;

// sessionStorage is a data store that's unique to each tab (i.e. top-level
// browsing context) and origin. Before the advent of Fission all the data
// for a given tab could be stored in a single content process; now each
// site-specific process stores only its portion of the data. As content
// processes terminate, their sessionStorage data needs to be saved in the
// parent process, in case the same origin appears again in the tab (e.g.
// by navigating an iframe element). Therefore SessionStorageManager
// objects exist in both the parent and content processes.
//
// Whenever a write operation for SessionStorage executes, the content process
// sends the changes to the parent process at the next stable state. Whenever a
// content process navigates to an origin for the first time in a given tab, the
// parent process sends it the saved data. SessionStorageCache has a flag
// (mLoadedOrCloned) to ensure that it's only be loaded or cloned once.
//
// Note: the current implementation is expected to be replaced by a new
// implementation using LSNG.
class SessionStorageManagerBase {
 public:
  SessionStorageManagerBase() = default;

 protected:
  ~SessionStorageManagerBase() = default;

  struct OriginRecord {
    OriginRecord() = default;
    OriginRecord(OriginRecord&&) = default;
    OriginRecord& operator=(OriginRecord&&) = default;
    ~OriginRecord();

    RefPtr<SessionStorageCache> mCache;

    // A flag to ensure that cache is only loaded once.
    FlippedOnce<false> mLoaded;
  };

  void ClearStoragesInternal(const OriginAttributesPattern& aPattern,
                             const nsACString& aOriginScope);

  void ClearStoragesForOriginInternal(const nsACString& aOriginAttrs,
                                      const nsACString& aOriginKey);

  OriginRecord* GetOriginRecord(const nsACString& aOriginAttrs,
                                const nsACString& aOriginKey,
                                bool aMakeIfNeeded,
                                SessionStorageCache* aCloneFrom);

  using OriginKeyHashTable = nsClassHashtable<nsCStringHashKey, OriginRecord>;
  nsClassHashtable<nsCStringHashKey, OriginKeyHashTable> mOATable;
};

class SessionStorageManager final : public SessionStorageManagerBase,
                                    public nsIDOMSessionStorageManager,
                                    public StorageObserverSink {
 public:
  explicit SessionStorageManager(RefPtr<BrowsingContext> aBrowsingContext);

  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
  NS_DECL_NSIDOMSTORAGEMANAGER
  NS_DECL_NSIDOMSESSIONSTORAGEMANAGER

  NS_DECL_CYCLE_COLLECTION_CLASS(SessionStorageManager)

  bool CanLoadData();

  void SetActor(SessionStorageManagerChild* aActor);

  bool ActorExists() const;

  void ClearActor();

  nsresult EnsureManager();

  nsresult LoadData(nsIPrincipal& aPrincipal, SessionStorageCache& aCache);

  void CheckpointData(nsIPrincipal& aPrincipal, SessionStorageCache& aCache);

  nsresult ClearStoragesForOrigin(const nsACString& aOriginAttrs,
                                  const nsACString& aOriginKey);

 private:
  ~SessionStorageManager();

  // StorageObserverSink, handler to various chrome clearing notification
  nsresult Observe(const char* aTopic,
                   const nsAString& aOriginAttributesPattern,
                   const nsACString& aOriginScope) override;

  nsresult GetSessionStorageCacheHelper(nsIPrincipal* aPrincipal,
                                        bool aMakeIfNeeded,
                                        SessionStorageCache* aCloneFrom,
                                        RefPtr<SessionStorageCache>* aRetVal);

  nsresult GetSessionStorageCacheHelper(const nsACString& aOriginAttrs,
                                        const nsACString& aOriginKey,
                                        bool aMakeIfNeeded,
                                        SessionStorageCache* aCloneFrom,
                                        RefPtr<SessionStorageCache>* aRetVal);

  void ClearStorages(const OriginAttributesPattern& aPattern,
                     const nsACString& aOriginScope);

  SessionStorageCacheChild* EnsureCache(nsIPrincipal& aPrincipal,
                                        const nsACString& aOriginKey,
                                        SessionStorageCache& aCache);

  void CheckpointDataInternal(nsIPrincipal& aPrincipal,
                              const nsACString& aOriginKey,
                              SessionStorageCache& aCache);

  RefPtr<SessionStorageObserver> mObserver;

  RefPtr<BrowsingContext> mBrowsingContext;

  SessionStorageManagerChild* mActor;
};

/**
 * A specialized SessionStorageManager class that lives on the parent process
 * background thread. It is a shadow copy of SessionStorageManager and it's used
 * to preserve SessionStorageCaches for the other SessionStorageManagers.
 */
class BackgroundSessionStorageManager final : public SessionStorageManagerBase {
 public:
  // Parent process getter function.
  static BackgroundSessionStorageManager* GetOrCreate(uint64_t aTopContextId);

  NS_INLINE_DECL_REFCOUNTING(BackgroundSessionStorageManager);

  // Only called by CanonicalBrowsingContext::ReplaceBy on the parent process.
  static void PropagateManager(uint64_t aCurrentTopContextId,
                               uint64_t aTargetTopContextId);

  // Only called by CanonicalBrowsingContext::CanonicalDiscard on parent
  // process.
  static void RemoveManager(uint64_t aTopContextId);

  static void LoadData(
      uint64_t aTopContextId,
      const nsTArray<mozilla::dom::SSCacheCopy>& aCacheCopyList);

  using DataPromise =
      ::mozilla::ipc::PBackgroundChild::GetSessionStorageManagerDataPromise;
  static RefPtr<DataPromise> GetData(BrowsingContext* aContext,
                                     uint32_t aSizeLimit,
                                     bool aClearSessionStoreTimer = false);

  void GetData(uint32_t aSizeLimit, nsTArray<SSCacheCopy>& aCacheCopyList);

  void CopyDataToContentProcess(const nsACString& aOriginAttrs,
                                const nsACString& aOriginKey,
                                nsTArray<SSSetItemInfo>& aData);

  void UpdateData(const nsACString& aOriginAttrs, const nsACString& aOriginKey,
                  const nsTArray<SSWriteInfo>& aWriteInfos);

  void UpdateData(const nsACString& aOriginAttrs, const nsACString& aOriginKey,
                  const nsTArray<SSSetItemInfo>& aData);

  void ClearStorages(const OriginAttributesPattern& aPattern,
                     const nsACString& aOriginScope);

  void ClearStoragesForOrigin(const nsACString& aOriginAttrs,
                              const nsACString& aOriginKey);

  void SetCurrentBrowsingContextId(uint64_t aBrowsingContextId);

  void MaybeDispatchSessionStoreUpdate();

  void CancelSessionStoreUpdate();

  void AddParticipatingActor(SessionStorageManagerParent* aActor);

  void RemoveParticipatingActor(SessionStorageManagerParent* aActor);

 private:
  // Only be called by GetOrCreate() on the parent process.
  explicit BackgroundSessionStorageManager(uint64_t aBrowsingContextId);

  ~BackgroundSessionStorageManager();

  // Sets a timer for notifying main thread that the cache has been
  // updated. May do nothing if we're coalescing notifications.
  void MaybeScheduleSessionStoreUpdate();

  void DispatchSessionStoreUpdate();

  // The most current browsing context using this manager
  uint64_t mCurrentBrowsingContextId;

  // Callback for notifying main thread of calls to `UpdateData`.
  //
  // A timer that is held whenever this manager has dirty state that
  // has not yet been reflected to the main thread. The timer is used
  // to delay notifying the main thread to ask for changes, thereby
  // coalescing/throttling changes. (Note that SessionStorage, like
  // LocalStorage, treats attempts to set a value to its current value
  // as a no-op.)
  //

  // The timer is initialized with a fixed delay as soon as the state
  // becomes dirty; additional mutations to our state will not reset
  // the timer because then we might never flush to the main
  // thread. The timer is cleared only when a new set of data is sent
  // to the main thread and therefore this manager no longer has any
  // dirty state. This means that there is a period of time after the
  // nsITimer fires where this value is non-null but there is no
  // scheduled timer while we wait for the main thread to request the
  // new state. Callers of GetData can also optionally cancel the
  // current timer to reduce the amounts of notifications.
  //
  // When this manager is moved to a new top-level browsing context id
  // via a PropagateBackgroundSessionStorageManager message, the
  // behavior of the timer doesn't change because the main thread knows
  // about the renaming and is initiating it (and any in-flight
  // GetSessionStorageManagerData requests will be unaffected because
  // they use async-returns so the response is inherently matched up via
  // the issued promise).
  nsCOMPtr<nsITimer> mSessionStoreCallbackTimer;

  nsTArray<RefPtr<SessionStorageManagerParent>> mParticipatingActors;
};

}  // namespace dom
}  // namespace mozilla

#endif  // mozilla_dom_SessionStorageManager_h