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