tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

Manager.cpp (77853B)


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