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 #include "LocalStorageCache.h"
8 
9 #include "Storage.h"
10 #include "StorageDBThread.h"
11 #include "StorageIPC.h"
12 #include "StorageUtils.h"
13 #include "LocalStorageManager.h"
14 
15 #include "nsAutoPtr.h"
16 #include "nsDOMString.h"
17 #include "nsXULAppAPI.h"
18 #include "mozilla/Unused.h"
19 #include "nsProxyRelease.h"
20 #include "nsThreadUtils.h"
21 
22 namespace mozilla {
23 namespace dom {
24 
25 #define DOM_STORAGE_CACHE_KEEP_ALIVE_TIME_MS 20000
26 
27 namespace {
28 
29 const uint32_t kDefaultSet = 0;
30 const uint32_t kPrivateSet = 1;
31 const uint32_t kSessionSet = 2;
32 
GetDataSetIndex(bool aPrivate,bool aSessionOnly)33 inline uint32_t GetDataSetIndex(bool aPrivate, bool aSessionOnly) {
34   if (aPrivate) {
35     return kPrivateSet;
36   }
37 
38   if (aSessionOnly) {
39     return kSessionSet;
40   }
41 
42   return kDefaultSet;
43 }
44 
GetDataSetIndex(const LocalStorage * aStorage)45 inline uint32_t GetDataSetIndex(const LocalStorage* aStorage) {
46   return GetDataSetIndex(aStorage->IsPrivate(), aStorage->IsSessionOnly());
47 }
48 
49 }  // namespace
50 
51 // LocalStorageCacheBridge
52 
53 NS_IMPL_ADDREF(LocalStorageCacheBridge)
54 
55 // Since there is no consumer of return value of Release, we can turn this
56 // method to void to make implementation of asynchronous
57 // LocalStorageCache::Release much simpler.
NS_IMETHODIMP_(void)58 NS_IMETHODIMP_(void) LocalStorageCacheBridge::Release(void) {
59   MOZ_ASSERT(int32_t(mRefCnt) > 0, "dup release");
60   nsrefcnt count = --mRefCnt;
61   NS_LOG_RELEASE(this, count, "LocalStorageCacheBridge");
62   if (0 == count) {
63     mRefCnt = 1; /* stabilize */
64     /* enable this to find non-threadsafe destructors: */
65     /* NS_ASSERT_OWNINGTHREAD(_class); */
66     delete (this);
67   }
68 }
69 
70 // LocalStorageCache
71 
LocalStorageCache(const nsACString * aOriginNoSuffix)72 LocalStorageCache::LocalStorageCache(const nsACString* aOriginNoSuffix)
73     : mActor(nullptr),
74       mOriginNoSuffix(*aOriginNoSuffix),
75       mMonitor("LocalStorageCache"),
76       mLoaded(false),
77       mLoadResult(NS_OK),
78       mInitialized(false),
79       mPersistent(false),
80       mSessionOnlyDataSetActive(false),
81       mPreloadTelemetryRecorded(false) {
82   MOZ_COUNT_CTOR(LocalStorageCache);
83 }
84 
~LocalStorageCache()85 LocalStorageCache::~LocalStorageCache() {
86   if (mActor) {
87     mActor->SendDeleteMeInternal();
88     MOZ_ASSERT(!mActor, "SendDeleteMeInternal should have cleared!");
89   }
90 
91   if (mManager) {
92     mManager->DropCache(this);
93   }
94 
95   MOZ_COUNT_DTOR(LocalStorageCache);
96 }
97 
SetActor(LocalStorageCacheChild * aActor)98 void LocalStorageCache::SetActor(LocalStorageCacheChild* aActor) {
99   AssertIsOnOwningThread();
100   MOZ_ASSERT(aActor);
101   MOZ_ASSERT(!mActor);
102 
103   mActor = aActor;
104 }
105 
NS_IMETHODIMP_(void)106 NS_IMETHODIMP_(void)
107 LocalStorageCache::Release(void) {
108   // We must actually release on the main thread since the cache removes it
109   // self from the manager's hash table.  And we don't want to lock access to
110   // that hash table.
111   if (NS_IsMainThread()) {
112     LocalStorageCacheBridge::Release();
113     return;
114   }
115 
116   RefPtr<nsRunnableMethod<LocalStorageCacheBridge, void, false>> event =
117       NewNonOwningRunnableMethod("dom::LocalStorageCacheBridge::Release",
118                                  static_cast<LocalStorageCacheBridge*>(this),
119                                  &LocalStorageCacheBridge::Release);
120 
121   nsresult rv = NS_DispatchToMainThread(event);
122   if (NS_FAILED(rv)) {
123     NS_WARNING("LocalStorageCache::Release() on a non-main thread");
124     LocalStorageCacheBridge::Release();
125   }
126 }
127 
Init(LocalStorageManager * aManager,bool aPersistent,nsIPrincipal * aPrincipal,const nsACString & aQuotaOriginScope)128 void LocalStorageCache::Init(LocalStorageManager* aManager, bool aPersistent,
129                              nsIPrincipal* aPrincipal,
130                              const nsACString& aQuotaOriginScope) {
131   if (mInitialized) {
132     return;
133   }
134 
135   mInitialized = true;
136   aPrincipal->OriginAttributesRef().CreateSuffix(mOriginSuffix);
137   mPersistent = aPersistent;
138   if (aQuotaOriginScope.IsEmpty()) {
139     mQuotaOriginScope = Origin();
140   } else {
141     mQuotaOriginScope = aQuotaOriginScope;
142   }
143 
144   if (mPersistent) {
145     mManager = aManager;
146     Preload();
147   }
148 
149   // Check the quota string has (or has not) the identical origin suffix as
150   // this storage cache is bound to.
151   MOZ_ASSERT(StringBeginsWith(mQuotaOriginScope, mOriginSuffix));
152   MOZ_ASSERT(mOriginSuffix.IsEmpty() !=
153              StringBeginsWith(mQuotaOriginScope, NS_LITERAL_CSTRING("^")));
154 
155   mUsage = aManager->GetOriginUsage(mQuotaOriginScope);
156 }
157 
NotifyObservers(const LocalStorage * aStorage,const nsString & aKey,const nsString & aOldValue,const nsString & aNewValue)158 void LocalStorageCache::NotifyObservers(const LocalStorage* aStorage,
159                                         const nsString& aKey,
160                                         const nsString& aOldValue,
161                                         const nsString& aNewValue) {
162   AssertIsOnOwningThread();
163   MOZ_ASSERT(aStorage);
164 
165   if (!mActor) {
166     return;
167   }
168 
169   // We want to send a message to the parent in order to broadcast the
170   // StorageEvent correctly to any child process.
171 
172   Unused << mActor->SendNotify(aStorage->DocumentURI(), aKey, aOldValue,
173                                aNewValue);
174 }
175 
Persist(const LocalStorage * aStorage) const176 inline bool LocalStorageCache::Persist(const LocalStorage* aStorage) const {
177   return mPersistent && !aStorage->IsSessionOnly() && !aStorage->IsPrivate();
178 }
179 
Origin() const180 const nsCString LocalStorageCache::Origin() const {
181   return LocalStorageManager::CreateOrigin(mOriginSuffix, mOriginNoSuffix);
182 }
183 
DataSet(const LocalStorage * aStorage)184 LocalStorageCache::Data& LocalStorageCache::DataSet(
185     const LocalStorage* aStorage) {
186   uint32_t index = GetDataSetIndex(aStorage);
187 
188   if (index == kSessionSet && !mSessionOnlyDataSetActive) {
189     // Session only data set is demanded but not filled with
190     // current data set, copy to session only set now.
191 
192     WaitForPreload(Telemetry::LOCALDOMSTORAGE_SESSIONONLY_PRELOAD_BLOCKING_MS);
193 
194     Data& defaultSet = mData[kDefaultSet];
195     Data& sessionSet = mData[kSessionSet];
196 
197     for (auto iter = defaultSet.mKeys.Iter(); !iter.Done(); iter.Next()) {
198       sessionSet.mKeys.Put(iter.Key(), iter.UserData());
199     }
200 
201     mSessionOnlyDataSetActive = true;
202 
203     // This updates sessionSet.mOriginQuotaUsage and also updates global usage
204     // for all session only data
205     ProcessUsageDelta(kSessionSet, defaultSet.mOriginQuotaUsage);
206   }
207 
208   return mData[index];
209 }
210 
ProcessUsageDelta(const LocalStorage * aStorage,int64_t aDelta,const MutationSource aSource)211 bool LocalStorageCache::ProcessUsageDelta(const LocalStorage* aStorage,
212                                           int64_t aDelta,
213                                           const MutationSource aSource) {
214   return ProcessUsageDelta(GetDataSetIndex(aStorage), aDelta, aSource);
215 }
216 
ProcessUsageDelta(uint32_t aGetDataSetIndex,const int64_t aDelta,const MutationSource aSource)217 bool LocalStorageCache::ProcessUsageDelta(uint32_t aGetDataSetIndex,
218                                           const int64_t aDelta,
219                                           const MutationSource aSource) {
220   // Check if we are in a low disk space situation
221   if (aSource == ContentMutation && aDelta > 0 && mManager &&
222       mManager->IsLowDiskSpace()) {
223     return false;
224   }
225 
226   // Check limit per this origin
227   Data& data = mData[aGetDataSetIndex];
228   uint64_t newOriginUsage = data.mOriginQuotaUsage + aDelta;
229   if (aSource == ContentMutation && aDelta > 0 &&
230       newOriginUsage > LocalStorageManager::GetQuota()) {
231     return false;
232   }
233 
234   // Now check eTLD+1 limit
235   if (mUsage &&
236       !mUsage->CheckAndSetETLD1UsageDelta(aGetDataSetIndex, aDelta, aSource)) {
237     return false;
238   }
239 
240   // Update size in our data set
241   data.mOriginQuotaUsage = newOriginUsage;
242   return true;
243 }
244 
Preload()245 void LocalStorageCache::Preload() {
246   if (mLoaded || !mPersistent) {
247     return;
248   }
249 
250   StorageDBChild* storageChild = StorageDBChild::GetOrCreate();
251   if (!storageChild) {
252     mLoaded = true;
253     mLoadResult = NS_ERROR_FAILURE;
254     return;
255   }
256 
257   storageChild->AsyncPreload(this);
258 }
259 
260 namespace {
261 
262 // The AutoTimer provided by telemetry headers is only using static,
263 // i.e. compile time known ID, but here we know the ID only at run time.
264 // Hence a new class.
265 class TelemetryAutoTimer {
266  public:
TelemetryAutoTimer(Telemetry::HistogramID aId)267   explicit TelemetryAutoTimer(Telemetry::HistogramID aId)
268       : id(aId), start(TimeStamp::Now()) {}
269 
~TelemetryAutoTimer()270   ~TelemetryAutoTimer() { Telemetry::AccumulateTimeDelta(id, start); }
271 
272  private:
273   Telemetry::HistogramID id;
274   const TimeStamp start;
275 };
276 
277 }  // namespace
278 
WaitForPreload(Telemetry::HistogramID aTelemetryID)279 void LocalStorageCache::WaitForPreload(Telemetry::HistogramID aTelemetryID) {
280   if (!mPersistent) {
281     return;
282   }
283 
284   bool loaded = mLoaded;
285 
286   // Telemetry of rates of pending preloads
287   if (!mPreloadTelemetryRecorded) {
288     mPreloadTelemetryRecorded = true;
289     Telemetry::Accumulate(
290         Telemetry::LOCALDOMSTORAGE_PRELOAD_PENDING_ON_FIRST_ACCESS, !loaded);
291   }
292 
293   if (loaded) {
294     return;
295   }
296 
297   // Measure which operation blocks and for how long
298   TelemetryAutoTimer timer(aTelemetryID);
299 
300   // If preload already started (i.e. we got some first data, but not all)
301   // SyncPreload will just wait for it to finish rather then synchronously
302   // read from the database.  It seems to me more optimal.
303 
304   // TODO place for A/B testing (force main thread load vs. let preload finish)
305 
306   // No need to check sDatabase for being non-null since preload is either
307   // done before we've shut the DB down or when the DB could not start,
308   // preload has not even be started.
309   StorageDBChild::Get()->SyncPreload(this);
310 }
311 
GetLength(const LocalStorage * aStorage,uint32_t * aRetval)312 nsresult LocalStorageCache::GetLength(const LocalStorage* aStorage,
313                                       uint32_t* aRetval) {
314   if (Persist(aStorage)) {
315     WaitForPreload(Telemetry::LOCALDOMSTORAGE_GETLENGTH_BLOCKING_MS);
316     if (NS_FAILED(mLoadResult)) {
317       return mLoadResult;
318     }
319   }
320 
321   *aRetval = DataSet(aStorage).mKeys.Count();
322   return NS_OK;
323 }
324 
GetKey(const LocalStorage * aStorage,uint32_t aIndex,nsAString & aRetval)325 nsresult LocalStorageCache::GetKey(const LocalStorage* aStorage,
326                                    uint32_t aIndex, nsAString& aRetval) {
327   // XXX: This does a linear search for the key at index, which would
328   // suck if there's a large numer of indexes. Do we care? If so,
329   // maybe we need to have a lazily populated key array here or
330   // something?
331   if (Persist(aStorage)) {
332     WaitForPreload(Telemetry::LOCALDOMSTORAGE_GETKEY_BLOCKING_MS);
333     if (NS_FAILED(mLoadResult)) {
334       return mLoadResult;
335     }
336   }
337 
338   aRetval.SetIsVoid(true);
339   for (auto iter = DataSet(aStorage).mKeys.Iter(); !iter.Done(); iter.Next()) {
340     if (aIndex == 0) {
341       aRetval = iter.Key();
342       break;
343     }
344     aIndex--;
345   }
346 
347   return NS_OK;
348 }
349 
GetKeys(const LocalStorage * aStorage,nsTArray<nsString> & aKeys)350 void LocalStorageCache::GetKeys(const LocalStorage* aStorage,
351                                 nsTArray<nsString>& aKeys) {
352   if (Persist(aStorage)) {
353     WaitForPreload(Telemetry::LOCALDOMSTORAGE_GETALLKEYS_BLOCKING_MS);
354   }
355 
356   if (NS_FAILED(mLoadResult)) {
357     return;
358   }
359 
360   for (auto iter = DataSet(aStorage).mKeys.Iter(); !iter.Done(); iter.Next()) {
361     aKeys.AppendElement(iter.Key());
362   }
363 }
364 
GetItem(const LocalStorage * aStorage,const nsAString & aKey,nsAString & aRetval)365 nsresult LocalStorageCache::GetItem(const LocalStorage* aStorage,
366                                     const nsAString& aKey, nsAString& aRetval) {
367   if (Persist(aStorage)) {
368     WaitForPreload(Telemetry::LOCALDOMSTORAGE_GETVALUE_BLOCKING_MS);
369     if (NS_FAILED(mLoadResult)) {
370       return mLoadResult;
371     }
372   }
373 
374   // not using AutoString since we don't want to copy buffer to result
375   nsString value;
376   if (!DataSet(aStorage).mKeys.Get(aKey, &value)) {
377     SetDOMStringToNull(value);
378   }
379 
380   aRetval = value;
381 
382   return NS_OK;
383 }
384 
SetItem(const LocalStorage * aStorage,const nsAString & aKey,const nsString & aValue,nsString & aOld,const MutationSource aSource)385 nsresult LocalStorageCache::SetItem(const LocalStorage* aStorage,
386                                     const nsAString& aKey,
387                                     const nsString& aValue, nsString& aOld,
388                                     const MutationSource aSource) {
389   // Size of the cache that will change after this action.
390   int64_t delta = 0;
391 
392   if (Persist(aStorage)) {
393     WaitForPreload(Telemetry::LOCALDOMSTORAGE_SETVALUE_BLOCKING_MS);
394     if (NS_FAILED(mLoadResult)) {
395       return mLoadResult;
396     }
397   }
398 
399   Data& data = DataSet(aStorage);
400   if (!data.mKeys.Get(aKey, &aOld)) {
401     SetDOMStringToNull(aOld);
402 
403     // We only consider key size if the key doesn't exist before.
404     delta += static_cast<int64_t>(aKey.Length());
405   }
406 
407   delta += static_cast<int64_t>(aValue.Length()) -
408            static_cast<int64_t>(aOld.Length());
409 
410   if (!ProcessUsageDelta(aStorage, delta, aSource)) {
411     return NS_ERROR_DOM_QUOTA_REACHED;
412   }
413 
414   if (aValue == aOld && DOMStringIsNull(aValue) == DOMStringIsNull(aOld)) {
415     return NS_SUCCESS_DOM_NO_OPERATION;
416   }
417 
418   data.mKeys.Put(aKey, aValue);
419 
420   if (aSource != ContentMutation) {
421     return NS_OK;
422   }
423 
424   NotifyObservers(aStorage, nsString(aKey), aOld, aValue);
425 
426   if (Persist(aStorage)) {
427     StorageDBChild* storageChild = StorageDBChild::Get();
428     if (!storageChild) {
429       NS_ERROR(
430           "Writing to localStorage after the database has been shut down"
431           ", data lose!");
432       return NS_ERROR_NOT_INITIALIZED;
433     }
434 
435     if (DOMStringIsNull(aOld)) {
436       return storageChild->AsyncAddItem(this, aKey, aValue);
437     }
438 
439     return storageChild->AsyncUpdateItem(this, aKey, aValue);
440   }
441 
442   return NS_OK;
443 }
444 
RemoveItem(const LocalStorage * aStorage,const nsAString & aKey,nsString & aOld,const MutationSource aSource)445 nsresult LocalStorageCache::RemoveItem(const LocalStorage* aStorage,
446                                        const nsAString& aKey, nsString& aOld,
447                                        const MutationSource aSource) {
448   if (Persist(aStorage)) {
449     WaitForPreload(Telemetry::LOCALDOMSTORAGE_REMOVEKEY_BLOCKING_MS);
450     if (NS_FAILED(mLoadResult)) {
451       return mLoadResult;
452     }
453   }
454 
455   Data& data = DataSet(aStorage);
456   if (!data.mKeys.Get(aKey, &aOld)) {
457     SetDOMStringToNull(aOld);
458     return NS_SUCCESS_DOM_NO_OPERATION;
459   }
460 
461   // Recalculate the cached data size
462   const int64_t delta = -(static_cast<int64_t>(aOld.Length()) +
463                           static_cast<int64_t>(aKey.Length()));
464   Unused << ProcessUsageDelta(aStorage, delta, aSource);
465   data.mKeys.Remove(aKey);
466 
467   if (aSource != ContentMutation) {
468     return NS_OK;
469   }
470 
471   NotifyObservers(aStorage, nsString(aKey), aOld, VoidString());
472 
473   if (Persist(aStorage)) {
474     StorageDBChild* storageChild = StorageDBChild::Get();
475     if (!storageChild) {
476       NS_ERROR(
477           "Writing to localStorage after the database has been shut down"
478           ", data lose!");
479       return NS_ERROR_NOT_INITIALIZED;
480     }
481 
482     return storageChild->AsyncRemoveItem(this, aKey);
483   }
484 
485   return NS_OK;
486 }
487 
Clear(const LocalStorage * aStorage,const MutationSource aSource)488 nsresult LocalStorageCache::Clear(const LocalStorage* aStorage,
489                                   const MutationSource aSource) {
490   bool refresh = false;
491   if (Persist(aStorage)) {
492     // We need to preload all data (know the size) before we can proceeed
493     // to correctly decrease cached usage number.
494     // XXX as in case of unload, this is not technically needed now, but
495     // after super-scope quota introduction we have to do this.  Get telemetry
496     // right now.
497     WaitForPreload(Telemetry::LOCALDOMSTORAGE_CLEAR_BLOCKING_MS);
498     if (NS_FAILED(mLoadResult)) {
499       // When we failed to load data from the database, force delete of the
500       // scope data and make use of the storage possible again.
501       refresh = true;
502       mLoadResult = NS_OK;
503     }
504   }
505 
506   Data& data = DataSet(aStorage);
507   bool hadData = !!data.mKeys.Count();
508 
509   if (hadData) {
510     Unused << ProcessUsageDelta(aStorage, -data.mOriginQuotaUsage, aSource);
511     data.mKeys.Clear();
512   }
513 
514   if (aSource != ContentMutation) {
515     return hadData ? NS_OK : NS_SUCCESS_DOM_NO_OPERATION;
516   }
517 
518   if (hadData) {
519     NotifyObservers(aStorage, VoidString(), VoidString(), VoidString());
520   }
521 
522   if (Persist(aStorage) && (refresh || hadData)) {
523     StorageDBChild* storageChild = StorageDBChild::Get();
524     if (!storageChild) {
525       NS_ERROR(
526           "Writing to localStorage after the database has been shut down"
527           ", data lose!");
528       return NS_ERROR_NOT_INITIALIZED;
529     }
530 
531     return storageChild->AsyncClear(this);
532   }
533 
534   return hadData ? NS_OK : NS_SUCCESS_DOM_NO_OPERATION;
535 }
536 
GetOriginQuotaUsage(const LocalStorage * aStorage) const537 int64_t LocalStorageCache::GetOriginQuotaUsage(
538     const LocalStorage* aStorage) const {
539   return mData[GetDataSetIndex(aStorage)].mOriginQuotaUsage;
540 }
541 
UnloadItems(uint32_t aUnloadFlags)542 void LocalStorageCache::UnloadItems(uint32_t aUnloadFlags) {
543   if (aUnloadFlags & kUnloadDefault) {
544     // Must wait for preload to pass correct usage to ProcessUsageDelta
545     // XXX this is not technically needed right now since there is just
546     // per-origin isolated quota handling, but when we introduce super-
547     // -scope quotas, we have to do this.  Better to start getting
548     // telemetry right now.
549     WaitForPreload(Telemetry::LOCALDOMSTORAGE_UNLOAD_BLOCKING_MS);
550 
551     mData[kDefaultSet].mKeys.Clear();
552     ProcessUsageDelta(kDefaultSet, -mData[kDefaultSet].mOriginQuotaUsage);
553   }
554 
555   if (aUnloadFlags & kUnloadPrivate) {
556     mData[kPrivateSet].mKeys.Clear();
557     ProcessUsageDelta(kPrivateSet, -mData[kPrivateSet].mOriginQuotaUsage);
558   }
559 
560   if (aUnloadFlags & kUnloadSession) {
561     mData[kSessionSet].mKeys.Clear();
562     ProcessUsageDelta(kSessionSet, -mData[kSessionSet].mOriginQuotaUsage);
563     mSessionOnlyDataSetActive = false;
564   }
565 
566 #ifdef DOM_STORAGE_TESTS
567   if (aUnloadFlags & kTestReload) {
568     WaitForPreload(Telemetry::LOCALDOMSTORAGE_UNLOAD_BLOCKING_MS);
569 
570     mData[kDefaultSet].mKeys.Clear();
571     mLoaded = false;  // This is only used in testing code
572     Preload();
573   }
574 #endif
575 }
576 
577 // LocalStorageCacheBridge
578 
LoadedCount()579 uint32_t LocalStorageCache::LoadedCount() {
580   MonitorAutoLock monitor(mMonitor);
581   Data& data = mData[kDefaultSet];
582   return data.mKeys.Count();
583 }
584 
LoadItem(const nsAString & aKey,const nsString & aValue)585 bool LocalStorageCache::LoadItem(const nsAString& aKey,
586                                  const nsString& aValue) {
587   MonitorAutoLock monitor(mMonitor);
588   if (mLoaded) {
589     return false;
590   }
591 
592   Data& data = mData[kDefaultSet];
593   if (data.mKeys.Get(aKey, nullptr)) {
594     return true;  // don't stop, just don't override
595   }
596 
597   data.mKeys.Put(aKey, aValue);
598   data.mOriginQuotaUsage += aKey.Length() + aValue.Length();
599   return true;
600 }
601 
LoadDone(nsresult aRv)602 void LocalStorageCache::LoadDone(nsresult aRv) {
603   MonitorAutoLock monitor(mMonitor);
604   mLoadResult = aRv;
605   mLoaded = true;
606   monitor.Notify();
607 }
608 
LoadWait()609 void LocalStorageCache::LoadWait() {
610   MonitorAutoLock monitor(mMonitor);
611   while (!mLoaded) {
612     monitor.Wait();
613   }
614 }
615 
616 // StorageUsage
617 
StorageUsage(const nsACString & aOriginScope)618 StorageUsage::StorageUsage(const nsACString& aOriginScope)
619     : mOriginScope(aOriginScope) {
620   mUsage[kDefaultSet] = mUsage[kPrivateSet] = mUsage[kSessionSet] = 0LL;
621 }
622 
623 namespace {
624 
625 class LoadUsageRunnable : public Runnable {
626  public:
LoadUsageRunnable(int64_t * aUsage,const int64_t aDelta)627   LoadUsageRunnable(int64_t* aUsage, const int64_t aDelta)
628       : Runnable("dom::LoadUsageRunnable"), mTarget(aUsage), mDelta(aDelta) {}
629 
630  private:
631   int64_t* mTarget;
632   int64_t mDelta;
633 
Run()634   NS_IMETHOD Run() override {
635     *mTarget = mDelta;
636     return NS_OK;
637   }
638 };
639 
640 }  // namespace
641 
LoadUsage(const int64_t aUsage)642 void StorageUsage::LoadUsage(const int64_t aUsage) {
643   // Using kDefaultSet index since it is the index for the persitent data
644   // stored in the database we have just loaded usage for.
645   if (!NS_IsMainThread()) {
646     // In single process scenario we get this call from the DB thread
647     RefPtr<LoadUsageRunnable> r =
648         new LoadUsageRunnable(mUsage + kDefaultSet, aUsage);
649     NS_DispatchToMainThread(r);
650   } else {
651     // On a child process we get this on the main thread already
652     mUsage[kDefaultSet] += aUsage;
653   }
654 }
655 
CheckAndSetETLD1UsageDelta(uint32_t aDataSetIndex,const int64_t aDelta,const LocalStorageCache::MutationSource aSource)656 bool StorageUsage::CheckAndSetETLD1UsageDelta(
657     uint32_t aDataSetIndex, const int64_t aDelta,
658     const LocalStorageCache::MutationSource aSource) {
659   MOZ_ASSERT(NS_IsMainThread());
660 
661   int64_t newUsage = mUsage[aDataSetIndex] + aDelta;
662   if (aSource == LocalStorageCache::ContentMutation && aDelta > 0 &&
663       newUsage > LocalStorageManager::GetQuota()) {
664     return false;
665   }
666 
667   mUsage[aDataSetIndex] = newUsage;
668   return true;
669 }
670 
671 }  // namespace dom
672 }  // namespace mozilla
673