/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 |