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