dom/src/storage/DOMStorageIPC.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     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 "DOMStorageIPC.h"
     8 #include "DOMStorageManager.h"
    10 #include "mozilla/dom/ContentChild.h"
    11 #include "mozilla/dom/ContentParent.h"
    12 #include "mozilla/unused.h"
    13 #include "nsIDiskSpaceWatcher.h"
    14 #include "nsThreadUtils.h"
    16 namespace mozilla {
    17 namespace dom {
    19 // ----------------------------------------------------------------------------
    20 // Child
    21 // ----------------------------------------------------------------------------
    23 NS_IMPL_ADDREF(DOMStorageDBChild)
    25 NS_IMETHODIMP_(MozExternalRefCountType) DOMStorageDBChild::Release(void)
    26 {
    27   NS_PRECONDITION(0 != mRefCnt, "dup release");
    28   nsrefcnt count = --mRefCnt;
    29   NS_LOG_RELEASE(this, count, "DOMStorageDBChild");
    30   if (count == 1 && mIPCOpen) {
    31     Send__delete__(this);
    32     return 0;
    33   }
    34   if (count == 0) {
    35     mRefCnt = 1;
    36     delete this;
    37     return 0;
    38   }
    39   return count;
    40 }
    42 void
    43 DOMStorageDBChild::AddIPDLReference()
    44 {
    45   NS_ABORT_IF_FALSE(!mIPCOpen, "Attempting to retain multiple IPDL references");
    46   mIPCOpen = true;
    47   AddRef();
    48 }
    50 void
    51 DOMStorageDBChild::ReleaseIPDLReference()
    52 {
    53   NS_ABORT_IF_FALSE(mIPCOpen, "Attempting to release non-existent IPDL reference");
    54   mIPCOpen = false;
    55   Release();
    56 }
    58 DOMStorageDBChild::DOMStorageDBChild(DOMLocalStorageManager* aManager)
    59   : mManager(aManager)
    60   , mStatus(NS_OK)
    61   , mIPCOpen(false)
    62 {
    63 }
    65 DOMStorageDBChild::~DOMStorageDBChild()
    66 {
    67 }
    69 nsTHashtable<nsCStringHashKey>&
    70 DOMStorageDBChild::ScopesHavingData()
    71 {
    72   if (!mScopesHavingData) {
    73     mScopesHavingData = new nsTHashtable<nsCStringHashKey>;
    74   }
    76   return *mScopesHavingData;
    77 }
    79 nsresult
    80 DOMStorageDBChild::Init()
    81 {
    82   ContentChild* child = ContentChild::GetSingleton();
    83   AddIPDLReference();
    84   child->SendPStorageConstructor(this);
    85   return NS_OK;
    86 }
    88 nsresult
    89 DOMStorageDBChild::Shutdown()
    90 {
    91   // There is nothing to do here, IPC will release automatically and
    92   // the actual thread running on the parent process will also stop
    93   // automatically in profile-before-change topic observer.
    94   return NS_OK;
    95 }
    97 void
    98 DOMStorageDBChild::AsyncPreload(DOMStorageCacheBridge* aCache, bool aPriority)
    99 {
   100   if (mIPCOpen) {
   101     // Adding ref to cache for the time of preload.  This ensures a reference to
   102     // to the cache and that all keys will load into this cache object.
   103     mLoadingCaches.PutEntry(aCache);
   104     SendAsyncPreload(aCache->Scope(), aPriority);
   105   } else {
   106     // No IPC, no love.  But the LoadDone call is expected.
   107     aCache->LoadDone(NS_ERROR_UNEXPECTED);
   108   }
   109 }
   111 void
   112 DOMStorageDBChild::AsyncGetUsage(DOMStorageUsageBridge* aUsage)
   113 {
   114   if (mIPCOpen) {
   115     SendAsyncGetUsage(aUsage->Scope());
   116   }
   117 }
   119 void
   120 DOMStorageDBChild::SyncPreload(DOMStorageCacheBridge* aCache, bool aForceSync)
   121 {
   122   if (NS_FAILED(mStatus)) {
   123     aCache->LoadDone(mStatus);
   124     return;
   125   }
   127   if (!mIPCOpen) {
   128     aCache->LoadDone(NS_ERROR_UNEXPECTED);
   129     return;
   130   }
   132   // There is no way to put the child process to a wait state to receive all
   133   // incoming async responses from the parent, hence we have to do a sync preload
   134   // instead.  We are smart though, we only demand keys that are left to load in
   135   // case the async preload has already loaded some keys.
   136   InfallibleTArray<nsString> keys, values;
   137   nsresult rv;
   138   SendPreload(aCache->Scope(), aCache->LoadedCount(), &keys, &values, &rv);
   140   for (uint32_t i = 0; i < keys.Length(); ++i) {
   141     aCache->LoadItem(keys[i], values[i]);
   142   }
   144   aCache->LoadDone(rv);
   145 }
   147 nsresult
   148 DOMStorageDBChild::AsyncAddItem(DOMStorageCacheBridge* aCache,
   149                                 const nsAString& aKey,
   150                                 const nsAString& aValue)
   151 {
   152   if (NS_FAILED(mStatus) || !mIPCOpen) {
   153     return mStatus;
   154   }
   156   SendAsyncAddItem(aCache->Scope(), nsString(aKey), nsString(aValue));
   157   ScopesHavingData().PutEntry(aCache->Scope());
   158   return NS_OK;
   159 }
   161 nsresult
   162 DOMStorageDBChild::AsyncUpdateItem(DOMStorageCacheBridge* aCache,
   163                                    const nsAString& aKey,
   164                                    const nsAString& aValue)
   165 {
   166   if (NS_FAILED(mStatus) || !mIPCOpen) {
   167     return mStatus;
   168   }
   170   SendAsyncUpdateItem(aCache->Scope(), nsString(aKey), nsString(aValue));
   171   ScopesHavingData().PutEntry(aCache->Scope());
   172   return NS_OK;
   173 }
   175 nsresult
   176 DOMStorageDBChild::AsyncRemoveItem(DOMStorageCacheBridge* aCache,
   177                                    const nsAString& aKey)
   178 {
   179   if (NS_FAILED(mStatus) || !mIPCOpen) {
   180     return mStatus;
   181   }
   183   SendAsyncRemoveItem(aCache->Scope(), nsString(aKey));
   184   return NS_OK;
   185 }
   187 nsresult
   188 DOMStorageDBChild::AsyncClear(DOMStorageCacheBridge* aCache)
   189 {
   190   if (NS_FAILED(mStatus) || !mIPCOpen) {
   191     return mStatus;
   192   }
   194   SendAsyncClear(aCache->Scope());
   195   ScopesHavingData().RemoveEntry(aCache->Scope());
   196   return NS_OK;
   197 }
   199 bool
   200 DOMStorageDBChild::ShouldPreloadScope(const nsACString& aScope)
   201 {
   202   // Return true if we didn't receive the aScope list yet.
   203   // I tend to rather preserve a bit of early-after-start performance
   204   // then a bit of memory here.
   205   return !mScopesHavingData || mScopesHavingData->Contains(aScope);
   206 }
   208 bool
   209 DOMStorageDBChild::RecvObserve(const nsCString& aTopic,
   210                                const nsCString& aScopePrefix)
   211 {
   212   DOMStorageObserver::Self()->Notify(aTopic.get(), aScopePrefix);
   213   return true;
   214 }
   216 bool
   217 DOMStorageDBChild::RecvScopesHavingData(const InfallibleTArray<nsCString>& aScopes)
   218 {
   219   for (uint32_t i = 0; i < aScopes.Length(); ++i) {
   220     ScopesHavingData().PutEntry(aScopes[i]);
   221   }
   223   return true;
   224 }
   226 bool
   227 DOMStorageDBChild::RecvLoadItem(const nsCString& aScope,
   228                                 const nsString& aKey,
   229                                 const nsString& aValue)
   230 {
   231   DOMStorageCache* aCache = mManager->GetCache(aScope);
   232   if (aCache) {
   233     aCache->LoadItem(aKey, aValue);
   234   }
   236   return true;
   237 }
   239 bool
   240 DOMStorageDBChild::RecvLoadDone(const nsCString& aScope, const nsresult& aRv)
   241 {
   242   DOMStorageCache* aCache = mManager->GetCache(aScope);
   243   if (aCache) {
   244     aCache->LoadDone(aRv);
   246     // Just drop reference to this cache now since the load is done.
   247     mLoadingCaches.RemoveEntry(static_cast<DOMStorageCacheBridge*>(aCache));
   248   }
   250   return true;
   251 }
   253 bool
   254 DOMStorageDBChild::RecvLoadUsage(const nsCString& aScope, const int64_t& aUsage)
   255 {
   256   nsRefPtr<DOMStorageUsageBridge> scopeUsage = mManager->GetScopeUsage(aScope);
   257   scopeUsage->LoadUsage(aUsage);
   258   return true;
   259 }
   261 bool
   262 DOMStorageDBChild::RecvError(const nsresult& aRv)
   263 {
   264   mStatus = aRv;
   265   return true;
   266 }
   268 // ----------------------------------------------------------------------------
   269 // Parent
   270 // ----------------------------------------------------------------------------
   272 NS_IMPL_ADDREF(DOMStorageDBParent)
   273 NS_IMPL_RELEASE(DOMStorageDBParent)
   275 void
   276 DOMStorageDBParent::AddIPDLReference()
   277 {
   278   NS_ABORT_IF_FALSE(!mIPCOpen, "Attempting to retain multiple IPDL references");
   279   mIPCOpen = true;
   280   AddRef();
   281 }
   283 void
   284 DOMStorageDBParent::ReleaseIPDLReference()
   285 {
   286   NS_ABORT_IF_FALSE(mIPCOpen, "Attempting to release non-existent IPDL reference");
   287   mIPCOpen = false;
   288   Release();
   289 }
   291 namespace { // anon
   293 class SendInitialChildDataRunnable : public nsRunnable
   294 {
   295 public:
   296   SendInitialChildDataRunnable(DOMStorageDBParent* aParent)
   297     : mParent(aParent)
   298   {}
   300 private:
   301   NS_IMETHOD Run()
   302   {
   303     if (!mParent->IPCOpen()) {
   304       return NS_OK;
   305     }
   307     DOMStorageDBBridge* db = DOMStorageCache::GetDatabase();
   308     if (db) {
   309       InfallibleTArray<nsCString> scopes;
   310       db->GetScopesHavingData(&scopes);
   311       mozilla::unused << mParent->SendScopesHavingData(scopes);
   312     }
   314     // We need to check if the device is in a low disk space situation, so
   315     // we can forbid in that case any write in localStorage.
   316     nsCOMPtr<nsIDiskSpaceWatcher> diskSpaceWatcher =
   317       do_GetService("@mozilla.org/toolkit/disk-space-watcher;1");
   318     if (!diskSpaceWatcher) {
   319       NS_WARNING("Could not get disk information from DiskSpaceWatcher");
   320       return NS_OK;
   321     }
   322     bool lowDiskSpace = false;
   323     diskSpaceWatcher->GetIsDiskFull(&lowDiskSpace);
   324     if (lowDiskSpace) {
   325       mozilla::unused << mParent->SendObserve(
   326         nsDependentCString("low-disk-space"), EmptyCString());
   327     }
   329     return NS_OK;
   330   }
   332   nsRefPtr<DOMStorageDBParent> mParent;
   333 };
   335 } // anon
   337 DOMStorageDBParent::DOMStorageDBParent()
   338 : mIPCOpen(false)
   339 {
   340   DOMStorageObserver* observer = DOMStorageObserver::Self();
   341   if (observer) {
   342     observer->AddSink(this);
   343   }
   345   // We are always open by IPC only
   346   AddIPDLReference();
   348   // Cannot send directly from here since the channel
   349   // is not completely built at this moment.
   350   nsRefPtr<SendInitialChildDataRunnable> r =
   351     new SendInitialChildDataRunnable(this);
   352   NS_DispatchToCurrentThread(r);
   353 }
   355 DOMStorageDBParent::~DOMStorageDBParent()
   356 {
   357   DOMStorageObserver* observer = DOMStorageObserver::Self();
   358   if (observer) {
   359     observer->RemoveSink(this);
   360   }
   361 }
   363 mozilla::ipc::IProtocol*
   364 DOMStorageDBParent::CloneProtocol(Channel* aChannel,
   365                                   mozilla::ipc::ProtocolCloneContext* aCtx)
   366 {
   367   ContentParent* contentParent = aCtx->GetContentParent();
   368   nsAutoPtr<PStorageParent> actor(contentParent->AllocPStorageParent());
   369   if (!actor || !contentParent->RecvPStorageConstructor(actor)) {
   370     return nullptr;
   371   }
   372   return actor.forget();
   373 }
   375 DOMStorageDBParent::CacheParentBridge*
   376 DOMStorageDBParent::NewCache(const nsACString& aScope)
   377 {
   378   return new CacheParentBridge(this, aScope);
   379 }
   381 bool
   382 DOMStorageDBParent::RecvAsyncPreload(const nsCString& aScope, const bool& aPriority)
   383 {
   384   DOMStorageDBBridge* db = DOMStorageCache::StartDatabase();
   385   if (!db) {
   386     return false;
   387   }
   389   db->AsyncPreload(NewCache(aScope), aPriority);
   390   return true;
   391 }
   393 bool
   394 DOMStorageDBParent::RecvAsyncGetUsage(const nsCString& aScope)
   395 {
   396   DOMStorageDBBridge* db = DOMStorageCache::StartDatabase();
   397   if (!db) {
   398     return false;
   399   }
   401   // The object releases it self in LoadUsage method
   402   nsRefPtr<UsageParentBridge> usage = new UsageParentBridge(this, aScope);
   403   db->AsyncGetUsage(usage);
   404   return true;
   405 }
   407 namespace { // anon
   409 // We need another implementation of DOMStorageCacheBridge to do
   410 // synchronous IPC preload.  This class just receives Load* notifications
   411 // and fills the returning arguments of RecvPreload with the database
   412 // values for us.
   413 class SyncLoadCacheHelper : public DOMStorageCacheBridge
   414 {
   415 public:
   416   SyncLoadCacheHelper(const nsCString& aScope,
   417                       uint32_t aAlreadyLoadedCount,
   418                       InfallibleTArray<nsString>* aKeys,
   419                       InfallibleTArray<nsString>* aValues,
   420                       nsresult* rv)
   421   : mMonitor("DOM Storage SyncLoad IPC")
   422   , mScope(aScope)
   423   , mKeys(aKeys)
   424   , mValues(aValues)
   425   , mRv(rv)
   426   , mLoaded(false)
   427   , mLoadedCount(aAlreadyLoadedCount)
   428   {
   429     // Precaution
   430     *mRv = NS_ERROR_UNEXPECTED;
   431   }
   433   virtual const nsCString& Scope() const { return mScope; }
   434   virtual bool Loaded() { return mLoaded; }
   435   virtual uint32_t LoadedCount() { return mLoadedCount; }
   436   virtual bool LoadItem(const nsAString& aKey, const nsString& aValue)
   437   {
   438     // Called on the aCache background thread
   439     if (mLoaded) {
   440       return false;
   441     }
   443     ++mLoadedCount;
   444     mKeys->AppendElement(aKey);
   445     mValues->AppendElement(aValue);
   446     return true;
   447   }
   449   virtual void LoadDone(nsresult aRv)
   450   {
   451     // Called on the aCache background thread
   452     MonitorAutoLock monitor(mMonitor);
   453     mLoaded = true;
   454     *mRv = aRv;
   455     monitor.Notify();
   456   }
   458   virtual void LoadWait()
   459   {
   460     // Called on the main thread, exits after LoadDone() call
   461     MonitorAutoLock monitor(mMonitor);
   462     while (!mLoaded) {
   463       monitor.Wait();
   464     }
   465   }
   467 private:
   468   Monitor mMonitor;
   469   nsCString mScope;
   470   InfallibleTArray<nsString>* mKeys;
   471   InfallibleTArray<nsString>* mValues;
   472   nsresult* mRv;
   473   bool mLoaded;
   474   uint32_t mLoadedCount;
   475 };
   477 } // anon
   479 bool
   480 DOMStorageDBParent::RecvPreload(const nsCString& aScope,
   481                                 const uint32_t& aAlreadyLoadedCount,
   482                                 InfallibleTArray<nsString>* aKeys,
   483                                 InfallibleTArray<nsString>* aValues,
   484                                 nsresult* aRv)
   485 {
   486   DOMStorageDBBridge* db = DOMStorageCache::StartDatabase();
   487   if (!db) {
   488     return false;
   489   }
   491   nsRefPtr<SyncLoadCacheHelper> cache(
   492     new SyncLoadCacheHelper(aScope, aAlreadyLoadedCount, aKeys, aValues, aRv));
   494   db->SyncPreload(cache, true);
   495   return true;
   496 }
   498 bool
   499 DOMStorageDBParent::RecvAsyncAddItem(const nsCString& aScope,
   500                                      const nsString& aKey,
   501                                      const nsString& aValue)
   502 {
   503   DOMStorageDBBridge* db = DOMStorageCache::StartDatabase();
   504   if (!db) {
   505     return false;
   506   }
   508   nsresult rv = db->AsyncAddItem(NewCache(aScope), aKey, aValue);
   509   if (NS_FAILED(rv) && mIPCOpen) {
   510     mozilla::unused << SendError(rv);
   511   }
   513   return true;
   514 }
   516 bool
   517 DOMStorageDBParent::RecvAsyncUpdateItem(const nsCString& aScope,
   518                                         const nsString& aKey,
   519                                         const nsString& aValue)
   520 {
   521   DOMStorageDBBridge* db = DOMStorageCache::StartDatabase();
   522   if (!db) {
   523     return false;
   524   }
   526   nsresult rv = db->AsyncUpdateItem(NewCache(aScope), aKey, aValue);
   527   if (NS_FAILED(rv) && mIPCOpen) {
   528     mozilla::unused << SendError(rv);
   529   }
   531   return true;
   532 }
   534 bool
   535 DOMStorageDBParent::RecvAsyncRemoveItem(const nsCString& aScope,
   536                                         const nsString& aKey)
   537 {
   538   DOMStorageDBBridge* db = DOMStorageCache::StartDatabase();
   539   if (!db) {
   540     return false;
   541   }
   543   nsresult rv = db->AsyncRemoveItem(NewCache(aScope), aKey);
   544   if (NS_FAILED(rv) && mIPCOpen) {
   545     mozilla::unused << SendError(rv);
   546   }
   548   return true;
   549 }
   551 bool
   552 DOMStorageDBParent::RecvAsyncClear(const nsCString& aScope)
   553 {
   554   DOMStorageDBBridge* db = DOMStorageCache::StartDatabase();
   555   if (!db) {
   556     return false;
   557   }
   559   nsresult rv = db->AsyncClear(NewCache(aScope));
   560   if (NS_FAILED(rv) && mIPCOpen) {
   561     mozilla::unused << SendError(rv);
   562   }
   564   return true;
   565 }
   567 bool
   568 DOMStorageDBParent::RecvAsyncFlush()
   569 {
   570   DOMStorageDBBridge* db = DOMStorageCache::GetDatabase();
   571   if (!db) {
   572     return false;
   573   }
   575   db->AsyncFlush();
   576   return true;
   577 }
   579 // DOMStorageObserverSink
   581 nsresult
   582 DOMStorageDBParent::Observe(const char* aTopic,
   583                             const nsACString& aScopePrefix)
   584 {
   585   if (mIPCOpen) {
   586     mozilla::unused << SendObserve(nsDependentCString(aTopic),
   587                                    nsCString(aScopePrefix));
   588   }
   590   return NS_OK;
   591 }
   593 namespace { // anon
   595 // Results must be sent back on the main thread
   596 class LoadRunnable : public nsRunnable
   597 {
   598 public:
   599   enum TaskType {
   600     loadItem,
   601     loadDone
   602   };
   604   LoadRunnable(DOMStorageDBParent* aParent,
   605                TaskType aType,
   606                const nsACString& aScope,
   607                const nsAString& aKey = EmptyString(),
   608                const nsAString& aValue = EmptyString())
   609   : mParent(aParent)
   610   , mType(aType)
   611   , mScope(aScope)
   612   , mKey(aKey)
   613   , mValue(aValue)
   614   { }
   616   LoadRunnable(DOMStorageDBParent* aParent,
   617                TaskType aType,
   618                const nsACString& aScope,
   619                nsresult aRv)
   620   : mParent(aParent)
   621   , mType(aType)
   622   , mScope(aScope)
   623   , mRv(aRv)
   624   { }
   626 private:
   627   nsRefPtr<DOMStorageDBParent> mParent;
   628   TaskType mType;
   629   nsCString mScope;
   630   nsString mKey;
   631   nsString mValue;
   632   nsresult mRv;
   634   NS_IMETHOD Run()
   635   {
   636     if (!mParent->IPCOpen()) {
   637       return NS_OK;
   638     }
   640     switch (mType)
   641     {
   642     case loadItem:
   643       mozilla::unused << mParent->SendLoadItem(mScope, mKey, mValue);
   644       break;
   645     case loadDone:
   646       mozilla::unused << mParent->SendLoadDone(mScope, mRv);
   647       break;
   648     }
   650     return NS_OK;
   651   }
   652 };
   654 } // anon
   656 // DOMStorageDBParent::CacheParentBridge
   658 bool
   659 DOMStorageDBParent::CacheParentBridge::LoadItem(const nsAString& aKey, const nsString& aValue)
   660 {
   661   if (mLoaded) {
   662     return false;
   663   }
   665   ++mLoadedCount;
   667   nsRefPtr<LoadRunnable> r =
   668     new LoadRunnable(mParent, LoadRunnable::loadItem, mScope, aKey, aValue);
   669   NS_DispatchToMainThread(r);
   670   return true;
   671 }
   673 void
   674 DOMStorageDBParent::CacheParentBridge::LoadDone(nsresult aRv)
   675 {
   676   // Prevent send of duplicate LoadDone.
   677   if (mLoaded) {
   678     return;
   679   }
   681   mLoaded = true;
   683   nsRefPtr<LoadRunnable> r =
   684     new LoadRunnable(mParent, LoadRunnable::loadDone, mScope, aRv);
   685   NS_DispatchToMainThread(r);
   686 }
   688 void
   689 DOMStorageDBParent::CacheParentBridge::LoadWait()
   690 {
   691   // Should never be called on this implementation
   692   MOZ_ASSERT(false);
   693 }
   695 // DOMStorageDBParent::UsageParentBridge
   697 namespace { // anon
   699 class UsageRunnable : public nsRunnable
   700 {
   701 public:
   702   UsageRunnable(DOMStorageDBParent* aParent, const nsACString& aScope, const int64_t& aUsage)
   703   : mParent(aParent)
   704   , mScope(aScope)
   705   , mUsage(aUsage)
   706   {}
   708 private:
   709   NS_IMETHOD Run()
   710   {
   711     if (!mParent->IPCOpen()) {
   712       return NS_OK;
   713     }
   715     mozilla::unused << mParent->SendLoadUsage(mScope, mUsage);
   716     return NS_OK;
   717   }
   719   nsRefPtr<DOMStorageDBParent> mParent;
   720   nsCString mScope;
   721   int64_t mUsage;
   722 };
   724 } // anon
   726 void
   727 DOMStorageDBParent::UsageParentBridge::LoadUsage(const int64_t aUsage)
   728 {
   729   nsRefPtr<UsageRunnable> r = new UsageRunnable(mParent, mScope, aUsage);
   730   NS_DispatchToMainThread(r);
   731 }
   733 } // ::dom
   734 } // ::mozilla

mercurial