LCOV - code coverage report
Current view: top level - dom/storage - LocalStorageCache.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 63 307 20.5 %
Date: 2017-07-14 16:53:18 Functions: 11 41 26.8 %
Legend: Lines: hit not hit

          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

Generated by: LCOV version 1.13