Bug 1707290 [wpt PR 28671] - Auto-expand details elements for find-in-page, a=testonly
[gecko.git] / dom / storage / LocalStorageCache.h
blob4c9f9a480dd48a97e1e533a264abce043d69de19
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"
12 #include "nsString.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 {
20 namespace dom {
22 class LocalStorage;
23 class LocalStorageCacheChild;
24 class LocalStorageManager;
25 class StorageUsage;
26 class StorageDBBridge;
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);
35 // The origin of the cache, result is concatenation of OriginNoSuffix() and
36 // OriginSuffix(), see below.
37 virtual const nsCString Origin() const = 0;
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;
43 // The origin in the database usage format (reversed) and without the suffix
44 virtual const nsCString& OriginNoSuffix() const = 0;
46 // Whether the cache is already fully loaded
47 virtual bool Loaded() = 0;
49 // How many items has so far been loaded into the cache, used
50 // for optimization purposes
51 virtual uint32_t LoadedCount() = 0;
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;
56 // Called by the database after all keys and values has been loaded
57 // to this cache
58 virtual void LoadDone(nsresult aRv) = 0;
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;
64 protected:
65 virtual ~LocalStorageCacheBridge() = default;
67 ThreadSafeAutoRefCnt mRefCnt;
68 NS_DECL_OWNINGTHREAD
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); }
79 void SetActor(LocalStorageCacheChild* aActor);
81 void ClearActor() {
82 AssertIsOnOwningThread();
84 mActor = nullptr;
87 NS_IMETHOD_(void) Release(void) override;
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
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);
111 protected:
112 virtual ~LocalStorageCache();
114 public:
115 void Init(LocalStorageManager* aManager, bool aPersistent,
116 nsIPrincipal* aPrincipal, const nsACString& aQuotaOriginScope);
118 // Get size of per-origin data.
119 int64_t GetOriginQuotaUsage(const LocalStorage* aStorage) const;
121 // Starts async preload of this cache if it persistent and not loaded.
122 void Preload();
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);
142 void GetKeys(const LocalStorage* aStorage, nsTArray<nsString>& aKeys);
144 // LocalStorageCacheBridge
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;
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 nsTHashMap<nsStringHashKey, nsString> mKeys;
165 public:
166 // Number of data sets we keep: default, session
167 static const uint32_t kDataSetCount = 2;
169 private:
170 // API to clear the cache data, this is invoked by chrome operations
171 // like cookie deletion.
172 friend class LocalStorageManager;
174 static const uint32_t kUnloadDefault = 1 << 0;
175 static const uint32_t kUnloadSession = 1 << 1;
176 static const uint32_t kUnloadComplete = kUnloadDefault | kUnloadSession;
178 #ifdef DOM_STORAGE_TESTS
179 static const uint32_t kTestReload = 1 << 15;
180 #endif
182 void UnloadItems(uint32_t aUnloadFlags);
184 private:
185 // Synchronously blocks until the cache is fully loaded from the database
186 void WaitForPreload(mozilla::Telemetry::HistogramID aTelemetryID);
188 // Helper to get one of the 3 data sets (regular, private, session)
189 Data& DataSet(const LocalStorage* aStorage);
191 // Used for firing storage events and synchronization of caches in other
192 // content processes.
193 void NotifyObservers(const LocalStorage* aStorage, const nsString& aKey,
194 const nsString& aOldValue, const nsString& aNewValue);
196 // Whether the storage change is about to persist
197 bool Persist(const LocalStorage* aStorage) const;
199 // Changes the quota usage on the given data set if it fits the quota.
200 // If not, then false is returned and no change to the set must be done.
201 // A special case is if aSource==E10sPropagated, then we will return true even
202 // if the change would put us over quota. This is done to ensure coherency of
203 // caches between processes in the face of races. It does allow an attacker
204 // to potentially use N multiples of the quota storage limit if they can
205 // arrange for their origin to execute code in N processes. However, this is
206 // not considered a particularly concerning threat model because it's already
207 // very possible for a rogue page to attempt to intentionally fill up the
208 // user's storage through the use of multiple domains.
209 bool ProcessUsageDelta(uint32_t aGetDataSetIndex, const int64_t aDelta,
210 const MutationSource aSource = ContentMutation);
211 bool ProcessUsageDelta(const LocalStorage* aStorage, const int64_t aDelta,
212 const MutationSource aSource = ContentMutation);
214 private:
215 // When a cache is reponsible for its life time (in case of localStorage data
216 // cache) we need to refer our manager since removal of the cache from the
217 // hash table is handled in the destructor by call to the manager. Cache
218 // could potentially overlive the manager, hence the hard ref.
219 RefPtr<LocalStorageManager> mManager;
221 // Reference to the usage counter object we check on for eTLD+1 quota limit.
222 // Obtained from the manager during initialization (Init method).
223 RefPtr<StorageUsage> mUsage;
225 // The LocalStorageCacheChild is created at the same time of this class.
226 // In normal operation, the actor will be synchronously cleared in our
227 // destructor when we tell it to delete itself. In a shutdown-related edge
228 // case in the parent process for JSM's, it is possible for the actor to be
229 // destroyed while this class remains alive, in which case it will be nulled
230 // out.
231 LocalStorageCacheChild* mActor;
233 // The origin this cache belongs to in the "DB format", i.e. reversed
234 nsCString mOriginNoSuffix;
236 // The origin attributes suffix
237 nsCString mOriginSuffix;
239 // The eTLD+1 scope used to count quota usage. It is in the reversed format
240 // and contains the origin attributes suffix.
241 nsCString mQuotaOriginScope;
243 // Non-private Browsing, Private Browsing and Session Only sets.
244 Data mData[kDataSetCount];
246 // This monitor is used to wait for full load of data.
247 mozilla::Monitor mMonitor;
249 // Flag that is initially false. When the cache is about to work with
250 // the database (i.e. it is persistent) this flags is set to true after
251 // all keys and coresponding values are loaded from the database.
252 // This flag never goes from true back to false. Since this flag is
253 // critical for mData hashtable synchronization, it's made atomic.
254 Atomic<bool, ReleaseAcquire> mLoaded;
256 // Result of load from the database. Valid after mLoaded flag has been set.
257 nsresult mLoadResult;
259 // Expected to be only 0 or 1.
260 uint32_t mPrivateBrowsingId;
262 // Init() method has been called
263 bool mInitialized : 1;
265 // This cache is about to be bound with the database (i.e. it has
266 // to load from the DB first and has to persist when modifying the
267 // default data set.)
268 bool mPersistent : 1;
270 // Whether we have already captured state of the cache preload on our first
271 // access.
272 bool mPreloadTelemetryRecorded : 1;
275 // StorageUsage
276 // Infrastructure to manage and check eTLD+1 quota
277 class StorageUsageBridge {
278 public:
279 NS_INLINE_DECL_THREADSAFE_VIRTUAL_REFCOUNTING(StorageUsageBridge)
281 virtual const nsCString& OriginScope() = 0;
282 virtual void LoadUsage(const int64_t aUsage) = 0;
284 protected:
285 // Protected destructor, to discourage deletion outside of Release():
286 virtual ~StorageUsageBridge() = default;
289 class StorageUsage : public StorageUsageBridge {
290 public:
291 explicit StorageUsage(const nsACString& aOriginScope);
293 bool CheckAndSetETLD1UsageDelta(
294 uint32_t aDataSetIndex, int64_t aUsageDelta,
295 const LocalStorageCache::MutationSource aSource);
297 private:
298 const nsCString& OriginScope() override { return mOriginScope; }
299 void LoadUsage(const int64_t aUsage) override;
301 nsCString mOriginScope;
302 int64_t mUsage[LocalStorageCache::kDataSetCount];
305 } // namespace dom
306 } // namespace mozilla
308 #endif // mozilla_dom_LocalStorageCache_h