dom/src/storage/DOMStorageCache.cpp

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:80901c3fab7a
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6 #include "DOMStorageCache.h"
7
8 #include "DOMStorage.h"
9 #include "DOMStorageDBThread.h"
10 #include "DOMStorageIPC.h"
11 #include "DOMStorageManager.h"
12
13 #include "nsDOMString.h"
14 #include "nsXULAppAPI.h"
15 #include "mozilla/unused.h"
16 #include "nsProxyRelease.h"
17 #include "nsThreadUtils.h"
18
19 namespace mozilla {
20 namespace dom {
21
22 #define DOM_STORAGE_CACHE_KEEP_ALIVE_TIME_MS 20000
23
24 // static
25 DOMStorageDBBridge* DOMStorageCache::sDatabase = nullptr;
26 bool DOMStorageCache::sDatabaseDown = false;
27
28 namespace { // anon
29
30 const uint32_t kDefaultSet = 0;
31 const uint32_t kPrivateSet = 1;
32 const uint32_t kSessionSet = 2;
33
34 inline uint32_t
35 GetDataSetIndex(bool aPrivate, bool aSessionOnly)
36 {
37 if (aPrivate) {
38 return kPrivateSet;
39 }
40
41 if (aSessionOnly) {
42 return kSessionSet;
43 }
44
45 return kDefaultSet;
46 }
47
48 inline uint32_t
49 GetDataSetIndex(const DOMStorage* aStorage)
50 {
51 return GetDataSetIndex(aStorage->IsPrivate(), aStorage->IsSessionOnly());
52 }
53
54 } // anon
55
56 // DOMStorageCacheBridge
57
58 NS_IMPL_ADDREF(DOMStorageCacheBridge)
59
60 // Since there is no consumer of return value of Release, we can turn this
61 // method to void to make implementation of asynchronous DOMStorageCache::Release
62 // much simpler.
63 NS_IMETHODIMP_(void) DOMStorageCacheBridge::Release(void)
64 {
65 MOZ_ASSERT(int32_t(mRefCnt) > 0, "dup release");
66 nsrefcnt count = --mRefCnt;
67 NS_LOG_RELEASE(this, count, "DOMStorageCacheBridge");
68 if (0 == count) {
69 mRefCnt = 1; /* stabilize */
70 /* enable this to find non-threadsafe destructors: */
71 /* NS_ASSERT_OWNINGTHREAD(_class); */
72 delete (this);
73 }
74 }
75
76 // DOMStorageCache
77
78 DOMStorageCache::DOMStorageCache(const nsACString* aScope)
79 : mScope(*aScope)
80 , mMonitor("DOMStorageCache")
81 , mLoaded(false)
82 , mLoadResult(NS_OK)
83 , mInitialized(false)
84 , mPersistent(false)
85 , mSessionOnlyDataSetActive(false)
86 , mPreloadTelemetryRecorded(false)
87 {
88 MOZ_COUNT_CTOR(DOMStorageCache);
89 }
90
91 DOMStorageCache::~DOMStorageCache()
92 {
93 if (mManager) {
94 mManager->DropCache(this);
95 }
96
97 MOZ_COUNT_DTOR(DOMStorageCache);
98 }
99
100 NS_IMETHODIMP_(void)
101 DOMStorageCache::Release(void)
102 {
103 // We must actually release on the main thread since the cache removes it
104 // self from the manager's hash table. And we don't want to lock access to
105 // that hash table.
106 if (NS_IsMainThread()) {
107 DOMStorageCacheBridge::Release();
108 return;
109 }
110
111 nsRefPtr<nsRunnableMethod<DOMStorageCacheBridge, void, false> > event =
112 NS_NewNonOwningRunnableMethod(static_cast<DOMStorageCacheBridge*>(this),
113 &DOMStorageCacheBridge::Release);
114
115 nsresult rv = NS_DispatchToMainThread(event);
116 if (NS_FAILED(rv)) {
117 NS_WARNING("DOMStorageCache::Release() on a non-main thread");
118 DOMStorageCacheBridge::Release();
119 }
120 }
121
122 void
123 DOMStorageCache::Init(DOMStorageManager* aManager,
124 bool aPersistent,
125 nsIURI* aFirstPartyIsolationURI,
126 nsIPrincipal* aPrincipal,
127 const nsACString& aQuotaScope)
128 {
129 if (mInitialized) {
130 return;
131 }
132
133 mInitialized = true;
134 mFirstPartyIsolationURI = aFirstPartyIsolationURI;
135 mPrincipal = aPrincipal;
136 mPersistent = aPersistent;
137 mQuotaScope = aQuotaScope.IsEmpty() ? mScope : aQuotaScope;
138
139 if (mPersistent) {
140 mManager = aManager;
141 Preload();
142 }
143
144 mUsage = aManager->GetScopeUsage(mQuotaScope);
145 }
146
147 inline bool
148 DOMStorageCache::Persist(const DOMStorage* aStorage) const
149 {
150 return mPersistent &&
151 !aStorage->IsSessionOnly() &&
152 !aStorage->IsPrivate();
153 }
154
155 namespace { // anon
156
157 PLDHashOperator
158 CloneSetData(const nsAString& aKey, const nsString aValue, void* aArg)
159 {
160 DOMStorageCache::Data* target = static_cast<DOMStorageCache::Data*>(aArg);
161 target->mKeys.Put(aKey, aValue);
162
163 return PL_DHASH_NEXT;
164 }
165
166 } // anon
167
168 DOMStorageCache::Data&
169 DOMStorageCache::DataSet(const DOMStorage* aStorage)
170 {
171 uint32_t index = GetDataSetIndex(aStorage);
172
173 if (index == kSessionSet && !mSessionOnlyDataSetActive) {
174 // Session only data set is demanded but not filled with
175 // current data set, copy to session only set now.
176
177 WaitForPreload(Telemetry::LOCALDOMSTORAGE_SESSIONONLY_PRELOAD_BLOCKING_MS);
178
179 Data& defaultSet = mData[kDefaultSet];
180 Data& sessionSet = mData[kSessionSet];
181
182 defaultSet.mKeys.EnumerateRead(CloneSetData, &sessionSet);
183
184 mSessionOnlyDataSetActive = true;
185
186 // This updates sessionSet.mOriginQuotaUsage and also updates global usage
187 // for all session only data
188 ProcessUsageDelta(kSessionSet, defaultSet.mOriginQuotaUsage);
189 }
190
191 return mData[index];
192 }
193
194 bool
195 DOMStorageCache::ProcessUsageDelta(const DOMStorage* aStorage, int64_t aDelta)
196 {
197 return ProcessUsageDelta(GetDataSetIndex(aStorage), aDelta);
198 }
199
200 bool
201 DOMStorageCache::ProcessUsageDelta(uint32_t aGetDataSetIndex, const int64_t aDelta)
202 {
203 // Check if we are in a low disk space situation
204 if (aDelta > 0 && mManager && mManager->IsLowDiskSpace()) {
205 return false;
206 }
207
208 // Check limit per this origin
209 Data& data = mData[aGetDataSetIndex];
210 uint64_t newOriginUsage = data.mOriginQuotaUsage + aDelta;
211 if (aDelta > 0 && newOriginUsage > DOMStorageManager::GetQuota()) {
212 return false;
213 }
214
215 // Now check eTLD+1 limit
216 if (mUsage && !mUsage->CheckAndSetETLD1UsageDelta(aGetDataSetIndex, aDelta)) {
217 return false;
218 }
219
220 // Update size in our data set
221 data.mOriginQuotaUsage = newOriginUsage;
222 return true;
223 }
224
225 void
226 DOMStorageCache::Preload()
227 {
228 if (mLoaded || !mPersistent) {
229 return;
230 }
231
232 if (!StartDatabase()) {
233 mLoaded = true;
234 mLoadResult = NS_ERROR_FAILURE;
235 return;
236 }
237
238 sDatabase->AsyncPreload(this);
239 }
240
241 namespace { // anon
242
243 // This class is passed to timer as a tick observer. It refers the cache
244 // and keeps it alive for a time.
245 class DOMStorageCacheHolder : public nsITimerCallback
246 {
247 NS_DECL_ISUPPORTS
248
249 NS_IMETHODIMP
250 Notify(nsITimer* aTimer)
251 {
252 mCache = nullptr;
253 return NS_OK;
254 }
255
256 virtual ~DOMStorageCacheHolder() {}
257
258 nsRefPtr<DOMStorageCache> mCache;
259
260 public:
261 DOMStorageCacheHolder(DOMStorageCache* aCache) : mCache(aCache) {}
262 };
263
264 NS_IMPL_ISUPPORTS(DOMStorageCacheHolder, nsITimerCallback)
265
266 } // anon
267
268 void
269 DOMStorageCache::KeepAlive()
270 {
271 // Missing reference back to the manager means the cache is not responsible
272 // for its lifetime. Used for keeping sessionStorage live forever.
273 if (!mManager) {
274 return;
275 }
276
277 if (!NS_IsMainThread()) {
278 // Timer and the holder must be initialized on the main thread.
279 nsRefPtr<nsRunnableMethod<DOMStorageCache> > event =
280 NS_NewRunnableMethod(this, &DOMStorageCache::KeepAlive);
281
282 NS_DispatchToMainThread(event);
283 return;
284 }
285
286 nsCOMPtr<nsITimer> timer = do_CreateInstance("@mozilla.org/timer;1");
287 if (!timer) {
288 return;
289 }
290
291 nsRefPtr<DOMStorageCacheHolder> holder = new DOMStorageCacheHolder(this);
292 timer->InitWithCallback(holder, DOM_STORAGE_CACHE_KEEP_ALIVE_TIME_MS,
293 nsITimer::TYPE_ONE_SHOT);
294
295 mKeepAliveTimer.swap(timer);
296 }
297
298 namespace { // anon
299
300 // The AutoTimer provided by telemetry headers is only using static,
301 // i.e. compile time known ID, but here we know the ID only at run time.
302 // Hence a new class.
303 class TelemetryAutoTimer
304 {
305 public:
306 TelemetryAutoTimer(Telemetry::ID aId)
307 : id(aId), start(TimeStamp::Now()) {}
308 ~TelemetryAutoTimer()
309 { Telemetry::AccumulateDelta_impl<Telemetry::Millisecond>::compute(id, start); }
310 private:
311 Telemetry::ID id;
312 const TimeStamp start;
313 };
314
315 } // anon
316
317 void
318 DOMStorageCache::WaitForPreload(Telemetry::ID aTelemetryID)
319 {
320 if (!mPersistent) {
321 return;
322 }
323
324 bool loaded = mLoaded;
325
326 // Telemetry of rates of pending preloads
327 if (!mPreloadTelemetryRecorded) {
328 mPreloadTelemetryRecorded = true;
329 Telemetry::Accumulate(
330 Telemetry::LOCALDOMSTORAGE_PRELOAD_PENDING_ON_FIRST_ACCESS,
331 !loaded);
332 }
333
334 if (loaded) {
335 return;
336 }
337
338 // Measure which operation blocks and for how long
339 TelemetryAutoTimer timer(aTelemetryID);
340
341 // If preload already started (i.e. we got some first data, but not all)
342 // SyncPreload will just wait for it to finish rather then synchronously
343 // read from the database. It seems to me more optimal.
344
345 // TODO place for A/B testing (force main thread load vs. let preload finish)
346
347 // No need to check sDatabase for being non-null since preload is either
348 // done before we've shut the DB down or when the DB could not start,
349 // preload has not even be started.
350 sDatabase->SyncPreload(this);
351 }
352
353 nsresult
354 DOMStorageCache::GetLength(const DOMStorage* aStorage, uint32_t* aRetval)
355 {
356 Telemetry::AutoTimer<Telemetry::LOCALDOMSTORAGE_GETLENGTH_MS> autoTimer;
357
358 if (Persist(aStorage)) {
359 WaitForPreload(Telemetry::LOCALDOMSTORAGE_GETLENGTH_BLOCKING_MS);
360 if (NS_FAILED(mLoadResult)) {
361 return mLoadResult;
362 }
363 }
364
365 *aRetval = DataSet(aStorage).mKeys.Count();
366 return NS_OK;
367 }
368
369 namespace { // anon
370
371 class IndexFinderData
372 {
373 public:
374 IndexFinderData(uint32_t aIndex, nsAString& aRetval)
375 : mIndex(aIndex), mKey(aRetval)
376 {
377 mKey.SetIsVoid(true);
378 }
379
380 uint32_t mIndex;
381 nsAString& mKey;
382 };
383
384 PLDHashOperator
385 FindKeyOrder(const nsAString& aKey, const nsString aValue, void* aArg)
386 {
387 IndexFinderData* data = static_cast<IndexFinderData*>(aArg);
388
389 if (data->mIndex--) {
390 return PL_DHASH_NEXT;
391 }
392
393 data->mKey = aKey;
394 return PL_DHASH_STOP;
395 }
396
397 } // anon
398
399 nsresult
400 DOMStorageCache::GetKey(const DOMStorage* aStorage, uint32_t aIndex, nsAString& aRetval)
401 {
402 // XXX: This does a linear search for the key at index, which would
403 // suck if there's a large numer of indexes. Do we care? If so,
404 // maybe we need to have a lazily populated key array here or
405 // something?
406 Telemetry::AutoTimer<Telemetry::LOCALDOMSTORAGE_GETKEY_MS> autoTimer;
407
408 if (Persist(aStorage)) {
409 WaitForPreload(Telemetry::LOCALDOMSTORAGE_GETKEY_BLOCKING_MS);
410 if (NS_FAILED(mLoadResult)) {
411 return mLoadResult;
412 }
413 }
414
415 IndexFinderData data(aIndex, aRetval);
416 DataSet(aStorage).mKeys.EnumerateRead(FindKeyOrder, &data);
417 return NS_OK;
418 }
419
420 namespace { // anon
421
422 static PLDHashOperator
423 KeysArrayBuilder(const nsAString& aKey, const nsString aValue, void* aArg)
424 {
425 nsTArray<nsString>* keys = static_cast<nsTArray<nsString>* >(aArg);
426
427 keys->AppendElement(aKey);
428 return PL_DHASH_NEXT;
429 }
430
431 } // anon
432
433 nsTArray<nsString>*
434 DOMStorageCache::GetKeys(const DOMStorage* aStorage)
435 {
436 Telemetry::AutoTimer<Telemetry::LOCALDOMSTORAGE_GETALLKEYS_MS> autoTimer;
437
438 if (Persist(aStorage)) {
439 WaitForPreload(Telemetry::LOCALDOMSTORAGE_GETALLKEYS_BLOCKING_MS);
440 }
441
442 nsTArray<nsString>* result = new nsTArray<nsString>();
443 if (NS_SUCCEEDED(mLoadResult)) {
444 DataSet(aStorage).mKeys.EnumerateRead(KeysArrayBuilder, result);
445 }
446
447 return result;
448 }
449
450 nsresult
451 DOMStorageCache::GetItem(const DOMStorage* aStorage, const nsAString& aKey,
452 nsAString& aRetval)
453 {
454 Telemetry::AutoTimer<Telemetry::LOCALDOMSTORAGE_GETVALUE_MS> autoTimer;
455
456 if (Persist(aStorage)) {
457 WaitForPreload(Telemetry::LOCALDOMSTORAGE_GETVALUE_BLOCKING_MS);
458 if (NS_FAILED(mLoadResult)) {
459 return mLoadResult;
460 }
461 }
462
463 // not using AutoString since we don't want to copy buffer to result
464 nsString value;
465 if (!DataSet(aStorage).mKeys.Get(aKey, &value)) {
466 SetDOMStringToNull(value);
467 }
468
469 aRetval = value;
470
471 return NS_OK;
472 }
473
474 nsresult
475 DOMStorageCache::SetItem(const DOMStorage* aStorage, const nsAString& aKey,
476 const nsString& aValue, nsString& aOld)
477 {
478 Telemetry::AutoTimer<Telemetry::LOCALDOMSTORAGE_SETVALUE_MS> autoTimer;
479
480 if (Persist(aStorage)) {
481 WaitForPreload(Telemetry::LOCALDOMSTORAGE_SETVALUE_BLOCKING_MS);
482 if (NS_FAILED(mLoadResult)) {
483 return mLoadResult;
484 }
485 }
486
487 Data& data = DataSet(aStorage);
488 if (!data.mKeys.Get(aKey, &aOld)) {
489 SetDOMStringToNull(aOld);
490 }
491
492 // Check the quota first
493 const int64_t delta = static_cast<int64_t>(aValue.Length()) -
494 static_cast<int64_t>(aOld.Length());
495 if (!ProcessUsageDelta(aStorage, delta)) {
496 return NS_ERROR_DOM_QUOTA_REACHED;
497 }
498
499 if (aValue == aOld && DOMStringIsNull(aValue) == DOMStringIsNull(aOld)) {
500 return NS_SUCCESS_DOM_NO_OPERATION;
501 }
502
503 data.mKeys.Put(aKey, aValue);
504
505 if (Persist(aStorage)) {
506 if (!sDatabase) {
507 NS_ERROR("Writing to localStorage after the database has been shut down"
508 ", data lose!");
509 return NS_ERROR_NOT_INITIALIZED;
510 }
511
512 if (DOMStringIsNull(aOld)) {
513 return sDatabase->AsyncAddItem(this, aKey, aValue);
514 }
515
516 return sDatabase->AsyncUpdateItem(this, aKey, aValue);
517 }
518
519 return NS_OK;
520 }
521
522 nsresult
523 DOMStorageCache::RemoveItem(const DOMStorage* aStorage, const nsAString& aKey,
524 nsString& aOld)
525 {
526 Telemetry::AutoTimer<Telemetry::LOCALDOMSTORAGE_REMOVEKEY_MS> autoTimer;
527
528 if (Persist(aStorage)) {
529 WaitForPreload(Telemetry::LOCALDOMSTORAGE_REMOVEKEY_BLOCKING_MS);
530 if (NS_FAILED(mLoadResult)) {
531 return mLoadResult;
532 }
533 }
534
535 Data& data = DataSet(aStorage);
536 if (!data.mKeys.Get(aKey, &aOld)) {
537 SetDOMStringToNull(aOld);
538 return NS_SUCCESS_DOM_NO_OPERATION;
539 }
540
541 // Recalculate the cached data size
542 const int64_t delta = -(static_cast<int64_t>(aOld.Length()));
543 unused << ProcessUsageDelta(aStorage, delta);
544 data.mKeys.Remove(aKey);
545
546 if (Persist(aStorage)) {
547 if (!sDatabase) {
548 NS_ERROR("Writing to localStorage after the database has been shut down"
549 ", data lose!");
550 return NS_ERROR_NOT_INITIALIZED;
551 }
552
553 return sDatabase->AsyncRemoveItem(this, aKey);
554 }
555
556 return NS_OK;
557 }
558
559 nsresult
560 DOMStorageCache::Clear(const DOMStorage* aStorage)
561 {
562 Telemetry::AutoTimer<Telemetry::LOCALDOMSTORAGE_CLEAR_MS> autoTimer;
563
564 bool refresh = false;
565 if (Persist(aStorage)) {
566 // We need to preload all data (know the size) before we can proceeed
567 // to correctly decrease cached usage number.
568 // XXX as in case of unload, this is not technically needed now, but
569 // after super-scope quota introduction we have to do this. Get telemetry
570 // right now.
571 WaitForPreload(Telemetry::LOCALDOMSTORAGE_CLEAR_BLOCKING_MS);
572 if (NS_FAILED(mLoadResult)) {
573 // When we failed to load data from the database, force delete of the
574 // scope data and make use of the storage possible again.
575 refresh = true;
576 mLoadResult = NS_OK;
577 }
578 }
579
580 Data& data = DataSet(aStorage);
581 bool hadData = !!data.mKeys.Count();
582
583 if (hadData) {
584 unused << ProcessUsageDelta(aStorage, -data.mOriginQuotaUsage);
585 data.mKeys.Clear();
586 }
587
588 if (Persist(aStorage) && (refresh || hadData)) {
589 if (!sDatabase) {
590 NS_ERROR("Writing to localStorage after the database has been shut down"
591 ", data lose!");
592 return NS_ERROR_NOT_INITIALIZED;
593 }
594
595 return sDatabase->AsyncClear(this);
596 }
597
598 return hadData ? NS_OK : NS_SUCCESS_DOM_NO_OPERATION;
599 }
600
601 void
602 DOMStorageCache::CloneFrom(const DOMStorageCache* aThat)
603 {
604 mLoaded = aThat->mLoaded;
605 mInitialized = aThat->mInitialized;
606 mPersistent = aThat->mPersistent;
607 mSessionOnlyDataSetActive = aThat->mSessionOnlyDataSetActive;
608
609 for (uint32_t i = 0; i < kDataSetCount; ++i) {
610 aThat->mData[i].mKeys.EnumerateRead(CloneSetData, &mData[i]);
611 ProcessUsageDelta(i, aThat->mData[i].mOriginQuotaUsage);
612 }
613 }
614
615 // Defined in DOMStorageManager.cpp
616 extern bool
617 PrincipalsEqual(nsIPrincipal* aObjectPrincipal, nsIPrincipal* aSubjectPrincipal);
618
619 bool
620 DOMStorageCache::CheckPrincipal(nsIPrincipal* aPrincipal) const
621 {
622 return PrincipalsEqual(mPrincipal, aPrincipal);
623 }
624
625 void
626 DOMStorageCache::UnloadItems(uint32_t aUnloadFlags)
627 {
628 if (aUnloadFlags & kUnloadDefault) {
629 // Must wait for preload to pass correct usage to ProcessUsageDelta
630 // XXX this is not technically needed right now since there is just
631 // per-origin isolated quota handling, but when we introduce super-
632 // -scope quotas, we have to do this. Better to start getting
633 // telemetry right now.
634 WaitForPreload(Telemetry::LOCALDOMSTORAGE_UNLOAD_BLOCKING_MS);
635
636 mData[kDefaultSet].mKeys.Clear();
637 ProcessUsageDelta(kDefaultSet, -mData[kDefaultSet].mOriginQuotaUsage);
638 }
639
640 if (aUnloadFlags & kUnloadPrivate) {
641 mData[kPrivateSet].mKeys.Clear();
642 ProcessUsageDelta(kPrivateSet, -mData[kPrivateSet].mOriginQuotaUsage);
643 }
644
645 if (aUnloadFlags & kUnloadSession) {
646 mData[kSessionSet].mKeys.Clear();
647 ProcessUsageDelta(kSessionSet, -mData[kSessionSet].mOriginQuotaUsage);
648 mSessionOnlyDataSetActive = false;
649 }
650
651 #ifdef DOM_STORAGE_TESTS
652 if (aUnloadFlags & kTestReload) {
653 WaitForPreload(Telemetry::LOCALDOMSTORAGE_UNLOAD_BLOCKING_MS);
654
655 mData[kDefaultSet].mKeys.Clear();
656 mLoaded = false; // This is only used in testing code
657 Preload();
658 }
659 #endif
660 }
661
662 // DOMStorageCacheBridge
663
664 uint32_t
665 DOMStorageCache::LoadedCount()
666 {
667 MonitorAutoLock monitor(mMonitor);
668 Data& data = mData[kDefaultSet];
669 return data.mKeys.Count();
670 }
671
672 bool
673 DOMStorageCache::LoadItem(const nsAString& aKey, const nsString& aValue)
674 {
675 MonitorAutoLock monitor(mMonitor);
676 if (mLoaded) {
677 return false;
678 }
679
680 Data& data = mData[kDefaultSet];
681 if (data.mKeys.Get(aKey, nullptr)) {
682 return true; // don't stop, just don't override
683 }
684
685 data.mKeys.Put(aKey, aValue);
686 data.mOriginQuotaUsage += aKey.Length() + aValue.Length();
687 return true;
688 }
689
690 void
691 DOMStorageCache::LoadDone(nsresult aRv)
692 {
693 // Keep the preloaded cache alive for a time
694 KeepAlive();
695
696 MonitorAutoLock monitor(mMonitor);
697 mLoadResult = aRv;
698 mLoaded = true;
699 monitor.Notify();
700 }
701
702 void
703 DOMStorageCache::LoadWait()
704 {
705 MonitorAutoLock monitor(mMonitor);
706 while (!mLoaded) {
707 monitor.Wait();
708 }
709 }
710
711 // DOMStorageUsage
712
713 DOMStorageUsage::DOMStorageUsage(const nsACString& aScope)
714 : mScope(aScope)
715 {
716 mUsage[kDefaultSet] = mUsage[kPrivateSet] = mUsage[kSessionSet] = 0LL;
717 }
718
719 namespace { // anon
720
721 class LoadUsageRunnable : public nsRunnable
722 {
723 public:
724 LoadUsageRunnable(int64_t* aUsage, const int64_t aDelta)
725 : mTarget(aUsage)
726 , mDelta(aDelta)
727 {}
728
729 private:
730 int64_t* mTarget;
731 int64_t mDelta;
732
733 NS_IMETHOD Run() { *mTarget = mDelta; return NS_OK; }
734 };
735
736 } // anon
737
738 void
739 DOMStorageUsage::LoadUsage(const int64_t aUsage)
740 {
741 // Using kDefaultSet index since it is the index for the persitent data
742 // stored in the database we have just loaded usage for.
743 if (!NS_IsMainThread()) {
744 // In single process scenario we get this call from the DB thread
745 nsRefPtr<LoadUsageRunnable> r =
746 new LoadUsageRunnable(mUsage + kDefaultSet, aUsage);
747 NS_DispatchToMainThread(r);
748 } else {
749 // On a child process we get this on the main thread already
750 mUsage[kDefaultSet] += aUsage;
751 }
752 }
753
754 bool
755 DOMStorageUsage::CheckAndSetETLD1UsageDelta(uint32_t aDataSetIndex, const int64_t aDelta)
756 {
757 MOZ_ASSERT(NS_IsMainThread());
758
759 int64_t newUsage = mUsage[aDataSetIndex] + aDelta;
760 if (aDelta > 0 && newUsage > DOMStorageManager::GetQuota()) {
761 return false;
762 }
763
764 mUsage[aDataSetIndex] = newUsage;
765 return true;
766 }
767
768
769 // static
770 DOMStorageDBBridge*
771 DOMStorageCache::StartDatabase()
772 {
773 if (sDatabase || sDatabaseDown) {
774 // When sDatabaseDown is at true, sDatabase is null.
775 // Checking sDatabaseDown flag here prevents reinitialization of
776 // the database after shutdown.
777 return sDatabase;
778 }
779
780 if (XRE_GetProcessType() == GeckoProcessType_Default) {
781 nsAutoPtr<DOMStorageDBThread> db(new DOMStorageDBThread());
782
783 nsresult rv = db->Init();
784 if (NS_FAILED(rv)) {
785 return nullptr;
786 }
787
788 sDatabase = db.forget();
789 } else {
790 nsRefPtr<DOMStorageDBChild> db = new DOMStorageDBChild(
791 DOMLocalStorageManager::Self());
792
793 nsresult rv = db->Init();
794 if (NS_FAILED(rv)) {
795 return nullptr;
796 }
797
798 db.forget(&sDatabase);
799 }
800
801 return sDatabase;
802 }
803
804 // static
805 DOMStorageDBBridge*
806 DOMStorageCache::GetDatabase()
807 {
808 return sDatabase;
809 }
810
811 // static
812 nsresult
813 DOMStorageCache::StopDatabase()
814 {
815 if (!sDatabase) {
816 return NS_OK;
817 }
818
819 sDatabaseDown = true;
820
821 nsresult rv = sDatabase->Shutdown();
822 if (XRE_GetProcessType() == GeckoProcessType_Default) {
823 delete sDatabase;
824 } else {
825 DOMStorageDBChild* child = static_cast<DOMStorageDBChild*>(sDatabase);
826 NS_RELEASE(child);
827 }
828
829 sDatabase = nullptr;
830 return rv;
831 }
832
833 } // ::dom
834 } // ::mozilla

mercurial