dom/src/storage/DOMStorageCache.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     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/. */
     6 #include "DOMStorageCache.h"
     8 #include "DOMStorage.h"
     9 #include "DOMStorageDBThread.h"
    10 #include "DOMStorageIPC.h"
    11 #include "DOMStorageManager.h"
    13 #include "nsDOMString.h"
    14 #include "nsXULAppAPI.h"
    15 #include "mozilla/unused.h"
    16 #include "nsProxyRelease.h"
    17 #include "nsThreadUtils.h"
    19 namespace mozilla {
    20 namespace dom {
    22 #define DOM_STORAGE_CACHE_KEEP_ALIVE_TIME_MS 20000
    24 // static
    25 DOMStorageDBBridge* DOMStorageCache::sDatabase = nullptr;
    26 bool DOMStorageCache::sDatabaseDown = false;
    28 namespace { // anon
    30 const uint32_t kDefaultSet = 0;
    31 const uint32_t kPrivateSet = 1;
    32 const uint32_t kSessionSet = 2;
    34 inline uint32_t
    35 GetDataSetIndex(bool aPrivate, bool aSessionOnly)
    36 {
    37   if (aPrivate) {
    38     return kPrivateSet;
    39   }
    41   if (aSessionOnly) {
    42     return kSessionSet;
    43   }
    45   return kDefaultSet;
    46 }
    48 inline uint32_t
    49 GetDataSetIndex(const DOMStorage* aStorage)
    50 {
    51   return GetDataSetIndex(aStorage->IsPrivate(), aStorage->IsSessionOnly());
    52 }
    54 } // anon
    56 // DOMStorageCacheBridge
    58 NS_IMPL_ADDREF(DOMStorageCacheBridge)
    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 }
    76 // DOMStorageCache
    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 }
    91 DOMStorageCache::~DOMStorageCache()
    92 {
    93   if (mManager) {
    94     mManager->DropCache(this);
    95   }
    97   MOZ_COUNT_DTOR(DOMStorageCache);
    98 }
   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   }
   111   nsRefPtr<nsRunnableMethod<DOMStorageCacheBridge, void, false> > event =
   112     NS_NewNonOwningRunnableMethod(static_cast<DOMStorageCacheBridge*>(this),
   113                                   &DOMStorageCacheBridge::Release);
   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 }
   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   }
   133   mInitialized = true;
   134   mFirstPartyIsolationURI = aFirstPartyIsolationURI;
   135   mPrincipal = aPrincipal;
   136   mPersistent = aPersistent;
   137   mQuotaScope = aQuotaScope.IsEmpty() ? mScope : aQuotaScope;
   139   if (mPersistent) {
   140     mManager = aManager;
   141     Preload();
   142   }
   144   mUsage = aManager->GetScopeUsage(mQuotaScope);
   145 }
   147 inline bool
   148 DOMStorageCache::Persist(const DOMStorage* aStorage) const
   149 {
   150   return mPersistent &&
   151          !aStorage->IsSessionOnly() &&
   152          !aStorage->IsPrivate();
   153 }
   155 namespace { // anon
   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);
   163   return PL_DHASH_NEXT;
   164 }
   166 } // anon
   168 DOMStorageCache::Data&
   169 DOMStorageCache::DataSet(const DOMStorage* aStorage)
   170 {
   171   uint32_t index = GetDataSetIndex(aStorage);
   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.
   177     WaitForPreload(Telemetry::LOCALDOMSTORAGE_SESSIONONLY_PRELOAD_BLOCKING_MS);
   179     Data& defaultSet = mData[kDefaultSet];
   180     Data& sessionSet = mData[kSessionSet];
   182     defaultSet.mKeys.EnumerateRead(CloneSetData, &sessionSet);
   184     mSessionOnlyDataSetActive = true;
   186     // This updates sessionSet.mOriginQuotaUsage and also updates global usage
   187     // for all session only data
   188     ProcessUsageDelta(kSessionSet, defaultSet.mOriginQuotaUsage);
   189   }
   191   return mData[index];
   192 }
   194 bool
   195 DOMStorageCache::ProcessUsageDelta(const DOMStorage* aStorage, int64_t aDelta)
   196 {
   197   return ProcessUsageDelta(GetDataSetIndex(aStorage), aDelta);
   198 }
   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   }
   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   }
   215   // Now check eTLD+1 limit
   216   if (mUsage && !mUsage->CheckAndSetETLD1UsageDelta(aGetDataSetIndex, aDelta)) {
   217     return false;
   218   }
   220   // Update size in our data set
   221   data.mOriginQuotaUsage = newOriginUsage;
   222   return true;
   223 }
   225 void
   226 DOMStorageCache::Preload()
   227 {
   228   if (mLoaded || !mPersistent) {
   229     return;
   230   }
   232   if (!StartDatabase()) {
   233     mLoaded = true;
   234     mLoadResult = NS_ERROR_FAILURE;
   235     return;
   236   }
   238   sDatabase->AsyncPreload(this);
   239 }
   241 namespace { // anon
   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
   249   NS_IMETHODIMP
   250   Notify(nsITimer* aTimer)
   251   {
   252     mCache = nullptr;
   253     return NS_OK;
   254   }
   256   virtual ~DOMStorageCacheHolder() {}
   258   nsRefPtr<DOMStorageCache> mCache;
   260 public:
   261   DOMStorageCacheHolder(DOMStorageCache* aCache) : mCache(aCache) {}
   262 };
   264 NS_IMPL_ISUPPORTS(DOMStorageCacheHolder, nsITimerCallback)
   266 } // anon
   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   }
   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);
   282     NS_DispatchToMainThread(event);
   283     return;
   284   }
   286   nsCOMPtr<nsITimer> timer = do_CreateInstance("@mozilla.org/timer;1");
   287   if (!timer) {
   288     return;
   289   }
   291   nsRefPtr<DOMStorageCacheHolder> holder = new DOMStorageCacheHolder(this);
   292   timer->InitWithCallback(holder, DOM_STORAGE_CACHE_KEEP_ALIVE_TIME_MS,
   293                           nsITimer::TYPE_ONE_SHOT);
   295   mKeepAliveTimer.swap(timer);
   296 }
   298 namespace { // anon
   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 };
   315 } // anon
   317 void
   318 DOMStorageCache::WaitForPreload(Telemetry::ID aTelemetryID)
   319 {
   320   if (!mPersistent) {
   321     return;
   322   }
   324   bool loaded = mLoaded;
   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   }
   334   if (loaded) {
   335     return;
   336   }
   338   // Measure which operation blocks and for how long
   339   TelemetryAutoTimer timer(aTelemetryID);
   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.
   345   // TODO place for A/B testing (force main thread load vs. let preload finish)
   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 }
   353 nsresult
   354 DOMStorageCache::GetLength(const DOMStorage* aStorage, uint32_t* aRetval)
   355 {
   356   Telemetry::AutoTimer<Telemetry::LOCALDOMSTORAGE_GETLENGTH_MS> autoTimer;
   358   if (Persist(aStorage)) {
   359     WaitForPreload(Telemetry::LOCALDOMSTORAGE_GETLENGTH_BLOCKING_MS);
   360     if (NS_FAILED(mLoadResult)) {
   361       return mLoadResult;
   362     }
   363   }
   365   *aRetval = DataSet(aStorage).mKeys.Count();
   366   return NS_OK;
   367 }
   369 namespace { // anon
   371 class IndexFinderData
   372 {
   373 public:
   374   IndexFinderData(uint32_t aIndex, nsAString& aRetval)
   375     : mIndex(aIndex), mKey(aRetval)
   376   {
   377     mKey.SetIsVoid(true);
   378   }
   380   uint32_t mIndex;
   381   nsAString& mKey;
   382 };
   384 PLDHashOperator
   385 FindKeyOrder(const nsAString& aKey, const nsString aValue, void* aArg)
   386 {
   387   IndexFinderData* data = static_cast<IndexFinderData*>(aArg);
   389   if (data->mIndex--) {
   390     return PL_DHASH_NEXT;
   391   }
   393   data->mKey = aKey;
   394   return PL_DHASH_STOP;
   395 }
   397 } // anon
   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;
   408   if (Persist(aStorage)) {
   409     WaitForPreload(Telemetry::LOCALDOMSTORAGE_GETKEY_BLOCKING_MS);
   410     if (NS_FAILED(mLoadResult)) {
   411       return mLoadResult;
   412     }
   413   }
   415   IndexFinderData data(aIndex, aRetval);
   416   DataSet(aStorage).mKeys.EnumerateRead(FindKeyOrder, &data);
   417   return NS_OK;
   418 }
   420 namespace { // anon
   422 static PLDHashOperator
   423 KeysArrayBuilder(const nsAString& aKey, const nsString aValue, void* aArg)
   424 {
   425   nsTArray<nsString>* keys = static_cast<nsTArray<nsString>* >(aArg);
   427   keys->AppendElement(aKey);
   428   return PL_DHASH_NEXT;
   429 }
   431 } // anon
   433 nsTArray<nsString>*
   434 DOMStorageCache::GetKeys(const DOMStorage* aStorage)
   435 {
   436   Telemetry::AutoTimer<Telemetry::LOCALDOMSTORAGE_GETALLKEYS_MS> autoTimer;
   438   if (Persist(aStorage)) {
   439     WaitForPreload(Telemetry::LOCALDOMSTORAGE_GETALLKEYS_BLOCKING_MS);
   440   }
   442   nsTArray<nsString>* result = new nsTArray<nsString>();
   443   if (NS_SUCCEEDED(mLoadResult)) {
   444     DataSet(aStorage).mKeys.EnumerateRead(KeysArrayBuilder, result);
   445   }
   447   return result;
   448 }
   450 nsresult
   451 DOMStorageCache::GetItem(const DOMStorage* aStorage, const nsAString& aKey,
   452                          nsAString& aRetval)
   453 {
   454   Telemetry::AutoTimer<Telemetry::LOCALDOMSTORAGE_GETVALUE_MS> autoTimer;
   456   if (Persist(aStorage)) {
   457     WaitForPreload(Telemetry::LOCALDOMSTORAGE_GETVALUE_BLOCKING_MS);
   458     if (NS_FAILED(mLoadResult)) {
   459       return mLoadResult;
   460     }
   461   }
   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   }
   469   aRetval = value;
   471   return NS_OK;
   472 }
   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;
   480   if (Persist(aStorage)) {
   481     WaitForPreload(Telemetry::LOCALDOMSTORAGE_SETVALUE_BLOCKING_MS);
   482     if (NS_FAILED(mLoadResult)) {
   483       return mLoadResult;
   484     }
   485   }
   487   Data& data = DataSet(aStorage);
   488   if (!data.mKeys.Get(aKey, &aOld)) {
   489     SetDOMStringToNull(aOld);
   490   }
   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   }
   499   if (aValue == aOld && DOMStringIsNull(aValue) == DOMStringIsNull(aOld)) {
   500     return NS_SUCCESS_DOM_NO_OPERATION;
   501   }
   503   data.mKeys.Put(aKey, aValue);
   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     }
   512     if (DOMStringIsNull(aOld)) {
   513       return sDatabase->AsyncAddItem(this, aKey, aValue);
   514     }
   516     return sDatabase->AsyncUpdateItem(this, aKey, aValue);
   517   }
   519   return NS_OK;
   520 }
   522 nsresult
   523 DOMStorageCache::RemoveItem(const DOMStorage* aStorage, const nsAString& aKey,
   524                             nsString& aOld)
   525 {
   526   Telemetry::AutoTimer<Telemetry::LOCALDOMSTORAGE_REMOVEKEY_MS> autoTimer;
   528   if (Persist(aStorage)) {
   529     WaitForPreload(Telemetry::LOCALDOMSTORAGE_REMOVEKEY_BLOCKING_MS);
   530     if (NS_FAILED(mLoadResult)) {
   531       return mLoadResult;
   532     }
   533   }
   535   Data& data = DataSet(aStorage);
   536   if (!data.mKeys.Get(aKey, &aOld)) {
   537     SetDOMStringToNull(aOld);
   538     return NS_SUCCESS_DOM_NO_OPERATION;
   539   }
   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);
   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     }
   553     return sDatabase->AsyncRemoveItem(this, aKey);
   554   }
   556   return NS_OK;
   557 }
   559 nsresult
   560 DOMStorageCache::Clear(const DOMStorage* aStorage)
   561 {
   562   Telemetry::AutoTimer<Telemetry::LOCALDOMSTORAGE_CLEAR_MS> autoTimer;
   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   }
   580   Data& data = DataSet(aStorage);
   581   bool hadData = !!data.mKeys.Count();
   583   if (hadData) {
   584     unused << ProcessUsageDelta(aStorage, -data.mOriginQuotaUsage);
   585     data.mKeys.Clear();
   586   }
   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     }
   595     return sDatabase->AsyncClear(this);
   596   }
   598   return hadData ? NS_OK : NS_SUCCESS_DOM_NO_OPERATION;
   599 }
   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;
   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 }
   615 // Defined in DOMStorageManager.cpp
   616 extern bool
   617 PrincipalsEqual(nsIPrincipal* aObjectPrincipal, nsIPrincipal* aSubjectPrincipal);
   619 bool
   620 DOMStorageCache::CheckPrincipal(nsIPrincipal* aPrincipal) const
   621 {
   622   return PrincipalsEqual(mPrincipal, aPrincipal);
   623 }
   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);
   636     mData[kDefaultSet].mKeys.Clear();
   637     ProcessUsageDelta(kDefaultSet, -mData[kDefaultSet].mOriginQuotaUsage);
   638   }
   640   if (aUnloadFlags & kUnloadPrivate) {
   641     mData[kPrivateSet].mKeys.Clear();
   642     ProcessUsageDelta(kPrivateSet, -mData[kPrivateSet].mOriginQuotaUsage);
   643   }
   645   if (aUnloadFlags & kUnloadSession) {
   646     mData[kSessionSet].mKeys.Clear();
   647     ProcessUsageDelta(kSessionSet, -mData[kSessionSet].mOriginQuotaUsage);
   648     mSessionOnlyDataSetActive = false;
   649   }
   651 #ifdef DOM_STORAGE_TESTS
   652   if (aUnloadFlags & kTestReload) {
   653     WaitForPreload(Telemetry::LOCALDOMSTORAGE_UNLOAD_BLOCKING_MS);
   655     mData[kDefaultSet].mKeys.Clear();
   656     mLoaded = false; // This is only used in testing code
   657     Preload();
   658   }
   659 #endif
   660 }
   662 // DOMStorageCacheBridge
   664 uint32_t
   665 DOMStorageCache::LoadedCount()
   666 {
   667   MonitorAutoLock monitor(mMonitor);
   668   Data& data = mData[kDefaultSet];
   669   return data.mKeys.Count();
   670 }
   672 bool
   673 DOMStorageCache::LoadItem(const nsAString& aKey, const nsString& aValue)
   674 {
   675   MonitorAutoLock monitor(mMonitor);
   676   if (mLoaded) {
   677     return false;
   678   }
   680   Data& data = mData[kDefaultSet];
   681   if (data.mKeys.Get(aKey, nullptr)) {
   682     return true; // don't stop, just don't override
   683   }
   685   data.mKeys.Put(aKey, aValue);
   686   data.mOriginQuotaUsage += aKey.Length() + aValue.Length();
   687   return true;
   688 }
   690 void
   691 DOMStorageCache::LoadDone(nsresult aRv)
   692 {
   693   // Keep the preloaded cache alive for a time
   694   KeepAlive();
   696   MonitorAutoLock monitor(mMonitor);
   697   mLoadResult = aRv;
   698   mLoaded = true;
   699   monitor.Notify();
   700 }
   702 void
   703 DOMStorageCache::LoadWait()
   704 {
   705   MonitorAutoLock monitor(mMonitor);
   706   while (!mLoaded) {
   707     monitor.Wait();
   708   }
   709 }
   711 // DOMStorageUsage
   713 DOMStorageUsage::DOMStorageUsage(const nsACString& aScope)
   714   : mScope(aScope)
   715 {
   716   mUsage[kDefaultSet] = mUsage[kPrivateSet] = mUsage[kSessionSet] = 0LL;
   717 }
   719 namespace { // anon
   721 class LoadUsageRunnable : public nsRunnable
   722 {
   723 public:
   724   LoadUsageRunnable(int64_t* aUsage, const int64_t aDelta)
   725     : mTarget(aUsage)
   726     , mDelta(aDelta)
   727   {}
   729 private:
   730   int64_t* mTarget;
   731   int64_t mDelta;
   733   NS_IMETHOD Run() { *mTarget = mDelta; return NS_OK; }
   734 };
   736 } // anon
   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 }
   754 bool
   755 DOMStorageUsage::CheckAndSetETLD1UsageDelta(uint32_t aDataSetIndex, const int64_t aDelta)
   756 {
   757   MOZ_ASSERT(NS_IsMainThread());
   759   int64_t newUsage = mUsage[aDataSetIndex] + aDelta;
   760   if (aDelta > 0 && newUsage > DOMStorageManager::GetQuota()) {
   761     return false;
   762   }
   764   mUsage[aDataSetIndex] = newUsage;
   765   return true;
   766 }
   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   }
   780   if (XRE_GetProcessType() == GeckoProcessType_Default) {
   781     nsAutoPtr<DOMStorageDBThread> db(new DOMStorageDBThread());
   783     nsresult rv = db->Init();
   784     if (NS_FAILED(rv)) {
   785       return nullptr;
   786     }
   788     sDatabase = db.forget();
   789   } else {
   790     nsRefPtr<DOMStorageDBChild> db = new DOMStorageDBChild(
   791         DOMLocalStorageManager::Self());
   793     nsresult rv = db->Init();
   794     if (NS_FAILED(rv)) {
   795       return nullptr;
   796     }
   798     db.forget(&sDatabase);
   799   }
   801   return sDatabase;
   802 }
   804 // static
   805 DOMStorageDBBridge*
   806 DOMStorageCache::GetDatabase()
   807 {
   808   return sDatabase;
   809 }
   811 // static
   812 nsresult
   813 DOMStorageCache::StopDatabase()
   814 {
   815   if (!sDatabase) {
   816     return NS_OK;
   817   }
   819   sDatabaseDown = true;
   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   }
   829   sDatabase = nullptr;
   830   return rv;
   831 }
   833 } // ::dom
   834 } // ::mozilla

mercurial