Source code

Revision control

Other Tools

1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
3
/* This Source Code Form is subject to the terms of the Mozilla Public
4
* License, v. 2.0. If a copy of the MPL was not distributed with this
5
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
#ifndef mozilla_dom_LocalStorageCache_h
8
#define mozilla_dom_LocalStorageCache_h
9
10
#include "nsIPrincipal.h"
11
12
#include "nsString.h"
13
#include "nsDataHashtable.h"
14
#include "nsHashKeys.h"
15
#include "mozilla/Monitor.h"
16
#include "mozilla/Telemetry.h"
17
#include "mozilla/Atomics.h"
18
19
namespace mozilla {
20
namespace dom {
21
22
class LocalStorage;
23
class LocalStorageCacheChild;
24
class LocalStorageManager;
25
class StorageUsage;
26
class StorageDBBridge;
27
28
// Interface class on which only the database or IPC may call.
29
// Used to populate the cache with DB data.
30
class LocalStorageCacheBridge {
31
public:
32
NS_IMETHOD_(MozExternalRefCountType) AddRef(void);
33
NS_IMETHOD_(void) Release(void);
34
35
// The origin of the cache, result is concatenation of OriginNoSuffix() and
36
// OriginSuffix(), see below.
37
virtual const nsCString Origin() const = 0;
38
39
// The origin attributes suffix alone, this is usually passed as an
40
// |aOriginSuffix| argument to various methods
41
virtual const nsCString& OriginSuffix() const = 0;
42
43
// The origin in the database usage format (reversed) and without the suffix
44
virtual const nsCString& OriginNoSuffix() const = 0;
45
46
// Whether the cache is already fully loaded
47
virtual bool Loaded() = 0;
48
49
// How many items has so far been loaded into the cache, used
50
// for optimization purposes
51
virtual uint32_t LoadedCount() = 0;
52
53
// Called by the database to load a key and its value to the cache
54
virtual bool LoadItem(const nsAString& aKey, const nsString& aValue) = 0;
55
56
// Called by the database after all keys and values has been loaded
57
// to this cache
58
virtual void LoadDone(nsresult aRv) = 0;
59
60
// Use to synchronously wait until the cache gets fully loaded with data,
61
// this method exits after LoadDone has been called
62
virtual void LoadWait() = 0;
63
64
protected:
65
virtual ~LocalStorageCacheBridge() {}
66
67
ThreadSafeAutoRefCnt mRefCnt;
68
NS_DECL_OWNINGTHREAD
69
};
70
71
// Implementation of scope cache that is responsible for preloading data
72
// for persistent storage (localStorage) and hold data for non-private,
73
// private and session-only cookie modes. It is also responsible for
74
// persisting data changes using the database, works as a write-back cache.
75
class LocalStorageCache : public LocalStorageCacheBridge {
76
public:
77
void AssertIsOnOwningThread() const { NS_ASSERT_OWNINGTHREAD(LocalStorage); }
78
79
void SetActor(LocalStorageCacheChild* aActor);
80
81
void ClearActor() {
82
AssertIsOnOwningThread();
83
84
mActor = nullptr;
85
}
86
87
NS_IMETHOD_(void) Release(void) override;
88
89
enum MutationSource {
90
// The mutation is a result of an explicit JS mutation in this process.
91
// The mutation should be sent to the sDatabase. Quota will be checked and
92
// QuotaExceededError may be returned without the mutation being applied.
93
ContentMutation,
94
// The mutation initially was triggered in a different process and is being
95
// propagated to this cache via LocalStorage::ApplyEvent. The mutation
96
// should
97
// not be sent to the sDatabase because the originating process is already
98
// doing that. (In addition to the redundant writes being wasteful, there
99
// is the potential for other processes to see inconsistent state from the
100
// database while preloading.) Quota will be updated but not checked
101
// because it's assumed it was checked in another process and data-coherency
102
// is more important than slightly exceeding quota.
103
E10sPropagated
104
};
105
106
// Note: We pass aOriginNoSuffix through the ctor here, because
107
// LocalStorageCacheHashKey's ctor is creating this class and
108
// accepts reversed-origin-no-suffix as an argument - the hashing key.
109
explicit LocalStorageCache(const nsACString* aOriginNoSuffix);
110
111
protected:
112
virtual ~LocalStorageCache();
113
114
public:
115
void Init(LocalStorageManager* aManager, bool aPersistent,
116
nsIPrincipal* aPrincipal, const nsACString& aQuotaOriginScope);
117
118
// Get size of per-origin data.
119
int64_t GetOriginQuotaUsage(const LocalStorage* aStorage) const;
120
121
// Starts async preload of this cache if it persistent and not loaded.
122
void Preload();
123
124
// The set of methods that are invoked by DOM storage web API.
125
// We are passing the LocalStorage object just to let the cache
126
// read properties like mPrivate and mSessionOnly.
127
// Get* methods return error when load from the database has failed.
128
nsresult GetLength(const LocalStorage* aStorage, uint32_t* aRetval);
129
nsresult GetKey(const LocalStorage* aStorage, uint32_t index,
130
nsAString& aRetval);
131
nsresult GetItem(const LocalStorage* aStorage, const nsAString& aKey,
132
nsAString& aRetval);
133
nsresult SetItem(const LocalStorage* aStorage, const nsAString& aKey,
134
const nsString& aValue, nsString& aOld,
135
const MutationSource aSource = ContentMutation);
136
nsresult RemoveItem(const LocalStorage* aStorage, const nsAString& aKey,
137
nsString& aOld,
138
const MutationSource aSource = ContentMutation);
139
nsresult Clear(const LocalStorage* aStorage,
140
const MutationSource aSource = ContentMutation);
141
142
void GetKeys(const LocalStorage* aStorage, nsTArray<nsString>& aKeys);
143
144
// LocalStorageCacheBridge
145
146
const nsCString Origin() const override;
147
const nsCString& OriginNoSuffix() const override { return mOriginNoSuffix; }
148
const nsCString& OriginSuffix() const override { return mOriginSuffix; }
149
bool Loaded() override { return mLoaded; }
150
uint32_t LoadedCount() override;
151
bool LoadItem(const nsAString& aKey, const nsString& aValue) override;
152
void LoadDone(nsresult aRv) override;
153
void LoadWait() override;
154
155
// Cache keeps 3 sets of data: regular, private and session-only.
156
// This class keeps keys and values for a set and also caches
157
// size of the data for quick per-origin quota checking.
158
class Data {
159
public:
160
Data() : mOriginQuotaUsage(0) {}
161
int64_t mOriginQuotaUsage;
162
nsDataHashtable<nsStringHashKey, nsString> mKeys;
163
};
164
165
public:
166
// Number of data sets we keep: default, private, session
167
static const uint32_t kDataSetCount = 3;
168
169
private:
170
// API to clear the cache data, this is invoked by chrome operations
171
// like cookie deletion.
172
friend class LocalStorageManager;
173
174
static const uint32_t kUnloadDefault = 1 << 0;
175
static const uint32_t kUnloadPrivate = 1 << 1;
176
static const uint32_t kUnloadSession = 1 << 2;
177
static const uint32_t kUnloadComplete =
178
kUnloadDefault | kUnloadPrivate | kUnloadSession;
179
180
#ifdef DOM_STORAGE_TESTS
181
static const uint32_t kTestReload = 1 << 15;
182
#endif
183
184
void UnloadItems(uint32_t aUnloadFlags);
185
186
private:
187
// Synchronously blocks until the cache is fully loaded from the database
188
void WaitForPreload(mozilla::Telemetry::HistogramID aTelemetryID);
189
190
// Helper to get one of the 3 data sets (regular, private, session)
191
Data& DataSet(const LocalStorage* aStorage);
192
193
// Used for firing storage events and synchronization of caches in other
194
// content processes.
195
void NotifyObservers(const LocalStorage* aStorage, const nsString& aKey,
196
const nsString& aOldValue, const nsString& aNewValue);
197
198
// Whether the storage change is about to persist
199
bool Persist(const LocalStorage* aStorage) const;
200
201
// Changes the quota usage on the given data set if it fits the quota.
202
// If not, then false is returned and no change to the set must be done.
203
// A special case is if aSource==E10sPropagated, then we will return true even
204
// if the change would put us over quota. This is done to ensure coherency of
205
// caches between processes in the face of races. It does allow an attacker
206
// to potentially use N multiples of the quota storage limit if they can
207
// arrange for their origin to execute code in N processes. However, this is
208
// not considered a particularly concerning threat model because it's already
209
// very possible for a rogue page to attempt to intentionally fill up the
210
// user's storage through the use of multiple domains.
211
bool ProcessUsageDelta(uint32_t aGetDataSetIndex, const int64_t aDelta,
212
const MutationSource aSource = ContentMutation);
213
bool ProcessUsageDelta(const LocalStorage* aStorage, const int64_t aDelta,
214
const MutationSource aSource = ContentMutation);
215
216
private:
217
// When a cache is reponsible for its life time (in case of localStorage data
218
// cache) we need to refer our manager since removal of the cache from the
219
// hash table is handled in the destructor by call to the manager. Cache
220
// could potentially overlive the manager, hence the hard ref.
221
RefPtr<LocalStorageManager> mManager;
222
223
// Reference to the usage counter object we check on for eTLD+1 quota limit.
224
// Obtained from the manager during initialization (Init method).
225
RefPtr<StorageUsage> mUsage;
226
227
// The LocalStorageCacheChild is created at the same time of this class.
228
// In normal operation, the actor will be synchronously cleared in our
229
// destructor when we tell it to delete itself. In a shutdown-related edge
230
// case in the parent process for JSM's, it is possible for the actor to be
231
// destroyed while this class remains alive, in which case it will be nulled
232
// out.
233
LocalStorageCacheChild* mActor;
234
235
// The origin this cache belongs to in the "DB format", i.e. reversed
236
nsCString mOriginNoSuffix;
237
238
// The origin attributes suffix
239
nsCString mOriginSuffix;
240
241
// The eTLD+1 scope used to count quota usage. It is in the reversed format
242
// and contains the origin attributes suffix.
243
nsCString mQuotaOriginScope;
244
245
// Non-private Browsing, Private Browsing and Session Only sets.
246
Data mData[kDataSetCount];
247
248
// This monitor is used to wait for full load of data.
249
mozilla::Monitor mMonitor;
250
251
// Flag that is initially false. When the cache is about to work with
252
// the database (i.e. it is persistent) this flags is set to true after
253
// all keys and coresponding values are loaded from the database.
254
// This flag never goes from true back to false. Since this flag is
255
// critical for mData hashtable synchronization, it's made atomic.
256
Atomic<bool, ReleaseAcquire> mLoaded;
257
258
// Result of load from the database. Valid after mLoaded flag has been set.
259
nsresult mLoadResult;
260
261
// Init() method has been called
262
bool mInitialized : 1;
263
264
// This cache is about to be bound with the database (i.e. it has
265
// to load from the DB first and has to persist when modifying the
266
// default data set.)
267
bool mPersistent : 1;
268
269
// Whether we have already captured state of the cache preload on our first
270
// access.
271
bool mPreloadTelemetryRecorded : 1;
272
};
273
274
// StorageUsage
275
// Infrastructure to manage and check eTLD+1 quota
276
class StorageUsageBridge {
277
public:
278
NS_INLINE_DECL_THREADSAFE_VIRTUAL_REFCOUNTING(StorageUsageBridge)
279
280
virtual const nsCString& OriginScope() = 0;
281
virtual void LoadUsage(const int64_t aUsage) = 0;
282
283
protected:
284
// Protected destructor, to discourage deletion outside of Release():
285
virtual ~StorageUsageBridge() {}
286
};
287
288
class StorageUsage : public StorageUsageBridge {
289
public:
290
explicit StorageUsage(const nsACString& aOriginScope);
291
292
bool CheckAndSetETLD1UsageDelta(
293
uint32_t aDataSetIndex, int64_t aUsageDelta,
294
const LocalStorageCache::MutationSource aSource);
295
296
private:
297
const nsCString& OriginScope() override { return mOriginScope; }
298
void LoadUsage(const int64_t aUsage) override;
299
300
nsCString mOriginScope;
301
int64_t mUsage[LocalStorageCache::kDataSetCount];
302
};
303
304
} // namespace dom
305
} // namespace mozilla
306
307
#endif // mozilla_dom_LocalStorageCache_h