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