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