Coverage Report

Created: 2018-09-25 14:53

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