LCOV - code coverage report
Current view: top level - dom/cache - Manager.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 2 832 0.2 %
Date: 2017-07-14 16:53:18 Functions: 0 129 0.0 %
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 "mozilla/dom/cache/Manager.h"
       8             : 
       9             : #include "mozilla/AutoRestore.h"
      10             : #include "mozilla/Mutex.h"
      11             : #include "mozilla/StaticMutex.h"
      12             : #include "mozilla/StaticPtr.h"
      13             : #include "mozilla/Unused.h"
      14             : #include "mozilla/dom/cache/Context.h"
      15             : #include "mozilla/dom/cache/DBAction.h"
      16             : #include "mozilla/dom/cache/DBSchema.h"
      17             : #include "mozilla/dom/cache/FileUtils.h"
      18             : #include "mozilla/dom/cache/ManagerId.h"
      19             : #include "mozilla/dom/cache/CacheTypes.h"
      20             : #include "mozilla/dom/cache/SavedTypes.h"
      21             : #include "mozilla/dom/cache/StreamList.h"
      22             : #include "mozilla/dom/cache/Types.h"
      23             : #include "mozilla/ipc/BackgroundParent.h"
      24             : #include "mozStorageHelper.h"
      25             : #include "nsIInputStream.h"
      26             : #include "nsID.h"
      27             : #include "nsIFile.h"
      28             : #include "nsIThread.h"
      29             : #include "nsThreadUtils.h"
      30             : #include "nsTObserverArray.h"
      31             : 
      32             : 
      33             : namespace mozilla {
      34             : namespace dom {
      35             : namespace cache {
      36             : 
      37             : namespace {
      38             : 
      39             : // An Action that is executed when a Context is first created.  It ensures that
      40             : // the directory and database are setup properly.  This lets other actions
      41             : // not worry about these details.
      42           0 : class SetupAction final : public SyncDBAction
      43             : {
      44             : public:
      45           0 :   SetupAction()
      46           0 :     : SyncDBAction(DBAction::Create)
      47           0 :   { }
      48             : 
      49             :   virtual nsresult
      50           0 :   RunSyncWithDBOnTarget(const QuotaInfo& aQuotaInfo, nsIFile* aDBDir,
      51             :                         mozIStorageConnection* aConn) override
      52             :   {
      53           0 :     nsresult rv = BodyCreateDir(aDBDir);
      54           0 :     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
      55             : 
      56             :     // executes in its own transaction
      57           0 :     rv = db::CreateOrMigrateSchema(aConn);
      58           0 :     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
      59             : 
      60             :     // If the Context marker file exists, then the last session was
      61             :     // not cleanly shutdown.  In these cases sqlite will ensure that
      62             :     // the database is valid, but we might still orphan data.  Both
      63             :     // Cache objects and body files can be referenced by DOM objects
      64             :     // after they are "removed" from their parent.  So we need to
      65             :     // look and see if any of these late access objects have been
      66             :     // orphaned.
      67             :     //
      68             :     // Note, this must be done after any schema version updates to
      69             :     // ensure our DBSchema methods work correctly.
      70           0 :     if (MarkerFileExists(aQuotaInfo)) {
      71           0 :       NS_WARNING("Cache not shutdown cleanly! Cleaning up stale data...");
      72             :       mozStorageTransaction trans(aConn, false,
      73           0 :                                   mozIStorageConnection::TRANSACTION_IMMEDIATE);
      74             : 
      75             :       // Clean up orphaned Cache objects
      76           0 :       AutoTArray<CacheId, 8> orphanedCacheIdList;
      77           0 :       nsresult rv = db::FindOrphanedCacheIds(aConn, orphanedCacheIdList);
      78           0 :       if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
      79             : 
      80           0 :       for (uint32_t i = 0; i < orphanedCacheIdList.Length(); ++i) {
      81           0 :         AutoTArray<nsID, 16> deletedBodyIdList;
      82           0 :         rv = db::DeleteCacheId(aConn, orphanedCacheIdList[i], deletedBodyIdList);
      83           0 :         if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
      84             : 
      85           0 :         rv = BodyDeleteFiles(aQuotaInfo, aDBDir, deletedBodyIdList);
      86           0 :         if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
      87             :       }
      88             : 
      89             :       // Clean up orphaned body objects
      90           0 :       AutoTArray<nsID, 64> knownBodyIdList;
      91           0 :       rv = db::GetKnownBodyIds(aConn, knownBodyIdList);
      92             : 
      93           0 :       rv = BodyDeleteOrphanedFiles(aQuotaInfo, aDBDir, knownBodyIdList);
      94           0 :       if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
      95             :     }
      96             : 
      97           0 :     return rv;
      98             :   }
      99             : };
     100             : 
     101             : // ----------------------------------------------------------------------------
     102             : 
     103             : // Action that is executed when we determine that content has stopped using
     104             : // a body file that has been orphaned.
     105           0 : class DeleteOrphanedBodyAction final : public Action
     106             : {
     107             : public:
     108           0 :   explicit DeleteOrphanedBodyAction(const nsTArray<nsID>& aDeletedBodyIdList)
     109           0 :     : mDeletedBodyIdList(aDeletedBodyIdList)
     110           0 :   { }
     111             : 
     112           0 :   explicit DeleteOrphanedBodyAction(const nsID& aBodyId)
     113           0 :   {
     114           0 :     mDeletedBodyIdList.AppendElement(aBodyId);
     115           0 :   }
     116             : 
     117             :   virtual void
     118           0 :   RunOnTarget(Resolver* aResolver, const QuotaInfo& aQuotaInfo, Data*) override
     119             :   {
     120           0 :     MOZ_DIAGNOSTIC_ASSERT(aResolver);
     121           0 :     MOZ_DIAGNOSTIC_ASSERT(aQuotaInfo.mDir);
     122             : 
     123             :     // Note that since DeleteOrphanedBodyAction isn't used while the context is
     124             :     // being initialized, we don't need to check for cancellation here.
     125             : 
     126           0 :     nsCOMPtr<nsIFile> dbDir;
     127           0 :     nsresult rv = aQuotaInfo.mDir->Clone(getter_AddRefs(dbDir));
     128           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
     129           0 :       aResolver->Resolve(rv);
     130           0 :       return;
     131             :     }
     132             : 
     133           0 :     rv = dbDir->Append(NS_LITERAL_STRING("cache"));
     134           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
     135           0 :       aResolver->Resolve(rv);
     136           0 :       return;
     137             :     }
     138             : 
     139           0 :     rv = BodyDeleteFiles(aQuotaInfo, dbDir, mDeletedBodyIdList);
     140           0 :     Unused << NS_WARN_IF(NS_FAILED(rv));
     141             : 
     142           0 :     aResolver->Resolve(rv);
     143             :   }
     144             : 
     145             : private:
     146             :   nsTArray<nsID> mDeletedBodyIdList;
     147             : };
     148             : 
     149           0 : bool IsHeadRequest(const CacheRequest& aRequest, const CacheQueryParams& aParams)
     150             : {
     151           0 :   return !aParams.ignoreMethod() && aRequest.method().LowerCaseEqualsLiteral("head");
     152             : }
     153             : 
     154           0 : bool IsHeadRequest(const CacheRequestOrVoid& aRequest, const CacheQueryParams& aParams)
     155             : {
     156           0 :   if (aRequest.type() == CacheRequestOrVoid::TCacheRequest) {
     157           0 :     return !aParams.ignoreMethod() &&
     158           0 :            aRequest.get_CacheRequest().method().LowerCaseEqualsLiteral("head");
     159             :   }
     160           0 :   return false;
     161             : }
     162             : 
     163             : } // namespace
     164             : 
     165             : // ----------------------------------------------------------------------------
     166             : 
     167             : // Singleton class to track Manager instances and ensure there is only
     168             : // one for each unique ManagerId.
     169             : class Manager::Factory
     170             : {
     171             : public:
     172             :   friend class StaticAutoPtr<Manager::Factory>;
     173             : 
     174             :   static nsresult
     175           0 :   GetOrCreate(ManagerId* aManagerId, Manager** aManagerOut)
     176             :   {
     177           0 :     mozilla::ipc::AssertIsOnBackgroundThread();
     178             : 
     179             :     // Ensure there is a factory instance.  This forces the Get() call
     180             :     // below to use the same factory.
     181           0 :     nsresult rv = MaybeCreateInstance();
     182           0 :     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
     183             : 
     184           0 :     RefPtr<Manager> ref = Get(aManagerId);
     185           0 :     if (!ref) {
     186             :       // TODO: replace this with a thread pool (bug 1119864)
     187           0 :       nsCOMPtr<nsIThread> ioThread;
     188           0 :       rv = NS_NewNamedThread("DOMCacheThread", getter_AddRefs(ioThread));
     189           0 :       if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
     190             : 
     191           0 :       ref = new Manager(aManagerId, ioThread);
     192             : 
     193             :       // There may be an old manager for this origin in the process of
     194             :       // cleaning up.  We need to tell the new manager about this so
     195             :       // that it won't actually start until the old manager is done.
     196           0 :       RefPtr<Manager> oldManager = Get(aManagerId, Closing);
     197           0 :       ref->Init(oldManager);
     198             : 
     199           0 :       MOZ_ASSERT(!sFactory->mManagerList.Contains(ref));
     200           0 :       sFactory->mManagerList.AppendElement(ref);
     201             :     }
     202             : 
     203           0 :     ref.forget(aManagerOut);
     204             : 
     205           0 :     return NS_OK;
     206             :   }
     207             : 
     208             :   static already_AddRefed<Manager>
     209           0 :   Get(ManagerId* aManagerId, State aState = Open)
     210             :   {
     211           0 :     mozilla::ipc::AssertIsOnBackgroundThread();
     212             : 
     213           0 :     nsresult rv = MaybeCreateInstance();
     214           0 :     if (NS_WARN_IF(NS_FAILED(rv))) { return nullptr; }
     215             : 
     216             :     // Iterate in reverse to find the most recent, matching Manager.  This
     217             :     // is important when looking for a Closing Manager.  If a new Manager
     218             :     // chains to an old Manager we want it to be the most recent one.
     219           0 :     ManagerList::BackwardIterator iter(sFactory->mManagerList);
     220           0 :     while (iter.HasMore()) {
     221           0 :       RefPtr<Manager> manager = iter.GetNext();
     222           0 :       if (aState == manager->GetState() && *manager->mManagerId == *aManagerId) {
     223           0 :         return manager.forget();
     224             :       }
     225             :     }
     226             : 
     227           0 :     return nullptr;
     228             :   }
     229             : 
     230             :   static void
     231           0 :   Remove(Manager* aManager)
     232             :   {
     233           0 :     mozilla::ipc::AssertIsOnBackgroundThread();
     234           0 :     MOZ_DIAGNOSTIC_ASSERT(aManager);
     235           0 :     MOZ_DIAGNOSTIC_ASSERT(sFactory);
     236             : 
     237           0 :     MOZ_ALWAYS_TRUE(sFactory->mManagerList.RemoveElement(aManager));
     238             : 
     239             :     // clean up the factory singleton if there are no more managers
     240           0 :     MaybeDestroyInstance();
     241           0 :   }
     242             : 
     243             :   static void
     244           0 :   Abort(const nsACString& aOrigin)
     245             :   {
     246           0 :     mozilla::ipc::AssertIsOnBackgroundThread();
     247             : 
     248           0 :     if (!sFactory) {
     249           0 :       return;
     250             :     }
     251             : 
     252           0 :     MOZ_DIAGNOSTIC_ASSERT(!sFactory->mManagerList.IsEmpty());
     253             : 
     254             :     {
     255           0 :       ManagerList::ForwardIterator iter(sFactory->mManagerList);
     256           0 :       while (iter.HasMore()) {
     257           0 :         RefPtr<Manager> manager = iter.GetNext();
     258           0 :         if (aOrigin.IsVoid() ||
     259           0 :             manager->mManagerId->QuotaOrigin() == aOrigin) {
     260           0 :           manager->Abort();
     261             :         }
     262             :       }
     263             :     }
     264             :   }
     265             : 
     266             :   static void
     267           0 :   ShutdownAll()
     268             :   {
     269           0 :     mozilla::ipc::AssertIsOnBackgroundThread();
     270             : 
     271           0 :     if (!sFactory) {
     272           0 :       return;
     273             :     }
     274             : 
     275           0 :     MOZ_DIAGNOSTIC_ASSERT(!sFactory->mManagerList.IsEmpty());
     276             : 
     277             :     {
     278             :       // Note that we are synchronously calling shutdown code here.  If any
     279             :       // of the shutdown code synchronously decides to delete the Factory
     280             :       // we need to delay that delete until the end of this method.
     281           0 :       AutoRestore<bool> restore(sFactory->mInSyncShutdown);
     282           0 :       sFactory->mInSyncShutdown = true;
     283             : 
     284           0 :       ManagerList::ForwardIterator iter(sFactory->mManagerList);
     285           0 :       while (iter.HasMore()) {
     286           0 :         RefPtr<Manager> manager = iter.GetNext();
     287           0 :         manager->Shutdown();
     288             :       }
     289             :     }
     290             : 
     291           0 :     MaybeDestroyInstance();
     292             :   }
     293             : 
     294             :   static bool
     295           0 :   IsShutdownAllComplete()
     296             :   {
     297           0 :     mozilla::ipc::AssertIsOnBackgroundThread();
     298           0 :     return !sFactory;
     299             :   }
     300             : 
     301             : private:
     302           0 :   Factory()
     303           0 :     : mInSyncShutdown(false)
     304             :   {
     305           0 :     MOZ_COUNT_CTOR(cache::Manager::Factory);
     306           0 :   }
     307             : 
     308           0 :   ~Factory()
     309           0 :   {
     310           0 :     MOZ_COUNT_DTOR(cache::Manager::Factory);
     311           0 :     MOZ_DIAGNOSTIC_ASSERT(mManagerList.IsEmpty());
     312           0 :     MOZ_DIAGNOSTIC_ASSERT(!mInSyncShutdown);
     313           0 :   }
     314             : 
     315             :   static nsresult
     316           0 :   MaybeCreateInstance()
     317             :   {
     318           0 :     mozilla::ipc::AssertIsOnBackgroundThread();
     319             : 
     320           0 :     if (!sFactory) {
     321             :       // Be clear about what we are locking.  sFactory is bg thread only, so
     322             :       // we don't need to lock it here.  Just protect sFactoryShutdown and
     323             :       // sBackgroundThread.
     324             :       {
     325           0 :         StaticMutexAutoLock lock(sMutex);
     326             : 
     327           0 :         if (sFactoryShutdown) {
     328           0 :           return NS_ERROR_ILLEGAL_DURING_SHUTDOWN;
     329             :         }
     330             :       }
     331             : 
     332             :       // We cannot use ClearOnShutdown() here because we're not on the main
     333             :       // thread.  Instead, we delete sFactory in Factory::Remove() after the
     334             :       // last manager is removed.  ShutdownObserver ensures this happens
     335             :       // before shutdown.
     336           0 :       sFactory = new Factory();
     337             :     }
     338             : 
     339             :     // Never return sFactory to code outside Factory.  We need to delete it
     340             :     // out from under ourselves just before we return from Remove().  This
     341             :     // would be (even more) dangerous if other code had a pointer to the
     342             :     // factory itself.
     343             : 
     344           0 :     return NS_OK;
     345             :   }
     346             : 
     347             :   static void
     348           0 :   MaybeDestroyInstance()
     349             :   {
     350           0 :     mozilla::ipc::AssertIsOnBackgroundThread();
     351           0 :     MOZ_DIAGNOSTIC_ASSERT(sFactory);
     352             : 
     353             :     // If the factory is is still in use then we cannot delete yet.  This
     354             :     // could be due to managers still existing or because we are in the
     355             :     // middle of shutting down.  We need to be careful not to delete ourself
     356             :     // synchronously during shutdown.
     357           0 :     if (!sFactory->mManagerList.IsEmpty() || sFactory->mInSyncShutdown) {
     358           0 :       return;
     359             :     }
     360             : 
     361           0 :     sFactory = nullptr;
     362             :   }
     363             : 
     364             :   // Singleton created on demand and deleted when last Manager is cleared
     365             :   // in Remove().
     366             :   // PBackground thread only.
     367             :   static StaticAutoPtr<Factory> sFactory;
     368             : 
     369             :   // protects following static attribute
     370             :   static StaticMutex sMutex;
     371             : 
     372             :   // Indicate if shutdown has occurred to block re-creation of sFactory.
     373             :   // Must hold sMutex to access.
     374             :   static bool sFactoryShutdown;
     375             : 
     376             :   // Weak references as we don't want to keep Manager objects alive forever.
     377             :   // When a Manager is destroyed it calls Factory::Remove() to clear itself.
     378             :   // PBackground thread only.
     379             :   typedef nsTObserverArray<Manager*> ManagerList;
     380             :   ManagerList mManagerList;
     381             : 
     382             :   // This flag is set when we are looping through the list and calling
     383             :   // Shutdown() on each Manager.  We need to be careful not to synchronously
     384             :   // trigger the deletion of the factory while still executing this loop.
     385             :   bool mInSyncShutdown;
     386             : };
     387             : 
     388             : // static
     389           3 : StaticAutoPtr<Manager::Factory> Manager::Factory::sFactory;
     390             : 
     391             : // static
     392           3 : StaticMutex Manager::Factory::sMutex;
     393             : 
     394             : // static
     395             : bool Manager::Factory::sFactoryShutdown = false;
     396             : 
     397             : // ----------------------------------------------------------------------------
     398             : 
     399             : // Abstract class to help implement the various Actions.  The vast majority
     400             : // of Actions are synchronous and need to report back to a Listener on the
     401             : // Manager.
     402           0 : class Manager::BaseAction : public SyncDBAction
     403             : {
     404             : protected:
     405           0 :   BaseAction(Manager* aManager, ListenerId aListenerId)
     406           0 :     : SyncDBAction(DBAction::Existing)
     407             :     , mManager(aManager)
     408           0 :     , mListenerId(aListenerId)
     409             :   {
     410           0 :   }
     411             : 
     412             :   virtual void
     413             :   Complete(Listener* aListener, ErrorResult&& aRv) = 0;
     414             : 
     415             :   virtual void
     416           0 :   CompleteOnInitiatingThread(nsresult aRv) override
     417             :   {
     418           0 :     NS_ASSERT_OWNINGTHREAD(Manager::BaseAction);
     419           0 :     Listener* listener = mManager->GetListener(mListenerId);
     420           0 :     if (listener) {
     421           0 :       Complete(listener, ErrorResult(aRv));
     422             :     }
     423             : 
     424             :     // ensure we release the manager on the initiating thread
     425           0 :     mManager = nullptr;
     426           0 :   }
     427             : 
     428             :   RefPtr<Manager> mManager;
     429             :   const ListenerId mListenerId;
     430             : };
     431             : 
     432             : // ----------------------------------------------------------------------------
     433             : 
     434             : // Action that is executed when we determine that content has stopped using
     435             : // a Cache object that has been orphaned.
     436           0 : class Manager::DeleteOrphanedCacheAction final : public SyncDBAction
     437             : {
     438             : public:
     439           0 :   DeleteOrphanedCacheAction(Manager* aManager, CacheId aCacheId)
     440           0 :     : SyncDBAction(DBAction::Existing)
     441             :     , mManager(aManager)
     442           0 :     , mCacheId(aCacheId)
     443           0 :   { }
     444             : 
     445             :   virtual nsresult
     446           0 :   RunSyncWithDBOnTarget(const QuotaInfo& aQuotaInfo, nsIFile* aDBDir,
     447             :                         mozIStorageConnection* aConn) override
     448             :   {
     449             :     mozStorageTransaction trans(aConn, false,
     450           0 :                                 mozIStorageConnection::TRANSACTION_IMMEDIATE);
     451             : 
     452           0 :     nsresult rv = db::DeleteCacheId(aConn, mCacheId, mDeletedBodyIdList);
     453           0 :     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
     454             : 
     455           0 :     rv = trans.Commit();
     456           0 :     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
     457             : 
     458           0 :     return rv;
     459             :   }
     460             : 
     461             :   virtual void
     462           0 :   CompleteOnInitiatingThread(nsresult aRv) override
     463             :   {
     464           0 :     mManager->NoteOrphanedBodyIdList(mDeletedBodyIdList);
     465             : 
     466             :     // ensure we release the manager on the initiating thread
     467           0 :     mManager = nullptr;
     468           0 :   }
     469             : 
     470             : private:
     471             :   RefPtr<Manager> mManager;
     472             :   const CacheId mCacheId;
     473             :   nsTArray<nsID> mDeletedBodyIdList;
     474             : };
     475             : 
     476             : // ----------------------------------------------------------------------------
     477             : 
     478           0 : class Manager::CacheMatchAction final : public Manager::BaseAction
     479             : {
     480             : public:
     481           0 :   CacheMatchAction(Manager* aManager, ListenerId aListenerId,
     482             :                    CacheId aCacheId, const CacheMatchArgs& aArgs,
     483             :                    StreamList* aStreamList)
     484           0 :     : BaseAction(aManager, aListenerId)
     485             :     , mCacheId(aCacheId)
     486             :     , mArgs(aArgs)
     487             :     , mStreamList(aStreamList)
     488           0 :     , mFoundResponse(false)
     489           0 :   { }
     490             : 
     491             :   virtual nsresult
     492           0 :   RunSyncWithDBOnTarget(const QuotaInfo& aQuotaInfo, nsIFile* aDBDir,
     493             :                         mozIStorageConnection* aConn) override
     494             :   {
     495           0 :     nsresult rv = db::CacheMatch(aConn, mCacheId, mArgs.request(),
     496           0 :                                  mArgs.params(), &mFoundResponse, &mResponse);
     497           0 :     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
     498             : 
     499           0 :     if (!mFoundResponse || !mResponse.mHasBodyId
     500           0 :                         || IsHeadRequest(mArgs.request(), mArgs.params())) {
     501           0 :       mResponse.mHasBodyId = false;
     502           0 :       return rv;
     503             :     }
     504             : 
     505           0 :     nsCOMPtr<nsIInputStream> stream;
     506           0 :     rv = BodyOpen(aQuotaInfo, aDBDir, mResponse.mBodyId, getter_AddRefs(stream));
     507           0 :     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
     508           0 :     if (NS_WARN_IF(!stream)) { return NS_ERROR_FILE_NOT_FOUND; }
     509             : 
     510           0 :     mStreamList->Add(mResponse.mBodyId, stream);
     511             : 
     512           0 :     return rv;
     513             :   }
     514             : 
     515             :   virtual void
     516           0 :   Complete(Listener* aListener, ErrorResult&& aRv) override
     517             :   {
     518           0 :     if (!mFoundResponse) {
     519           0 :       aListener->OnOpComplete(Move(aRv), CacheMatchResult(void_t()));
     520             :     } else {
     521           0 :       mStreamList->Activate(mCacheId);
     522           0 :       aListener->OnOpComplete(Move(aRv), CacheMatchResult(void_t()), mResponse,
     523           0 :                               mStreamList);
     524             :     }
     525           0 :     mStreamList = nullptr;
     526           0 :   }
     527             : 
     528           0 :   virtual bool MatchesCacheId(CacheId aCacheId) const override
     529             :   {
     530           0 :     return aCacheId == mCacheId;
     531             :   }
     532             : 
     533             : private:
     534             :   const CacheId mCacheId;
     535             :   const CacheMatchArgs mArgs;
     536             :   RefPtr<StreamList> mStreamList;
     537             :   bool mFoundResponse;
     538             :   SavedResponse mResponse;
     539             : };
     540             : 
     541             : // ----------------------------------------------------------------------------
     542             : 
     543           0 : class Manager::CacheMatchAllAction final : public Manager::BaseAction
     544             : {
     545             : public:
     546           0 :   CacheMatchAllAction(Manager* aManager, ListenerId aListenerId,
     547             :                       CacheId aCacheId, const CacheMatchAllArgs& aArgs,
     548             :                       StreamList* aStreamList)
     549           0 :     : BaseAction(aManager, aListenerId)
     550             :     , mCacheId(aCacheId)
     551             :     , mArgs(aArgs)
     552           0 :     , mStreamList(aStreamList)
     553           0 :   { }
     554             : 
     555             :   virtual nsresult
     556           0 :   RunSyncWithDBOnTarget(const QuotaInfo& aQuotaInfo, nsIFile* aDBDir,
     557             :                         mozIStorageConnection* aConn) override
     558             :   {
     559           0 :     nsresult rv = db::CacheMatchAll(aConn, mCacheId, mArgs.requestOrVoid(),
     560           0 :                                     mArgs.params(), mSavedResponses);
     561           0 :     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
     562             : 
     563           0 :     for (uint32_t i = 0; i < mSavedResponses.Length(); ++i) {
     564           0 :       if (!mSavedResponses[i].mHasBodyId
     565           0 :           || IsHeadRequest(mArgs.requestOrVoid(), mArgs.params())) {
     566           0 :         mSavedResponses[i].mHasBodyId = false;
     567           0 :         continue;
     568             :       }
     569             : 
     570           0 :       nsCOMPtr<nsIInputStream> stream;
     571           0 :       rv = BodyOpen(aQuotaInfo, aDBDir, mSavedResponses[i].mBodyId,
     572           0 :                     getter_AddRefs(stream));
     573           0 :       if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
     574           0 :       if (NS_WARN_IF(!stream)) { return NS_ERROR_FILE_NOT_FOUND; }
     575             : 
     576           0 :       mStreamList->Add(mSavedResponses[i].mBodyId, stream);
     577             :     }
     578             : 
     579           0 :     return rv;
     580             :   }
     581             : 
     582             :   virtual void
     583           0 :   Complete(Listener* aListener, ErrorResult&& aRv) override
     584             :   {
     585           0 :     mStreamList->Activate(mCacheId);
     586           0 :     aListener->OnOpComplete(Move(aRv), CacheMatchAllResult(), mSavedResponses,
     587           0 :                             mStreamList);
     588           0 :     mStreamList = nullptr;
     589           0 :   }
     590             : 
     591           0 :   virtual bool MatchesCacheId(CacheId aCacheId) const override
     592             :   {
     593           0 :     return aCacheId == mCacheId;
     594             :   }
     595             : 
     596             : private:
     597             :   const CacheId mCacheId;
     598             :   const CacheMatchAllArgs mArgs;
     599             :   RefPtr<StreamList> mStreamList;
     600             :   nsTArray<SavedResponse> mSavedResponses;
     601             : };
     602             : 
     603             : // ----------------------------------------------------------------------------
     604             : 
     605             : // This is the most complex Action.  It puts a request/response pair into the
     606             : // Cache.  It does not complete until all of the body data has been saved to
     607             : // disk.  This means its an asynchronous Action.
     608             : class Manager::CachePutAllAction final : public DBAction
     609             : {
     610             : public:
     611           0 :   CachePutAllAction(Manager* aManager, ListenerId aListenerId,
     612             :                     CacheId aCacheId,
     613             :                     const nsTArray<CacheRequestResponse>& aPutList,
     614             :                     const nsTArray<nsCOMPtr<nsIInputStream>>& aRequestStreamList,
     615             :                     const nsTArray<nsCOMPtr<nsIInputStream>>& aResponseStreamList)
     616           0 :     : DBAction(DBAction::Existing)
     617             :     , mManager(aManager)
     618             :     , mListenerId(aListenerId)
     619             :     , mCacheId(aCacheId)
     620             :     , mList(aPutList.Length())
     621             :     , mExpectedAsyncCopyCompletions(1)
     622             :     , mAsyncResult(NS_OK)
     623           0 :     , mMutex("cache::Manager::CachePutAllAction")
     624             :   {
     625           0 :     MOZ_DIAGNOSTIC_ASSERT(!aPutList.IsEmpty());
     626           0 :     MOZ_DIAGNOSTIC_ASSERT(aPutList.Length() == aRequestStreamList.Length());
     627           0 :     MOZ_DIAGNOSTIC_ASSERT(aPutList.Length() == aResponseStreamList.Length());
     628             : 
     629           0 :     for (uint32_t i = 0; i < aPutList.Length(); ++i) {
     630           0 :       Entry* entry = mList.AppendElement();
     631           0 :       entry->mRequest = aPutList[i].request();
     632           0 :       entry->mRequestStream = aRequestStreamList[i];
     633           0 :       entry->mResponse = aPutList[i].response();
     634           0 :       entry->mResponseStream = aResponseStreamList[i];
     635             :     }
     636           0 :   }
     637             : 
     638             : private:
     639           0 :   ~CachePutAllAction() { }
     640             : 
     641             :   virtual void
     642           0 :   RunWithDBOnTarget(Resolver* aResolver, const QuotaInfo& aQuotaInfo,
     643             :                     nsIFile* aDBDir, mozIStorageConnection* aConn) override
     644             :   {
     645           0 :     MOZ_DIAGNOSTIC_ASSERT(aResolver);
     646           0 :     MOZ_DIAGNOSTIC_ASSERT(aDBDir);
     647           0 :     MOZ_DIAGNOSTIC_ASSERT(aConn);
     648           0 :     MOZ_DIAGNOSTIC_ASSERT(!mResolver);
     649           0 :     MOZ_DIAGNOSTIC_ASSERT(!mDBDir);
     650           0 :     MOZ_DIAGNOSTIC_ASSERT(!mConn);
     651             : 
     652           0 :     MOZ_DIAGNOSTIC_ASSERT(!mTarget);
     653           0 :     mTarget = GetCurrentThreadSerialEventTarget();
     654           0 :     MOZ_DIAGNOSTIC_ASSERT(mTarget);
     655             : 
     656             :     // We should be pre-initialized to expect one async completion.  This is
     657             :     // the "manual" completion we call at the end of this method in all
     658             :     // cases.
     659           0 :     MOZ_DIAGNOSTIC_ASSERT(mExpectedAsyncCopyCompletions == 1);
     660             : 
     661           0 :     mResolver = aResolver;
     662           0 :     mDBDir = aDBDir;
     663           0 :     mConn = aConn;
     664           0 :     mQuotaInfo.emplace(aQuotaInfo);
     665             : 
     666             :     // File bodies are streamed to disk via asynchronous copying.  Start
     667             :     // this copying now.  Each copy will eventually result in a call
     668             :     // to OnAsyncCopyComplete().
     669           0 :     nsresult rv = NS_OK;
     670           0 :     for (uint32_t i = 0; i < mList.Length(); ++i) {
     671           0 :       rv = StartStreamCopy(aQuotaInfo, mList[i], RequestStream,
     672           0 :                            &mExpectedAsyncCopyCompletions);
     673           0 :       if (NS_WARN_IF(NS_FAILED(rv))) {
     674           0 :         break;
     675             :       }
     676             : 
     677           0 :       rv = StartStreamCopy(aQuotaInfo, mList[i], ResponseStream,
     678           0 :                            &mExpectedAsyncCopyCompletions);
     679           0 :       if (NS_WARN_IF(NS_FAILED(rv))) {
     680           0 :         break;
     681             :       }
     682             :     }
     683             : 
     684             : 
     685             :     // Always call OnAsyncCopyComplete() manually here.  This covers the
     686             :     // case where there is no async copying and also reports any startup
     687             :     // errors correctly.  If we hit an error, then OnAsyncCopyComplete()
     688             :     // will cancel any async copying.
     689           0 :     OnAsyncCopyComplete(rv);
     690           0 :   }
     691             : 
     692             :   // Called once for each asynchronous file copy whether it succeeds or
     693             :   // fails.  If a file copy is canceled, it still calls this method with
     694             :   // an error code.
     695             :   void
     696           0 :   OnAsyncCopyComplete(nsresult aRv)
     697             :   {
     698           0 :     MOZ_ASSERT(mTarget->IsOnCurrentThread());
     699           0 :     MOZ_DIAGNOSTIC_ASSERT(mConn);
     700           0 :     MOZ_DIAGNOSTIC_ASSERT(mResolver);
     701           0 :     MOZ_DIAGNOSTIC_ASSERT(mExpectedAsyncCopyCompletions > 0);
     702             : 
     703             :     // Explicitly check for cancellation here to catch a race condition.
     704             :     // Consider:
     705             :     //
     706             :     // 1) NS_AsyncCopy() executes on IO thread, but has not saved its
     707             :     //    copy context yet.
     708             :     // 2) CancelAllStreamCopying() occurs on PBackground thread
     709             :     // 3) Copy context from (1) is saved on IO thread.
     710             :     //
     711             :     // Checking for cancellation here catches this condition when we
     712             :     // first call OnAsyncCopyComplete() manually from RunWithDBOnTarget().
     713             :     //
     714             :     // This explicit cancellation check also handles the case where we
     715             :     // are canceled just after all stream copying completes.  We should
     716             :     // abort the synchronous DB operations in this case if we have not
     717             :     // started them yet.
     718           0 :     if (NS_SUCCEEDED(aRv) && IsCanceled()) {
     719           0 :       aRv = NS_ERROR_ABORT;
     720             :     }
     721             : 
     722             :     // If any of the async copies fail, we need to still wait for them all to
     723             :     // complete.  Cancel any other streams still working and remember the
     724             :     // error.  All canceled streams will call OnAsyncCopyComplete().
     725           0 :     if (NS_FAILED(aRv) && NS_SUCCEEDED(mAsyncResult)) {
     726           0 :       CancelAllStreamCopying();
     727           0 :       mAsyncResult = aRv;
     728             :     }
     729             : 
     730             :     // Check to see if async copying is still on-going.  If so, then simply
     731             :     // return for now.  We must wait for a later OnAsyncCopyComplete() call.
     732           0 :     mExpectedAsyncCopyCompletions -= 1;
     733           0 :     if (mExpectedAsyncCopyCompletions > 0) {
     734           0 :       return;
     735             :     }
     736             : 
     737             :     // We have finished with all async copying.  Indicate this by clearing all
     738             :     // our copy contexts.
     739             :     {
     740           0 :       MutexAutoLock lock(mMutex);
     741           0 :       mCopyContextList.Clear();
     742             :     }
     743             : 
     744             :     // An error occurred while async copying.  Terminate the Action.
     745             :     // DoResolve() will clean up any files we may have written.
     746           0 :     if (NS_FAILED(mAsyncResult)) {
     747           0 :       DoResolve(mAsyncResult);
     748           0 :       return;
     749             :     }
     750             : 
     751             :     mozStorageTransaction trans(mConn, false,
     752           0 :                                 mozIStorageConnection::TRANSACTION_IMMEDIATE);
     753             : 
     754           0 :     nsresult rv = NS_OK;
     755           0 :     for (uint32_t i = 0; i < mList.Length(); ++i) {
     756           0 :       Entry& e = mList[i];
     757           0 :       if (e.mRequestStream) {
     758           0 :         rv = BodyFinalizeWrite(mDBDir, e.mRequestBodyId);
     759           0 :         if (NS_WARN_IF(NS_FAILED(rv))) {
     760           0 :           DoResolve(rv);
     761           0 :           return;
     762             :         }
     763             :       }
     764           0 :       if (e.mResponseStream) {
     765           0 :         rv = BodyFinalizeWrite(mDBDir, e.mResponseBodyId);
     766           0 :         if (NS_WARN_IF(NS_FAILED(rv))) {
     767           0 :           DoResolve(rv);
     768           0 :           return;
     769             :         }
     770             :       }
     771             : 
     772           0 :       rv = db::CachePut(mConn, mCacheId, e.mRequest,
     773           0 :                         e.mRequestStream ? &e.mRequestBodyId : nullptr,
     774             :                         e.mResponse,
     775           0 :                         e.mResponseStream ? &e.mResponseBodyId : nullptr,
     776           0 :                         mDeletedBodyIdList);
     777           0 :       if (NS_WARN_IF(NS_FAILED(rv))) {
     778           0 :         DoResolve(rv);
     779           0 :         return;
     780             :       }
     781             :     }
     782             : 
     783           0 :     rv = trans.Commit();
     784           0 :     Unused << NS_WARN_IF(NS_FAILED(rv));
     785             : 
     786           0 :     DoResolve(rv);
     787             :   }
     788             : 
     789             :   virtual void
     790           0 :   CompleteOnInitiatingThread(nsresult aRv) override
     791             :   {
     792           0 :     NS_ASSERT_OWNINGTHREAD(Action);
     793             : 
     794           0 :     for (uint32_t i = 0; i < mList.Length(); ++i) {
     795           0 :       mList[i].mRequestStream = nullptr;
     796           0 :       mList[i].mResponseStream = nullptr;
     797             :     }
     798             : 
     799           0 :     mManager->NoteOrphanedBodyIdList(mDeletedBodyIdList);
     800             : 
     801           0 :     Listener* listener = mManager->GetListener(mListenerId);
     802           0 :     mManager = nullptr;
     803           0 :     if (listener) {
     804           0 :       listener->OnOpComplete(ErrorResult(aRv), CachePutAllResult());
     805             :     }
     806           0 :   }
     807             : 
     808             :   virtual void
     809           0 :   CancelOnInitiatingThread() override
     810             :   {
     811           0 :     NS_ASSERT_OWNINGTHREAD(Action);
     812           0 :     Action::CancelOnInitiatingThread();
     813           0 :     CancelAllStreamCopying();
     814           0 :   }
     815             : 
     816           0 :   virtual bool MatchesCacheId(CacheId aCacheId) const override
     817             :   {
     818           0 :     NS_ASSERT_OWNINGTHREAD(Action);
     819           0 :     return aCacheId == mCacheId;
     820             :   }
     821             : 
     822           0 :   struct Entry
     823             :   {
     824             :     CacheRequest mRequest;
     825             :     nsCOMPtr<nsIInputStream> mRequestStream;
     826             :     nsID mRequestBodyId;
     827             :     nsCOMPtr<nsISupports> mRequestCopyContext;
     828             : 
     829             :     CacheResponse mResponse;
     830             :     nsCOMPtr<nsIInputStream> mResponseStream;
     831             :     nsID mResponseBodyId;
     832             :     nsCOMPtr<nsISupports> mResponseCopyContext;
     833             :   };
     834             : 
     835             :   enum StreamId
     836             :   {
     837             :     RequestStream,
     838             :     ResponseStream
     839             :   };
     840             : 
     841             :   nsresult
     842           0 :   StartStreamCopy(const QuotaInfo& aQuotaInfo, Entry& aEntry,
     843             :                   StreamId aStreamId, uint32_t* aCopyCountOut)
     844             :   {
     845           0 :     MOZ_ASSERT(mTarget->IsOnCurrentThread());
     846           0 :     MOZ_DIAGNOSTIC_ASSERT(aCopyCountOut);
     847             : 
     848           0 :     if (IsCanceled()) {
     849           0 :       return NS_ERROR_ABORT;
     850             :     }
     851             : 
     852           0 :     nsCOMPtr<nsIInputStream> source;
     853             :     nsID* bodyId;
     854             : 
     855           0 :     if (aStreamId == RequestStream) {
     856           0 :       source = aEntry.mRequestStream;
     857           0 :       bodyId = &aEntry.mRequestBodyId;
     858             :     } else {
     859           0 :       MOZ_DIAGNOSTIC_ASSERT(aStreamId == ResponseStream);
     860           0 :       source = aEntry.mResponseStream;
     861           0 :       bodyId = &aEntry.mResponseBodyId;
     862             :     }
     863             : 
     864           0 :     if (!source) {
     865           0 :       return NS_OK;
     866             :     }
     867             : 
     868           0 :     nsCOMPtr<nsISupports> copyContext;
     869             : 
     870           0 :     nsresult rv = BodyStartWriteStream(aQuotaInfo, mDBDir, source, this,
     871             :                                        AsyncCopyCompleteFunc, bodyId,
     872           0 :                                        getter_AddRefs(copyContext));
     873           0 :     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
     874             : 
     875           0 :     mBodyIdWrittenList.AppendElement(*bodyId);
     876             : 
     877           0 :     if (copyContext) {
     878           0 :       MutexAutoLock lock(mMutex);
     879           0 :       mCopyContextList.AppendElement(copyContext);
     880             :     }
     881             : 
     882           0 :     *aCopyCountOut += 1;
     883             : 
     884           0 :     return rv;
     885             :   }
     886             : 
     887             :   void
     888           0 :   CancelAllStreamCopying()
     889             :   {
     890             :     // May occur on either owning thread or target thread
     891           0 :     MutexAutoLock lock(mMutex);
     892           0 :     for (uint32_t i = 0; i < mCopyContextList.Length(); ++i) {
     893           0 :       BodyCancelWrite(mDBDir, mCopyContextList[i]);
     894             :     }
     895           0 :     mCopyContextList.Clear();
     896           0 :   }
     897             : 
     898             :   static void
     899           0 :   AsyncCopyCompleteFunc(void* aClosure, nsresult aRv)
     900             :   {
     901             :     // May be on any thread, including STS event target.
     902           0 :     MOZ_DIAGNOSTIC_ASSERT(aClosure);
     903             :     // Weak ref as we are guaranteed to the action is alive until
     904             :     // CompleteOnInitiatingThread is called.
     905           0 :     CachePutAllAction* action = static_cast<CachePutAllAction*>(aClosure);
     906           0 :     action->CallOnAsyncCopyCompleteOnTargetThread(aRv);
     907           0 :   }
     908             : 
     909             :   void
     910           0 :   CallOnAsyncCopyCompleteOnTargetThread(nsresult aRv)
     911             :   {
     912             :     // May be on any thread, including STS event target.  Non-owning runnable
     913             :     // here since we are guaranteed the Action will survive until
     914             :     // CompleteOnInitiatingThread is called.
     915           0 :     nsCOMPtr<nsIRunnable> runnable = NewNonOwningRunnableMethod<nsresult>(
     916             :       "dom::cache::Manager::CachePutAllAction::OnAsyncCopyComplete",
     917             :       this,
     918             :       &CachePutAllAction::OnAsyncCopyComplete,
     919           0 :       aRv);
     920           0 :     MOZ_ALWAYS_SUCCEEDS(
     921             :       mTarget->Dispatch(runnable.forget(), nsIThread::DISPATCH_NORMAL));
     922           0 :   }
     923             : 
     924             :   void
     925           0 :   DoResolve(nsresult aRv)
     926             :   {
     927           0 :     MOZ_ASSERT(mTarget->IsOnCurrentThread());
     928             : 
     929             :     // DoResolve() must not be called until all async copying has completed.
     930             : #ifdef DEBUG
     931             :     {
     932           0 :       MutexAutoLock lock(mMutex);
     933           0 :       MOZ_ASSERT(mCopyContextList.IsEmpty());
     934             :     }
     935             : #endif
     936             : 
     937             :     // Clean up any files we might have written before hitting the error.
     938           0 :     if (NS_FAILED(aRv)) {
     939           0 :       BodyDeleteFiles(mQuotaInfo.ref(), mDBDir, mBodyIdWrittenList);
     940             :     }
     941             : 
     942             :     // Must be released on the target thread where it was opened.
     943           0 :     mConn = nullptr;
     944             : 
     945             :     // Drop our ref to the target thread as we are done with this thread.
     946             :     // Also makes our thread assertions catch any incorrect method calls
     947             :     // after resolve.
     948           0 :     mTarget = nullptr;
     949             : 
     950             :     // Make sure to de-ref the resolver per the Action API contract.
     951           0 :     RefPtr<Action::Resolver> resolver;
     952           0 :     mResolver.swap(resolver);
     953           0 :     resolver->Resolve(aRv);
     954           0 :   }
     955             : 
     956             :   // initiating thread only
     957             :   RefPtr<Manager> mManager;
     958             :   const ListenerId mListenerId;
     959             : 
     960             :   // Set on initiating thread, read on target thread.  State machine guarantees
     961             :   // these are not modified while being read by the target thread.
     962             :   const CacheId mCacheId;
     963             :   nsTArray<Entry> mList;
     964             :   uint32_t mExpectedAsyncCopyCompletions;
     965             : 
     966             :   // target thread only
     967             :   RefPtr<Resolver> mResolver;
     968             :   nsCOMPtr<nsIFile> mDBDir;
     969             :   nsCOMPtr<mozIStorageConnection> mConn;
     970             :   nsCOMPtr<nsISerialEventTarget> mTarget;
     971             :   nsresult mAsyncResult;
     972             :   nsTArray<nsID> mBodyIdWrittenList;
     973             : 
     974             :   // Written to on target thread, accessed on initiating thread after target
     975             :   // thread activity is guaranteed complete
     976             :   nsTArray<nsID> mDeletedBodyIdList;
     977             : 
     978             :   // accessed from any thread while mMutex locked
     979             :   Mutex mMutex;
     980             :   nsTArray<nsCOMPtr<nsISupports>> mCopyContextList;
     981             : 
     982             :   Maybe<QuotaInfo> mQuotaInfo;
     983             : };
     984             : 
     985             : // ----------------------------------------------------------------------------
     986             : 
     987           0 : class Manager::CacheDeleteAction final : public Manager::BaseAction
     988             : {
     989             : public:
     990           0 :   CacheDeleteAction(Manager* aManager, ListenerId aListenerId,
     991             :                     CacheId aCacheId, const CacheDeleteArgs& aArgs)
     992           0 :     : BaseAction(aManager, aListenerId)
     993             :     , mCacheId(aCacheId)
     994             :     , mArgs(aArgs)
     995           0 :     , mSuccess(false)
     996           0 :   { }
     997             : 
     998             :   virtual nsresult
     999           0 :   RunSyncWithDBOnTarget(const QuotaInfo& aQuotaInfo, nsIFile* aDBDir,
    1000             :                         mozIStorageConnection* aConn) override
    1001             :   {
    1002             :     mozStorageTransaction trans(aConn, false,
    1003           0 :                                 mozIStorageConnection::TRANSACTION_IMMEDIATE);
    1004             : 
    1005           0 :     nsresult rv = db::CacheDelete(aConn, mCacheId, mArgs.request(),
    1006           0 :                                   mArgs.params(), mDeletedBodyIdList,
    1007           0 :                                   &mSuccess);
    1008           0 :     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1009             : 
    1010           0 :     rv = trans.Commit();
    1011           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
    1012           0 :       mSuccess = false;
    1013           0 :       return rv;
    1014             :     }
    1015             : 
    1016           0 :     return rv;
    1017             :   }
    1018             : 
    1019             :   virtual void
    1020           0 :   Complete(Listener* aListener, ErrorResult&& aRv) override
    1021             :   {
    1022           0 :     mManager->NoteOrphanedBodyIdList(mDeletedBodyIdList);
    1023           0 :     aListener->OnOpComplete(Move(aRv), CacheDeleteResult(mSuccess));
    1024           0 :   }
    1025             : 
    1026           0 :   virtual bool MatchesCacheId(CacheId aCacheId) const override
    1027             :   {
    1028           0 :     return aCacheId == mCacheId;
    1029             :   }
    1030             : 
    1031             : private:
    1032             :   const CacheId mCacheId;
    1033             :   const CacheDeleteArgs mArgs;
    1034             :   bool mSuccess;
    1035             :   nsTArray<nsID> mDeletedBodyIdList;
    1036             : };
    1037             : 
    1038             : // ----------------------------------------------------------------------------
    1039             : 
    1040           0 : class Manager::CacheKeysAction final : public Manager::BaseAction
    1041             : {
    1042             : public:
    1043           0 :   CacheKeysAction(Manager* aManager, ListenerId aListenerId,
    1044             :                   CacheId aCacheId, const CacheKeysArgs& aArgs,
    1045             :                   StreamList* aStreamList)
    1046           0 :     : BaseAction(aManager, aListenerId)
    1047             :     , mCacheId(aCacheId)
    1048             :     , mArgs(aArgs)
    1049           0 :     , mStreamList(aStreamList)
    1050           0 :   { }
    1051             : 
    1052             :   virtual nsresult
    1053           0 :   RunSyncWithDBOnTarget(const QuotaInfo& aQuotaInfo, nsIFile* aDBDir,
    1054             :                         mozIStorageConnection* aConn) override
    1055             :   {
    1056           0 :     nsresult rv = db::CacheKeys(aConn, mCacheId, mArgs.requestOrVoid(),
    1057           0 :                                 mArgs.params(), mSavedRequests);
    1058           0 :     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1059             : 
    1060           0 :     for (uint32_t i = 0; i < mSavedRequests.Length(); ++i) {
    1061           0 :       if (!mSavedRequests[i].mHasBodyId
    1062           0 :           || IsHeadRequest(mArgs.requestOrVoid(), mArgs.params())) {
    1063           0 :         mSavedRequests[i].mHasBodyId = false;
    1064           0 :         continue;
    1065             :       }
    1066             : 
    1067           0 :       nsCOMPtr<nsIInputStream> stream;
    1068           0 :       rv = BodyOpen(aQuotaInfo, aDBDir, mSavedRequests[i].mBodyId,
    1069           0 :                     getter_AddRefs(stream));
    1070           0 :       if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1071           0 :       if (NS_WARN_IF(!stream)) { return NS_ERROR_FILE_NOT_FOUND; }
    1072             : 
    1073           0 :       mStreamList->Add(mSavedRequests[i].mBodyId, stream);
    1074             :     }
    1075             : 
    1076           0 :     return rv;
    1077             :   }
    1078             : 
    1079             :   virtual void
    1080           0 :   Complete(Listener* aListener, ErrorResult&& aRv) override
    1081             :   {
    1082           0 :     mStreamList->Activate(mCacheId);
    1083           0 :     aListener->OnOpComplete(Move(aRv), CacheKeysResult(), mSavedRequests,
    1084           0 :                             mStreamList);
    1085           0 :     mStreamList = nullptr;
    1086           0 :   }
    1087             : 
    1088           0 :   virtual bool MatchesCacheId(CacheId aCacheId) const override
    1089             :   {
    1090           0 :     return aCacheId == mCacheId;
    1091             :   }
    1092             : 
    1093             : private:
    1094             :   const CacheId mCacheId;
    1095             :   const CacheKeysArgs mArgs;
    1096             :   RefPtr<StreamList> mStreamList;
    1097             :   nsTArray<SavedRequest> mSavedRequests;
    1098             : };
    1099             : 
    1100             : // ----------------------------------------------------------------------------
    1101             : 
    1102           0 : class Manager::StorageMatchAction final : public Manager::BaseAction
    1103             : {
    1104             : public:
    1105           0 :   StorageMatchAction(Manager* aManager, ListenerId aListenerId,
    1106             :                      Namespace aNamespace,
    1107             :                      const StorageMatchArgs& aArgs,
    1108             :                      StreamList* aStreamList)
    1109           0 :     : BaseAction(aManager, aListenerId)
    1110             :     , mNamespace(aNamespace)
    1111             :     , mArgs(aArgs)
    1112             :     , mStreamList(aStreamList)
    1113           0 :     , mFoundResponse(false)
    1114           0 :   { }
    1115             : 
    1116             :   virtual nsresult
    1117           0 :   RunSyncWithDBOnTarget(const QuotaInfo& aQuotaInfo, nsIFile* aDBDir,
    1118             :                         mozIStorageConnection* aConn) override
    1119             :   {
    1120           0 :     nsresult rv = db::StorageMatch(aConn, mNamespace, mArgs.request(),
    1121           0 :                                    mArgs.params(), &mFoundResponse,
    1122           0 :                                    &mSavedResponse);
    1123           0 :     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1124             : 
    1125           0 :     if (!mFoundResponse || !mSavedResponse.mHasBodyId
    1126           0 :                         || IsHeadRequest(mArgs.request(), mArgs.params())) {
    1127           0 :       mSavedResponse.mHasBodyId = false;
    1128           0 :       return rv;
    1129             :     }
    1130             : 
    1131           0 :     nsCOMPtr<nsIInputStream> stream;
    1132           0 :     rv = BodyOpen(aQuotaInfo, aDBDir, mSavedResponse.mBodyId,
    1133           0 :                   getter_AddRefs(stream));
    1134           0 :     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1135           0 :     if (NS_WARN_IF(!stream)) { return NS_ERROR_FILE_NOT_FOUND; }
    1136             : 
    1137           0 :     mStreamList->Add(mSavedResponse.mBodyId, stream);
    1138             : 
    1139           0 :     return rv;
    1140             :   }
    1141             : 
    1142             :   virtual void
    1143           0 :   Complete(Listener* aListener, ErrorResult&& aRv) override
    1144             :   {
    1145           0 :     if (!mFoundResponse) {
    1146           0 :       aListener->OnOpComplete(Move(aRv), StorageMatchResult(void_t()));
    1147             :     } else {
    1148           0 :       mStreamList->Activate(mSavedResponse.mCacheId);
    1149           0 :       aListener->OnOpComplete(Move(aRv), StorageMatchResult(void_t()), mSavedResponse,
    1150           0 :                               mStreamList);
    1151             :     }
    1152           0 :     mStreamList = nullptr;
    1153           0 :   }
    1154             : 
    1155             : private:
    1156             :   const Namespace mNamespace;
    1157             :   const StorageMatchArgs mArgs;
    1158             :   RefPtr<StreamList> mStreamList;
    1159             :   bool mFoundResponse;
    1160             :   SavedResponse mSavedResponse;
    1161             : };
    1162             : 
    1163             : // ----------------------------------------------------------------------------
    1164             : 
    1165           0 : class Manager::StorageHasAction final : public Manager::BaseAction
    1166             : {
    1167             : public:
    1168           0 :   StorageHasAction(Manager* aManager, ListenerId aListenerId,
    1169             :                    Namespace aNamespace, const StorageHasArgs& aArgs)
    1170           0 :     : BaseAction(aManager, aListenerId)
    1171             :     , mNamespace(aNamespace)
    1172             :     , mArgs(aArgs)
    1173           0 :     , mCacheFound(false)
    1174           0 :   { }
    1175             : 
    1176             :   virtual nsresult
    1177           0 :   RunSyncWithDBOnTarget(const QuotaInfo& aQuotaInfo, nsIFile* aDBDir,
    1178             :                         mozIStorageConnection* aConn) override
    1179             :   {
    1180             :     CacheId cacheId;
    1181           0 :     return db::StorageGetCacheId(aConn, mNamespace, mArgs.key(),
    1182           0 :                                  &mCacheFound, &cacheId);
    1183             :   }
    1184             : 
    1185             :   virtual void
    1186           0 :   Complete(Listener* aListener, ErrorResult&& aRv) override
    1187             :   {
    1188           0 :     aListener->OnOpComplete(Move(aRv), StorageHasResult(mCacheFound));
    1189           0 :   }
    1190             : 
    1191             : private:
    1192             :   const Namespace mNamespace;
    1193             :   const StorageHasArgs mArgs;
    1194             :   bool mCacheFound;
    1195             : };
    1196             : 
    1197             : // ----------------------------------------------------------------------------
    1198             : 
    1199           0 : class Manager::StorageOpenAction final : public Manager::BaseAction
    1200             : {
    1201             : public:
    1202           0 :   StorageOpenAction(Manager* aManager, ListenerId aListenerId,
    1203             :                     Namespace aNamespace, const StorageOpenArgs& aArgs)
    1204           0 :     : BaseAction(aManager, aListenerId)
    1205             :     , mNamespace(aNamespace)
    1206             :     , mArgs(aArgs)
    1207           0 :     , mCacheId(INVALID_CACHE_ID)
    1208           0 :   { }
    1209             : 
    1210             :   virtual nsresult
    1211           0 :   RunSyncWithDBOnTarget(const QuotaInfo& aQuotaInfo, nsIFile* aDBDir,
    1212             :                         mozIStorageConnection* aConn) override
    1213             :   {
    1214             :     // Cache does not exist, create it instead
    1215             :     mozStorageTransaction trans(aConn, false,
    1216           0 :                                 mozIStorageConnection::TRANSACTION_IMMEDIATE);
    1217             : 
    1218             :     // Look for existing cache
    1219             :     bool cacheFound;
    1220           0 :     nsresult rv = db::StorageGetCacheId(aConn, mNamespace, mArgs.key(),
    1221           0 :                                         &cacheFound, &mCacheId);
    1222           0 :     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1223           0 :     if (cacheFound) {
    1224           0 :       MOZ_DIAGNOSTIC_ASSERT(mCacheId != INVALID_CACHE_ID);
    1225           0 :       return rv;
    1226             :     }
    1227             : 
    1228           0 :     rv = db::CreateCacheId(aConn, &mCacheId);
    1229           0 :     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1230             : 
    1231           0 :     rv = db::StoragePutCache(aConn, mNamespace, mArgs.key(), mCacheId);
    1232           0 :     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1233             : 
    1234           0 :     rv = trans.Commit();
    1235           0 :     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1236             : 
    1237           0 :     MOZ_DIAGNOSTIC_ASSERT(mCacheId != INVALID_CACHE_ID);
    1238           0 :     return rv;
    1239             :   }
    1240             : 
    1241             :   virtual void
    1242           0 :   Complete(Listener* aListener, ErrorResult&& aRv) override
    1243             :   {
    1244           0 :     MOZ_DIAGNOSTIC_ASSERT(aRv.Failed() || mCacheId != INVALID_CACHE_ID);
    1245           0 :     aListener->OnOpComplete(Move(aRv), StorageOpenResult(), mCacheId);
    1246           0 :   }
    1247             : 
    1248             : private:
    1249             :   const Namespace mNamespace;
    1250             :   const StorageOpenArgs mArgs;
    1251             :   CacheId mCacheId;
    1252             : };
    1253             : 
    1254             : // ----------------------------------------------------------------------------
    1255             : 
    1256           0 : class Manager::StorageDeleteAction final : public Manager::BaseAction
    1257             : {
    1258             : public:
    1259           0 :   StorageDeleteAction(Manager* aManager, ListenerId aListenerId,
    1260             :                       Namespace aNamespace, const StorageDeleteArgs& aArgs)
    1261           0 :     : BaseAction(aManager, aListenerId)
    1262             :     , mNamespace(aNamespace)
    1263             :     , mArgs(aArgs)
    1264             :     , mCacheDeleted(false)
    1265           0 :     , mCacheId(INVALID_CACHE_ID)
    1266           0 :   { }
    1267             : 
    1268             :   virtual nsresult
    1269           0 :   RunSyncWithDBOnTarget(const QuotaInfo& aQuotaInfo, nsIFile* aDBDir,
    1270             :                         mozIStorageConnection* aConn) override
    1271             :   {
    1272             :     mozStorageTransaction trans(aConn, false,
    1273           0 :                                 mozIStorageConnection::TRANSACTION_IMMEDIATE);
    1274             : 
    1275             :     bool exists;
    1276           0 :     nsresult rv = db::StorageGetCacheId(aConn, mNamespace, mArgs.key(),
    1277           0 :                                         &exists, &mCacheId);
    1278           0 :     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1279             : 
    1280           0 :     if (!exists) {
    1281           0 :       mCacheDeleted = false;
    1282           0 :       return NS_OK;
    1283             :     }
    1284             : 
    1285           0 :     rv = db::StorageForgetCache(aConn, mNamespace, mArgs.key());
    1286           0 :     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1287             : 
    1288           0 :     rv = trans.Commit();
    1289           0 :     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1290             : 
    1291           0 :     mCacheDeleted = true;
    1292           0 :     return rv;
    1293             :   }
    1294             : 
    1295             :   virtual void
    1296           0 :   Complete(Listener* aListener, ErrorResult&& aRv) override
    1297             :   {
    1298           0 :     if (mCacheDeleted) {
    1299             :       // If content is referencing this cache, mark it orphaned to be
    1300             :       // deleted later.
    1301           0 :       if (!mManager->SetCacheIdOrphanedIfRefed(mCacheId)) {
    1302             : 
    1303             :         // no outstanding references, delete immediately
    1304           0 :         RefPtr<Context> context = mManager->mContext;
    1305             : 
    1306           0 :         if (context->IsCanceled()) {
    1307           0 :           context->NoteOrphanedData();
    1308             :         } else {
    1309           0 :           context->CancelForCacheId(mCacheId);
    1310             :           RefPtr<Action> action =
    1311           0 :             new DeleteOrphanedCacheAction(mManager, mCacheId);
    1312           0 :           context->Dispatch(action);
    1313             :         }
    1314             :       }
    1315             :     }
    1316             : 
    1317           0 :     aListener->OnOpComplete(Move(aRv), StorageDeleteResult(mCacheDeleted));
    1318           0 :   }
    1319             : 
    1320             : private:
    1321             :   const Namespace mNamespace;
    1322             :   const StorageDeleteArgs mArgs;
    1323             :   bool mCacheDeleted;
    1324             :   CacheId mCacheId;
    1325             : };
    1326             : 
    1327             : // ----------------------------------------------------------------------------
    1328             : 
    1329           0 : class Manager::StorageKeysAction final : public Manager::BaseAction
    1330             : {
    1331             : public:
    1332           0 :   StorageKeysAction(Manager* aManager, ListenerId aListenerId,
    1333             :                     Namespace aNamespace)
    1334           0 :     : BaseAction(aManager, aListenerId)
    1335           0 :     , mNamespace(aNamespace)
    1336           0 :   { }
    1337             : 
    1338             :   virtual nsresult
    1339           0 :   RunSyncWithDBOnTarget(const QuotaInfo& aQuotaInfo, nsIFile* aDBDir,
    1340             :                         mozIStorageConnection* aConn) override
    1341             :   {
    1342           0 :     return db::StorageGetKeys(aConn, mNamespace, mKeys);
    1343             :   }
    1344             : 
    1345             :   virtual void
    1346           0 :   Complete(Listener* aListener, ErrorResult&& aRv) override
    1347             :   {
    1348           0 :     if (aRv.Failed()) {
    1349           0 :       mKeys.Clear();
    1350             :     }
    1351           0 :     aListener->OnOpComplete(Move(aRv), StorageKeysResult(mKeys));
    1352           0 :   }
    1353             : 
    1354             : private:
    1355             :   const Namespace mNamespace;
    1356             :   nsTArray<nsString> mKeys;
    1357             : };
    1358             : 
    1359             : // ----------------------------------------------------------------------------
    1360             : 
    1361             : //static
    1362             : Manager::ListenerId Manager::sNextListenerId = 0;
    1363             : 
    1364             : void
    1365           0 : Manager::Listener::OnOpComplete(ErrorResult&& aRv, const CacheOpResult& aResult)
    1366             : {
    1367           0 :   OnOpComplete(Move(aRv), aResult, INVALID_CACHE_ID, nsTArray<SavedResponse>(),
    1368           0 :                nsTArray<SavedRequest>(), nullptr);
    1369           0 : }
    1370             : 
    1371             : void
    1372           0 : Manager::Listener::OnOpComplete(ErrorResult&& aRv, const CacheOpResult& aResult,
    1373             :                                 CacheId aOpenedCacheId)
    1374             : {
    1375           0 :   OnOpComplete(Move(aRv), aResult, aOpenedCacheId, nsTArray<SavedResponse>(),
    1376           0 :                nsTArray<SavedRequest>(), nullptr);
    1377           0 : }
    1378             : 
    1379             : void
    1380           0 : Manager::Listener::OnOpComplete(ErrorResult&& aRv, const CacheOpResult& aResult,
    1381             :                                 const SavedResponse& aSavedResponse,
    1382             :                                 StreamList* aStreamList)
    1383             : {
    1384           0 :   AutoTArray<SavedResponse, 1> responseList;
    1385           0 :   responseList.AppendElement(aSavedResponse);
    1386           0 :   OnOpComplete(Move(aRv), aResult, INVALID_CACHE_ID, responseList,
    1387           0 :                nsTArray<SavedRequest>(), aStreamList);
    1388           0 : }
    1389             : 
    1390             : void
    1391           0 : Manager::Listener::OnOpComplete(ErrorResult&& aRv, const CacheOpResult& aResult,
    1392             :                                 const nsTArray<SavedResponse>& aSavedResponseList,
    1393             :                                 StreamList* aStreamList)
    1394             : {
    1395           0 :   OnOpComplete(Move(aRv), aResult, INVALID_CACHE_ID, aSavedResponseList,
    1396           0 :                nsTArray<SavedRequest>(), aStreamList);
    1397           0 : }
    1398             : 
    1399             : void
    1400           0 : Manager::Listener::OnOpComplete(ErrorResult&& aRv, const CacheOpResult& aResult,
    1401             :                                 const nsTArray<SavedRequest>& aSavedRequestList,
    1402             :                                 StreamList* aStreamList)
    1403             : {
    1404           0 :   OnOpComplete(Move(aRv), aResult, INVALID_CACHE_ID, nsTArray<SavedResponse>(),
    1405           0 :                aSavedRequestList, aStreamList);
    1406           0 : }
    1407             : 
    1408             : // static
    1409             : nsresult
    1410           0 : Manager::GetOrCreate(ManagerId* aManagerId, Manager** aManagerOut)
    1411             : {
    1412           0 :   mozilla::ipc::AssertIsOnBackgroundThread();
    1413           0 :   return Factory::GetOrCreate(aManagerId, aManagerOut);
    1414             : }
    1415             : 
    1416             : // static
    1417             : already_AddRefed<Manager>
    1418           0 : Manager::Get(ManagerId* aManagerId)
    1419             : {
    1420           0 :   mozilla::ipc::AssertIsOnBackgroundThread();
    1421           0 :   return Factory::Get(aManagerId);
    1422             : }
    1423             : 
    1424             : // static
    1425             : void
    1426           0 : Manager::ShutdownAll()
    1427             : {
    1428           0 :   mozilla::ipc::AssertIsOnBackgroundThread();
    1429             : 
    1430           0 :   Factory::ShutdownAll();
    1431             : 
    1432           0 :   if (!mozilla::SpinEventLoopUntil([]() {
    1433             :         return Factory::IsShutdownAllComplete();
    1434           0 :       })) {
    1435           0 :     NS_WARNING("Something bad happened!");
    1436             :   }
    1437           0 : }
    1438             : 
    1439             : // static
    1440             : void
    1441           0 : Manager::Abort(const nsACString& aOrigin)
    1442             : {
    1443           0 :   mozilla::ipc::AssertIsOnBackgroundThread();
    1444             : 
    1445           0 :   Factory::Abort(aOrigin);
    1446           0 : }
    1447             : 
    1448             : void
    1449           0 : Manager::RemoveListener(Listener* aListener)
    1450             : {
    1451           0 :   NS_ASSERT_OWNINGTHREAD(Manager);
    1452             :   // There may not be a listener here in the case where an actor is killed
    1453             :   // before it can perform any actual async requests on Manager.
    1454           0 :   mListeners.RemoveElement(aListener, ListenerEntryListenerComparator());
    1455           0 :   MOZ_ASSERT(!mListeners.Contains(aListener,
    1456             :                                   ListenerEntryListenerComparator()));
    1457           0 :   MaybeAllowContextToClose();
    1458           0 : }
    1459             : 
    1460             : void
    1461           0 : Manager::RemoveContext(Context* aContext)
    1462             : {
    1463           0 :   NS_ASSERT_OWNINGTHREAD(Manager);
    1464           0 :   MOZ_DIAGNOSTIC_ASSERT(mContext);
    1465           0 :   MOZ_DIAGNOSTIC_ASSERT(mContext == aContext);
    1466             : 
    1467             :   // Whether the Context destruction was triggered from the Manager going
    1468             :   // idle or the underlying storage being invalidated, we should know we
    1469             :   // are closing before the Context is destroyed.
    1470           0 :   MOZ_DIAGNOSTIC_ASSERT(mState == Closing);
    1471             : 
    1472             :   // Before forgetting the Context, check to see if we have any outstanding
    1473             :   // cache or body objects waiting for deletion.  If so, note that we've
    1474             :   // orphaned data so it will be cleaned up on the next open.
    1475           0 :   for (uint32_t i = 0; i < mCacheIdRefs.Length(); ++i) {
    1476           0 :     if (mCacheIdRefs[i].mOrphaned) {
    1477           0 :       aContext->NoteOrphanedData();
    1478           0 :       break;
    1479             :     }
    1480             :   }
    1481             : 
    1482           0 :   for (uint32_t i = 0; i < mBodyIdRefs.Length(); ++i) {
    1483           0 :     if (mBodyIdRefs[i].mOrphaned) {
    1484           0 :       aContext->NoteOrphanedData();
    1485           0 :       break;
    1486             :     }
    1487             :   }
    1488             : 
    1489           0 :   mContext = nullptr;
    1490             : 
    1491             :   // Once the context is gone, we can immediately remove ourself from the
    1492             :   // Factory list.  We don't need to block shutdown by staying in the list
    1493             :   // any more.
    1494           0 :   Factory::Remove(this);
    1495           0 : }
    1496             : 
    1497             : void
    1498           0 : Manager::NoteClosing()
    1499             : {
    1500           0 :   NS_ASSERT_OWNINGTHREAD(Manager);
    1501             :   // This can be called more than once legitimately through different paths.
    1502           0 :   mState = Closing;
    1503           0 : }
    1504             : 
    1505             : Manager::State
    1506           0 : Manager::GetState() const
    1507             : {
    1508           0 :   NS_ASSERT_OWNINGTHREAD(Manager);
    1509           0 :   return mState;
    1510             : }
    1511             : 
    1512             : void
    1513           0 : Manager::AddRefCacheId(CacheId aCacheId)
    1514             : {
    1515           0 :   NS_ASSERT_OWNINGTHREAD(Manager);
    1516           0 :   for (uint32_t i = 0; i < mCacheIdRefs.Length(); ++i) {
    1517           0 :     if (mCacheIdRefs[i].mCacheId == aCacheId) {
    1518           0 :       mCacheIdRefs[i].mCount += 1;
    1519           0 :       return;
    1520             :     }
    1521             :   }
    1522           0 :   CacheIdRefCounter* entry = mCacheIdRefs.AppendElement();
    1523           0 :   entry->mCacheId = aCacheId;
    1524           0 :   entry->mCount = 1;
    1525           0 :   entry->mOrphaned = false;
    1526             : }
    1527             : 
    1528             : void
    1529           0 : Manager::ReleaseCacheId(CacheId aCacheId)
    1530             : {
    1531           0 :   NS_ASSERT_OWNINGTHREAD(Manager);
    1532           0 :   for (uint32_t i = 0; i < mCacheIdRefs.Length(); ++i) {
    1533           0 :     if (mCacheIdRefs[i].mCacheId == aCacheId) {
    1534             : #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
    1535           0 :       uint32_t oldRef = mCacheIdRefs[i].mCount;
    1536             : #endif
    1537           0 :       mCacheIdRefs[i].mCount -= 1;
    1538           0 :       MOZ_DIAGNOSTIC_ASSERT(mCacheIdRefs[i].mCount < oldRef);
    1539           0 :       if (mCacheIdRefs[i].mCount == 0) {
    1540           0 :         bool orphaned = mCacheIdRefs[i].mOrphaned;
    1541           0 :         mCacheIdRefs.RemoveElementAt(i);
    1542           0 :         RefPtr<Context> context = mContext;
    1543             :         // If the context is already gone, then orphan flag should have been
    1544             :         // set in RemoveContext().
    1545           0 :         if (orphaned && context) {
    1546           0 :           if (context->IsCanceled()) {
    1547           0 :             context->NoteOrphanedData();
    1548             :           } else {
    1549           0 :             context->CancelForCacheId(aCacheId);
    1550             :             RefPtr<Action> action = new DeleteOrphanedCacheAction(this,
    1551           0 :                                                                     aCacheId);
    1552           0 :             context->Dispatch(action);
    1553             :           }
    1554             :         }
    1555             :       }
    1556           0 :       MaybeAllowContextToClose();
    1557           0 :       return;
    1558             :     }
    1559             :   }
    1560           0 :   MOZ_ASSERT_UNREACHABLE("Attempt to release CacheId that is not referenced!");
    1561             : }
    1562             : 
    1563             : void
    1564           0 : Manager::AddRefBodyId(const nsID& aBodyId)
    1565             : {
    1566           0 :   NS_ASSERT_OWNINGTHREAD(Manager);
    1567           0 :   for (uint32_t i = 0; i < mBodyIdRefs.Length(); ++i) {
    1568           0 :     if (mBodyIdRefs[i].mBodyId == aBodyId) {
    1569           0 :       mBodyIdRefs[i].mCount += 1;
    1570           0 :       return;
    1571             :     }
    1572             :   }
    1573           0 :   BodyIdRefCounter* entry = mBodyIdRefs.AppendElement();
    1574           0 :   entry->mBodyId = aBodyId;
    1575           0 :   entry->mCount = 1;
    1576           0 :   entry->mOrphaned = false;
    1577             : }
    1578             : 
    1579             : void
    1580           0 : Manager::ReleaseBodyId(const nsID& aBodyId)
    1581             : {
    1582           0 :   NS_ASSERT_OWNINGTHREAD(Manager);
    1583           0 :   for (uint32_t i = 0; i < mBodyIdRefs.Length(); ++i) {
    1584           0 :     if (mBodyIdRefs[i].mBodyId == aBodyId) {
    1585             : #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
    1586           0 :       uint32_t oldRef = mBodyIdRefs[i].mCount;
    1587             : #endif
    1588           0 :       mBodyIdRefs[i].mCount -= 1;
    1589           0 :       MOZ_DIAGNOSTIC_ASSERT(mBodyIdRefs[i].mCount < oldRef);
    1590           0 :       if (mBodyIdRefs[i].mCount < 1) {
    1591           0 :         bool orphaned = mBodyIdRefs[i].mOrphaned;
    1592           0 :         mBodyIdRefs.RemoveElementAt(i);
    1593           0 :         RefPtr<Context> context = mContext;
    1594             :         // If the context is already gone, then orphan flag should have been
    1595             :         // set in RemoveContext().
    1596           0 :         if (orphaned && context) {
    1597           0 :           if (context->IsCanceled()) {
    1598           0 :             context->NoteOrphanedData();
    1599             :           } else {
    1600           0 :             RefPtr<Action> action = new DeleteOrphanedBodyAction(aBodyId);
    1601           0 :             context->Dispatch(action);
    1602             :           }
    1603             :         }
    1604             :       }
    1605           0 :       MaybeAllowContextToClose();
    1606           0 :       return;
    1607             :     }
    1608             :   }
    1609           0 :   MOZ_ASSERT_UNREACHABLE("Attempt to release BodyId that is not referenced!");
    1610             : }
    1611             : 
    1612             : already_AddRefed<ManagerId>
    1613           0 : Manager::GetManagerId() const
    1614             : {
    1615           0 :   RefPtr<ManagerId> ref = mManagerId;
    1616           0 :   return ref.forget();
    1617             : }
    1618             : 
    1619             : void
    1620           0 : Manager::AddStreamList(StreamList* aStreamList)
    1621             : {
    1622           0 :   NS_ASSERT_OWNINGTHREAD(Manager);
    1623           0 :   MOZ_DIAGNOSTIC_ASSERT(aStreamList);
    1624           0 :   mStreamLists.AppendElement(aStreamList);
    1625           0 : }
    1626             : 
    1627             : void
    1628           0 : Manager::RemoveStreamList(StreamList* aStreamList)
    1629             : {
    1630           0 :   NS_ASSERT_OWNINGTHREAD(Manager);
    1631           0 :   MOZ_DIAGNOSTIC_ASSERT(aStreamList);
    1632           0 :   mStreamLists.RemoveElement(aStreamList);
    1633           0 : }
    1634             : 
    1635             : void
    1636           0 : Manager::ExecuteCacheOp(Listener* aListener, CacheId aCacheId,
    1637             :                         const CacheOpArgs& aOpArgs)
    1638             : {
    1639           0 :   NS_ASSERT_OWNINGTHREAD(Manager);
    1640           0 :   MOZ_DIAGNOSTIC_ASSERT(aListener);
    1641           0 :   MOZ_DIAGNOSTIC_ASSERT(aOpArgs.type() != CacheOpArgs::TCachePutAllArgs);
    1642             : 
    1643           0 :   if (NS_WARN_IF(mState == Closing)) {
    1644           0 :     aListener->OnOpComplete(ErrorResult(NS_ERROR_FAILURE), void_t());
    1645           0 :     return;
    1646             :   }
    1647             : 
    1648           0 :   RefPtr<Context> context = mContext;
    1649           0 :   MOZ_DIAGNOSTIC_ASSERT(!context->IsCanceled());
    1650             : 
    1651           0 :   RefPtr<StreamList> streamList = new StreamList(this, context);
    1652           0 :   ListenerId listenerId = SaveListener(aListener);
    1653             : 
    1654           0 :   RefPtr<Action> action;
    1655           0 :   switch(aOpArgs.type()) {
    1656             :     case CacheOpArgs::TCacheMatchArgs:
    1657             :       action = new CacheMatchAction(this, listenerId, aCacheId,
    1658           0 :                                     aOpArgs.get_CacheMatchArgs(), streamList);
    1659           0 :       break;
    1660             :     case CacheOpArgs::TCacheMatchAllArgs:
    1661             :       action = new CacheMatchAllAction(this, listenerId, aCacheId,
    1662             :                                        aOpArgs.get_CacheMatchAllArgs(),
    1663           0 :                                        streamList);
    1664           0 :       break;
    1665             :     case CacheOpArgs::TCacheDeleteArgs:
    1666             :       action = new CacheDeleteAction(this, listenerId, aCacheId,
    1667           0 :                                      aOpArgs.get_CacheDeleteArgs());
    1668           0 :       break;
    1669             :     case CacheOpArgs::TCacheKeysArgs:
    1670             :       action = new CacheKeysAction(this, listenerId, aCacheId,
    1671           0 :                                    aOpArgs.get_CacheKeysArgs(), streamList);
    1672           0 :       break;
    1673             :     default:
    1674           0 :       MOZ_CRASH("Unknown Cache operation!");
    1675             :   }
    1676             : 
    1677           0 :   context->Dispatch(action);
    1678             : }
    1679             : 
    1680             : void
    1681           0 : Manager::ExecuteStorageOp(Listener* aListener, Namespace aNamespace,
    1682             :                           const CacheOpArgs& aOpArgs)
    1683             : {
    1684           0 :   NS_ASSERT_OWNINGTHREAD(Manager);
    1685           0 :   MOZ_DIAGNOSTIC_ASSERT(aListener);
    1686             : 
    1687           0 :   if (NS_WARN_IF(mState == Closing)) {
    1688           0 :     aListener->OnOpComplete(ErrorResult(NS_ERROR_FAILURE), void_t());
    1689           0 :     return;
    1690             :   }
    1691             : 
    1692           0 :   RefPtr<Context> context = mContext;
    1693           0 :   MOZ_DIAGNOSTIC_ASSERT(!context->IsCanceled());
    1694             : 
    1695           0 :   RefPtr<StreamList> streamList = new StreamList(this, context);
    1696           0 :   ListenerId listenerId = SaveListener(aListener);
    1697             : 
    1698           0 :   RefPtr<Action> action;
    1699           0 :   switch(aOpArgs.type()) {
    1700             :     case CacheOpArgs::TStorageMatchArgs:
    1701             :       action = new StorageMatchAction(this, listenerId, aNamespace,
    1702             :                                       aOpArgs.get_StorageMatchArgs(),
    1703           0 :                                       streamList);
    1704           0 :       break;
    1705             :     case CacheOpArgs::TStorageHasArgs:
    1706             :       action = new StorageHasAction(this, listenerId, aNamespace,
    1707           0 :                                     aOpArgs.get_StorageHasArgs());
    1708           0 :       break;
    1709             :     case CacheOpArgs::TStorageOpenArgs:
    1710             :       action = new StorageOpenAction(this, listenerId, aNamespace,
    1711           0 :                                      aOpArgs.get_StorageOpenArgs());
    1712           0 :       break;
    1713             :     case CacheOpArgs::TStorageDeleteArgs:
    1714             :       action = new StorageDeleteAction(this, listenerId, aNamespace,
    1715           0 :                                        aOpArgs.get_StorageDeleteArgs());
    1716           0 :       break;
    1717             :     case CacheOpArgs::TStorageKeysArgs:
    1718           0 :       action = new StorageKeysAction(this, listenerId, aNamespace);
    1719           0 :       break;
    1720             :     default:
    1721           0 :       MOZ_CRASH("Unknown CacheStorage operation!");
    1722             :   }
    1723             : 
    1724           0 :   context->Dispatch(action);
    1725             : }
    1726             : 
    1727             : void
    1728           0 : Manager::ExecutePutAll(Listener* aListener, CacheId aCacheId,
    1729             :                        const nsTArray<CacheRequestResponse>& aPutList,
    1730             :                        const nsTArray<nsCOMPtr<nsIInputStream>>& aRequestStreamList,
    1731             :                        const nsTArray<nsCOMPtr<nsIInputStream>>& aResponseStreamList)
    1732             : {
    1733           0 :   NS_ASSERT_OWNINGTHREAD(Manager);
    1734           0 :   MOZ_DIAGNOSTIC_ASSERT(aListener);
    1735             : 
    1736           0 :   if (NS_WARN_IF(mState == Closing)) {
    1737           0 :     aListener->OnOpComplete(ErrorResult(NS_ERROR_FAILURE), CachePutAllResult());
    1738           0 :     return;
    1739             :   }
    1740             : 
    1741           0 :   RefPtr<Context> context = mContext;
    1742           0 :   MOZ_DIAGNOSTIC_ASSERT(!context->IsCanceled());
    1743             : 
    1744           0 :   ListenerId listenerId = SaveListener(aListener);
    1745             : 
    1746             :   RefPtr<Action> action = new CachePutAllAction(this, listenerId, aCacheId,
    1747             :                                                   aPutList, aRequestStreamList,
    1748           0 :                                                   aResponseStreamList);
    1749             : 
    1750           0 :   context->Dispatch(action);
    1751             : }
    1752             : 
    1753           0 : Manager::Manager(ManagerId* aManagerId, nsIThread* aIOThread)
    1754             :   : mManagerId(aManagerId)
    1755             :   , mIOThread(aIOThread)
    1756             :   , mContext(nullptr)
    1757             :   , mShuttingDown(false)
    1758           0 :   , mState(Open)
    1759             : {
    1760           0 :   MOZ_DIAGNOSTIC_ASSERT(mManagerId);
    1761           0 :   MOZ_DIAGNOSTIC_ASSERT(mIOThread);
    1762           0 : }
    1763             : 
    1764           0 : Manager::~Manager()
    1765             : {
    1766           0 :   NS_ASSERT_OWNINGTHREAD(Manager);
    1767           0 :   MOZ_DIAGNOSTIC_ASSERT(mState == Closing);
    1768           0 :   MOZ_DIAGNOSTIC_ASSERT(!mContext);
    1769             : 
    1770           0 :   nsCOMPtr<nsIThread> ioThread;
    1771           0 :   mIOThread.swap(ioThread);
    1772             : 
    1773             :   // Don't spin the event loop in the destructor waiting for the thread to
    1774             :   // shutdown.  Defer this to the main thread, instead.
    1775           0 :   MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(NewRunnableMethod("nsIThread::Shutdown",
    1776             :                                                                 ioThread, &nsIThread::Shutdown)));
    1777           0 : }
    1778             : 
    1779             : void
    1780           0 : Manager::Init(Manager* aOldManager)
    1781             : {
    1782           0 :   NS_ASSERT_OWNINGTHREAD(Manager);
    1783             : 
    1784           0 :   RefPtr<Context> oldContext;
    1785           0 :   if (aOldManager) {
    1786           0 :     oldContext = aOldManager->mContext;
    1787             :   }
    1788             : 
    1789             :   // Create the context immediately.  Since there can at most be one Context
    1790             :   // per Manager now, this lets us cleanly call Factory::Remove() once the
    1791             :   // Context goes away.
    1792           0 :   RefPtr<Action> setupAction = new SetupAction();
    1793           0 :   RefPtr<Context> ref = Context::Create(this, mIOThread->SerialEventTarget(), setupAction,
    1794           0 :                                         oldContext);
    1795           0 :   mContext = ref;
    1796           0 : }
    1797             : 
    1798             : void
    1799           0 : Manager::Shutdown()
    1800             : {
    1801           0 :   NS_ASSERT_OWNINGTHREAD(Manager);
    1802             : 
    1803             :   // Ignore duplicate attempts to shutdown.  This can occur when we start
    1804             :   // a browser initiated shutdown and then run ~Manager() which also
    1805             :   // calls Shutdown().
    1806           0 :   if (mShuttingDown) {
    1807           0 :     return;
    1808             :   }
    1809             : 
    1810           0 :   mShuttingDown = true;
    1811             : 
    1812             :   // Note that we are closing to prevent any new requests from coming in and
    1813             :   // creating a new Context.  We must ensure all Contexts and IO operations are
    1814             :   // complete before shutdown proceeds.
    1815           0 :   NoteClosing();
    1816             : 
    1817             :   // If there is a context, then cancel and only note that we are done after
    1818             :   // its cleaned up.
    1819           0 :   if (mContext) {
    1820           0 :     RefPtr<Context> context = mContext;
    1821           0 :     context->CancelAll();
    1822           0 :     return;
    1823             :   }
    1824             : }
    1825             : 
    1826             : void
    1827           0 : Manager::Abort()
    1828             : {
    1829           0 :   NS_ASSERT_OWNINGTHREAD(Manager);
    1830           0 :   MOZ_DIAGNOSTIC_ASSERT(mContext);
    1831             : 
    1832             :   // Note that we are closing to prevent any new requests from coming in and
    1833             :   // creating a new Context.  We must ensure all Contexts and IO operations are
    1834             :   // complete before origin clear proceeds.
    1835           0 :   NoteClosing();
    1836             : 
    1837             :   // Cancel and only note that we are done after the context is cleaned up.
    1838           0 :   RefPtr<Context> context = mContext;
    1839           0 :   context->CancelAll();
    1840           0 : }
    1841             : 
    1842             : Manager::ListenerId
    1843           0 : Manager::SaveListener(Listener* aListener)
    1844             : {
    1845           0 :   NS_ASSERT_OWNINGTHREAD(Manager);
    1846             : 
    1847             :   // Once a Listener is added, we keep a reference to it until its
    1848             :   // removed.  Since the same Listener might make multiple requests,
    1849             :   // ensure we only have a single reference in our list.
    1850             :   ListenerList::index_type index =
    1851           0 :     mListeners.IndexOf(aListener, 0, ListenerEntryListenerComparator());
    1852           0 :   if (index != ListenerList::NoIndex) {
    1853           0 :     return mListeners[index].mId;
    1854             :   }
    1855             : 
    1856           0 :   ListenerId id = sNextListenerId;
    1857           0 :   sNextListenerId += 1;
    1858             : 
    1859           0 :   mListeners.AppendElement(ListenerEntry(id, aListener));
    1860           0 :   return id;
    1861             : }
    1862             : 
    1863             : Manager::Listener*
    1864           0 : Manager::GetListener(ListenerId aListenerId) const
    1865             : {
    1866           0 :   NS_ASSERT_OWNINGTHREAD(Manager);
    1867             :   ListenerList::index_type index =
    1868           0 :     mListeners.IndexOf(aListenerId, 0, ListenerEntryIdComparator());
    1869           0 :   if (index != ListenerList::NoIndex) {
    1870           0 :     return mListeners[index].mListener;
    1871             :   }
    1872             : 
    1873             :   // This can legitimately happen if the actor is deleted while a request is
    1874             :   // in process.  For example, the child process OOMs.
    1875           0 :   return nullptr;
    1876             : }
    1877             : 
    1878             : bool
    1879           0 : Manager::SetCacheIdOrphanedIfRefed(CacheId aCacheId)
    1880             : {
    1881           0 :   NS_ASSERT_OWNINGTHREAD(Manager);
    1882           0 :   for (uint32_t i = 0; i < mCacheIdRefs.Length(); ++i) {
    1883           0 :     if (mCacheIdRefs[i].mCacheId == aCacheId) {
    1884           0 :       MOZ_DIAGNOSTIC_ASSERT(mCacheIdRefs[i].mCount > 0);
    1885           0 :       MOZ_DIAGNOSTIC_ASSERT(!mCacheIdRefs[i].mOrphaned);
    1886           0 :       mCacheIdRefs[i].mOrphaned = true;
    1887           0 :       return true;
    1888             :     }
    1889             :   }
    1890           0 :   return false;
    1891             : }
    1892             : 
    1893             : // TODO: provide way to set body non-orphaned if its added back to a cache (bug 1110479)
    1894             : 
    1895             : bool
    1896           0 : Manager::SetBodyIdOrphanedIfRefed(const nsID& aBodyId)
    1897             : {
    1898           0 :   NS_ASSERT_OWNINGTHREAD(Manager);
    1899           0 :   for (uint32_t i = 0; i < mBodyIdRefs.Length(); ++i) {
    1900           0 :     if (mBodyIdRefs[i].mBodyId == aBodyId) {
    1901           0 :       MOZ_DIAGNOSTIC_ASSERT(mBodyIdRefs[i].mCount > 0);
    1902           0 :       MOZ_DIAGNOSTIC_ASSERT(!mBodyIdRefs[i].mOrphaned);
    1903           0 :       mBodyIdRefs[i].mOrphaned = true;
    1904           0 :       return true;
    1905             :     }
    1906             :   }
    1907           0 :   return false;
    1908             : }
    1909             : 
    1910             : void
    1911           0 : Manager::NoteOrphanedBodyIdList(const nsTArray<nsID>& aDeletedBodyIdList)
    1912             : {
    1913           0 :   NS_ASSERT_OWNINGTHREAD(Manager);
    1914             : 
    1915           0 :   AutoTArray<nsID, 64> deleteNowList;
    1916           0 :   deleteNowList.SetCapacity(aDeletedBodyIdList.Length());
    1917             : 
    1918           0 :   for (uint32_t i = 0; i < aDeletedBodyIdList.Length(); ++i) {
    1919           0 :     if (!SetBodyIdOrphanedIfRefed(aDeletedBodyIdList[i])) {
    1920           0 :       deleteNowList.AppendElement(aDeletedBodyIdList[i]);
    1921             :     }
    1922             :   }
    1923             : 
    1924             :   // TODO: note that we need to check these bodies for staleness on startup (bug 1110446)
    1925           0 :   RefPtr<Context> context = mContext;
    1926           0 :   if (!deleteNowList.IsEmpty() && context && !context->IsCanceled()) {
    1927           0 :     RefPtr<Action> action = new DeleteOrphanedBodyAction(deleteNowList);
    1928           0 :     context->Dispatch(action);
    1929             :   }
    1930           0 : }
    1931             : 
    1932             : void
    1933           0 : Manager::MaybeAllowContextToClose()
    1934             : {
    1935           0 :   NS_ASSERT_OWNINGTHREAD(Manager);
    1936             : 
    1937             :   // If we have an active context, but we have no more users of the Manager,
    1938             :   // then let it shut itself down.  We must wait for all possible users of
    1939             :   // Cache state information to complete before doing this.  Once we allow
    1940             :   // the Context to close we may not reliably get notified of storage
    1941             :   // invalidation.
    1942           0 :   RefPtr<Context> context = mContext;
    1943           0 :   if (context && mListeners.IsEmpty()
    1944           0 :               && mCacheIdRefs.IsEmpty()
    1945           0 :               && mBodyIdRefs.IsEmpty()) {
    1946             : 
    1947             :     // Mark this Manager as invalid so that it won't get used again.  We don't
    1948             :     // want to start any new operations once we allow the Context to close since
    1949             :     // it may race with the underlying storage getting invalidated.
    1950           0 :     NoteClosing();
    1951             : 
    1952           0 :     context->AllowToClose();
    1953             :   }
    1954           0 : }
    1955             : 
    1956             : } // namespace cache
    1957             : } // namespace dom
    1958             : } // namespace mozilla

Generated by: LCOV version 1.13