tor-browser

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

Context.cpp (39422B)


      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/Context.h"
      8 
      9 #include "CacheCommon.h"
     10 #include "NotifyUtils.h"
     11 #include "QuotaClientImpl.h"
     12 #include "mozIStorageConnection.h"
     13 #include "mozilla/AutoRestore.h"
     14 #include "mozilla/Maybe.h"
     15 #include "mozilla/StaticPrefs_dom.h"
     16 #include "mozilla/dom/SafeRefPtr.h"
     17 #include "mozilla/dom/cache/Action.h"
     18 #include "mozilla/dom/cache/FileUtils.h"
     19 #include "mozilla/dom/cache/Manager.h"
     20 #include "mozilla/dom/cache/ManagerId.h"
     21 #include "mozilla/dom/quota/Assertions.h"
     22 #include "mozilla/dom/quota/ClientDirectoryLock.h"
     23 #include "mozilla/dom/quota/ClientDirectoryLockHandle.h"
     24 #include "mozilla/dom/quota/PrincipalUtils.h"
     25 #include "mozilla/dom/quota/QuotaManager.h"
     26 #include "mozilla/dom/quota/ResultExtensions.h"
     27 #include "mozilla/dom/quota/ThreadUtils.h"
     28 #include "mozilla/ipc/PBackgroundSharedTypes.h"
     29 #include "nsIPrincipal.h"
     30 #include "nsIRunnable.h"
     31 #include "nsIThread.h"
     32 #include "nsThreadUtils.h"
     33 
     34 namespace {
     35 
     36 using mozilla::dom::cache::Action;
     37 using mozilla::dom::cache::CacheDirectoryMetadata;
     38 
     39 class NullAction final : public Action {
     40 public:
     41  NullAction() = default;
     42 
     43  virtual void RunOnTarget(mozilla::SafeRefPtr<Resolver> aResolver,
     44                           const mozilla::Maybe<CacheDirectoryMetadata>&, Data*,
     45                           const mozilla::Maybe<mozilla::dom::cache::CipherKey>&
     46                           /* aMaybeCipherKey */) override {
     47    // Resolve success immediately.  This Action does no actual work.
     48    MOZ_DIAGNOSTIC_ASSERT(aResolver);
     49    aResolver->Resolve(NS_OK);
     50  }
     51 };
     52 
     53 }  // namespace
     54 
     55 namespace mozilla::dom::cache {
     56 
     57 using mozilla::dom::quota::AssertIsOnIOThread;
     58 using mozilla::dom::quota::ClientDirectoryLock;
     59 using mozilla::dom::quota::ClientDirectoryLockHandle;
     60 using mozilla::dom::quota::PERSISTENCE_TYPE_DEFAULT;
     61 using mozilla::dom::quota::PersistenceType;
     62 using mozilla::dom::quota::QuotaManager;
     63 using mozilla::dom::quota::SleepIfEnabled;
     64 
     65 class Context::Data final : public Action::Data {
     66 public:
     67  explicit Data(nsISerialEventTarget* aTarget) : mTarget(aTarget) {
     68    MOZ_DIAGNOSTIC_ASSERT(mTarget);
     69  }
     70 
     71  virtual mozIStorageConnection* GetConnection() const override {
     72    MOZ_ASSERT(mTarget->IsOnCurrentThread());
     73    return mConnection;
     74  }
     75 
     76  virtual void SetConnection(mozIStorageConnection* aConn) override {
     77    MOZ_ASSERT(mTarget->IsOnCurrentThread());
     78    MOZ_DIAGNOSTIC_ASSERT(!mConnection);
     79    mConnection = aConn;
     80    MOZ_DIAGNOSTIC_ASSERT(mConnection);
     81  }
     82 
     83 private:
     84  ~Data() {
     85    // We could proxy release our data here, but instead just assert.  The
     86    // Context code should guarantee that we are destroyed on the target
     87    // thread once the connection is initialized.  If we're not, then
     88    // QuotaManager might race and try to clear the origin out from under us.
     89    MOZ_ASSERT_IF(mConnection, mTarget->IsOnCurrentThread());
     90  }
     91 
     92  nsCOMPtr<nsISerialEventTarget> mTarget;
     93  nsCOMPtr<mozIStorageConnection> mConnection;
     94 
     95  // Threadsafe counting because we're created on the PBackground thread
     96  // and destroyed on the target IO thread.
     97  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Context::Data)
     98 };
     99 
    100 // Executed to perform the complicated dance of steps necessary to initialize
    101 // the QuotaManager.  This must be performed for each origin before any disk
    102 // IO occurrs.
    103 class Context::QuotaInitRunnable final : public nsIRunnable {
    104 public:
    105  QuotaInitRunnable(SafeRefPtr<Context> aContext, SafeRefPtr<Manager> aManager,
    106                    Data* aData, nsISerialEventTarget* aTarget,
    107                    SafeRefPtr<Action> aInitAction)
    108      : mContext(std::move(aContext)),
    109        mThreadsafeHandle(mContext->CreateThreadsafeHandle()),
    110        mManager(std::move(aManager)),
    111        mData(aData),
    112        mTarget(aTarget),
    113        mInitAction(std::move(aInitAction)),
    114        mInitiatingEventTarget(GetCurrentSerialEventTarget()),
    115        mResult(NS_OK),
    116        mState(STATE_INIT),
    117        mCanceled(false) {
    118    MOZ_DIAGNOSTIC_ASSERT(mContext);
    119    MOZ_DIAGNOSTIC_ASSERT(mManager);
    120    MOZ_DIAGNOSTIC_ASSERT(mData);
    121    MOZ_DIAGNOSTIC_ASSERT(mTarget);
    122    MOZ_DIAGNOSTIC_ASSERT(mInitiatingEventTarget);
    123    MOZ_DIAGNOSTIC_ASSERT(mInitAction);
    124  }
    125 
    126  Maybe<ClientDirectoryLock&> MaybeDirectoryLockRef() const {
    127    NS_ASSERT_OWNINGTHREAD(QuotaInitRunnable);
    128 
    129    return ToMaybeRef(mDirectoryLockHandle.get());
    130  }
    131 
    132  nsresult Dispatch() {
    133    NS_ASSERT_OWNINGTHREAD(QuotaInitRunnable);
    134    MOZ_DIAGNOSTIC_ASSERT(mState == STATE_INIT);
    135 
    136    mState = STATE_GET_INFO;
    137    nsresult rv = NS_DispatchToMainThread(this, nsIThread::DISPATCH_NORMAL);
    138    if (NS_WARN_IF(NS_FAILED(rv))) {
    139      mState = STATE_COMPLETE;
    140      Clear();
    141    }
    142    return rv;
    143  }
    144 
    145  void Cancel() {
    146    NS_ASSERT_OWNINGTHREAD(QuotaInitRunnable);
    147    MOZ_DIAGNOSTIC_ASSERT(!mCanceled);
    148    mCanceled = true;
    149    mInitAction->CancelOnInitiatingThread();
    150  }
    151 
    152  void DirectoryLockAcquired(ClientDirectoryLockHandle aLockHandle);
    153 
    154  void DirectoryLockFailed();
    155 
    156 private:
    157  class SyncResolver final : public Action::Resolver {
    158   public:
    159    SyncResolver() : mResolved(false), mResult(NS_OK) {}
    160 
    161    virtual void Resolve(nsresult aRv) override {
    162      MOZ_DIAGNOSTIC_ASSERT(!mResolved);
    163      mResolved = true;
    164      mResult = aRv;
    165    };
    166 
    167    bool Resolved() const { return mResolved; }
    168    nsresult Result() const { return mResult; }
    169 
    170   private:
    171    ~SyncResolver() = default;
    172 
    173    bool mResolved;
    174    nsresult mResult;
    175 
    176    NS_INLINE_DECL_REFCOUNTING(Context::QuotaInitRunnable::SyncResolver,
    177                               override)
    178  };
    179 
    180  ~QuotaInitRunnable() {
    181    MOZ_DIAGNOSTIC_ASSERT(mState == STATE_COMPLETE);
    182    MOZ_DIAGNOSTIC_ASSERT(!mContext);
    183    MOZ_DIAGNOSTIC_ASSERT(!mInitAction);
    184  }
    185 
    186  enum State {
    187    STATE_INIT,
    188    STATE_GET_INFO,
    189    STATE_CREATE_QUOTA_MANAGER,
    190    STATE_WAIT_FOR_DIRECTORY_LOCK,
    191    STATE_ENSURE_ORIGIN_INITIALIZED,
    192    STATE_RUN_ON_TARGET,
    193    STATE_RUNNING,
    194    STATE_COMPLETING,
    195    STATE_COMPLETE
    196  };
    197 
    198  void Complete(nsresult aResult) {
    199    MOZ_DIAGNOSTIC_ASSERT(mState == STATE_RUNNING || NS_FAILED(aResult));
    200 
    201    MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(mResult));
    202    mResult = aResult;
    203 
    204    mState = STATE_COMPLETING;
    205    MOZ_ALWAYS_SUCCEEDS(
    206        mInitiatingEventTarget->Dispatch(this, nsIThread::DISPATCH_NORMAL));
    207  }
    208 
    209  void Clear() {
    210    NS_ASSERT_OWNINGTHREAD(QuotaInitRunnable);
    211    MOZ_DIAGNOSTIC_ASSERT(mContext);
    212    mContext = nullptr;
    213    mManager = nullptr;
    214    mInitAction = nullptr;
    215  }
    216 
    217  SafeRefPtr<Context> mContext;
    218  SafeRefPtr<ThreadsafeHandle> mThreadsafeHandle;
    219  SafeRefPtr<Manager> mManager;
    220  RefPtr<Data> mData;
    221  nsCOMPtr<nsISerialEventTarget> mTarget;
    222  SafeRefPtr<Action> mInitAction;
    223  nsCOMPtr<nsIEventTarget> mInitiatingEventTarget;
    224  nsresult mResult;
    225  Maybe<mozilla::ipc::PrincipalInfo> mPrincipalInfo;
    226  Maybe<CacheDirectoryMetadata> mDirectoryMetadata;
    227  ClientDirectoryLockHandle mDirectoryLockHandle;
    228  RefPtr<CipherKeyManager> mCipherKeyManager;
    229  State mState;
    230  Atomic<bool> mCanceled;
    231 
    232 public:
    233  NS_DECL_THREADSAFE_ISUPPORTS
    234  NS_DECL_NSIRUNNABLE
    235 };
    236 
    237 void Context::QuotaInitRunnable::DirectoryLockAcquired(
    238    ClientDirectoryLockHandle aLockHandle) {
    239  NS_ASSERT_OWNINGTHREAD(QuotaInitRunnable);
    240  MOZ_DIAGNOSTIC_ASSERT(aLockHandle);
    241  MOZ_DIAGNOSTIC_ASSERT(mState == STATE_WAIT_FOR_DIRECTORY_LOCK);
    242  MOZ_DIAGNOSTIC_ASSERT(!mDirectoryLockHandle);
    243 
    244  mDirectoryLockHandle = std::move(aLockHandle);
    245 
    246  MOZ_DIAGNOSTIC_ASSERT(mDirectoryLockHandle->Id() >= 0);
    247  mDirectoryMetadata->mDirectoryLockId = mDirectoryLockHandle->Id();
    248 
    249  if (mCanceled || mDirectoryLockHandle->Invalidated()) {
    250    Complete(NS_ERROR_ABORT);
    251    return;
    252  }
    253 
    254  QuotaManager* qm = QuotaManager::Get();
    255  MOZ_DIAGNOSTIC_ASSERT(qm);
    256 
    257  mState = STATE_ENSURE_ORIGIN_INITIALIZED;
    258  nsresult rv = qm->IOThread()->Dispatch(this, nsIThread::DISPATCH_NORMAL);
    259  if (NS_WARN_IF(NS_FAILED(rv))) {
    260    Complete(rv);
    261    return;
    262  }
    263 
    264  NotifyDatabaseWorkStarted();
    265 }
    266 
    267 void Context::QuotaInitRunnable::DirectoryLockFailed() {
    268  NS_ASSERT_OWNINGTHREAD(QuotaInitRunnable);
    269  MOZ_DIAGNOSTIC_ASSERT(mState == STATE_WAIT_FOR_DIRECTORY_LOCK);
    270  MOZ_DIAGNOSTIC_ASSERT(!mDirectoryLockHandle);
    271 
    272  NS_WARNING("Failed to acquire a directory lock!");
    273 
    274  Complete(NS_ERROR_FAILURE);
    275 }
    276 
    277 NS_IMPL_ISUPPORTS(mozilla::dom::cache::Context::QuotaInitRunnable, nsIRunnable);
    278 
    279 // The QuotaManager init state machine is represented in the following diagram:
    280 //
    281 //    +---------------+
    282 //    |     Start     |      Resolve(error)
    283 //    | (Orig Thread) +---------------------+
    284 //    +-------+-------+                     |
    285 //            |                             |
    286 // +----------v-----------+                 |
    287 // |       GetInfo        |  Resolve(error) |
    288 // |    (Main Thread)     +-----------------+
    289 // +----------+-----------+                 |
    290 //            |                             |
    291 // +----------v-----------+                 |
    292 // |  CreateQuotaManager  |  Resolve(error) |
    293 // |    (Orig Thread)     +-----------------+
    294 // +----------+-----------+                 |
    295 //            |                             |
    296 // +----------v-----------+                 |
    297 // | WaitForDirectoryLock |  Resolve(error) |
    298 // |    (Orig Thread)     +-----------------+
    299 // +----------+-----------+                 |
    300 //            |                             |
    301 // +----------v------------+                |
    302 // |EnsureOriginInitialized| Resolve(error) |
    303 // |   (Quota IO Thread)   +----------------+
    304 // +----------+------------+                |
    305 //            |                             |
    306 // +----------v------------+                |
    307 // |     RunOnTarget       | Resolve(error) |
    308 // |   (Target Thread)     +----------------+
    309 // +----------+------------+                |
    310 //            |                             |
    311 //  +---------v---------+            +------v------+
    312 //  |      Running      |            |  Completing |
    313 //  | (Target Thread)   +------------>(Orig Thread)|
    314 //  +-------------------+            +------+------+
    315 //                                          |
    316 //                                    +-----v----+
    317 //                                    | Complete |
    318 //                                    +----------+
    319 //
    320 // The initialization process proceeds through the main states.  If an error
    321 // occurs, then we transition to Completing state back on the original thread.
    322 NS_IMETHODIMP
    323 Context::QuotaInitRunnable::Run() {
    324  // May run on different threads depending on the state.  See individual
    325  // state cases for thread assertions.
    326 
    327  SafeRefPtr<SyncResolver> resolver = MakeSafeRefPtr<SyncResolver>();
    328 
    329  switch (mState) {
    330    // -----------------------------------
    331    case STATE_GET_INFO: {
    332      MOZ_ASSERT(NS_IsMainThread());
    333 
    334      auto res = [this]() -> Result<Ok, nsresult> {
    335        if (mCanceled) {
    336          return Err(NS_ERROR_ABORT);
    337        }
    338 
    339        nsCOMPtr<nsIPrincipal> principal = mManager->GetManagerId().Principal();
    340 
    341        mozilla::ipc::PrincipalInfo principalInfo;
    342        QM_TRY(
    343            MOZ_TO_RESULT(PrincipalToPrincipalInfo(principal, &principalInfo)));
    344 
    345        mPrincipalInfo.emplace(std::move(principalInfo));
    346 
    347        mState = STATE_CREATE_QUOTA_MANAGER;
    348 
    349        MOZ_ALWAYS_SUCCEEDS(
    350            mInitiatingEventTarget->Dispatch(this, nsIThread::DISPATCH_NORMAL));
    351 
    352        return Ok{};
    353      }();
    354 
    355      if (res.isErr()) {
    356        resolver->Resolve(res.inspectErr());
    357      }
    358 
    359      break;
    360    }
    361    // ----------------------------------
    362    case STATE_CREATE_QUOTA_MANAGER: {
    363      NS_ASSERT_OWNINGTHREAD(QuotaInitRunnable);
    364 
    365      if (mCanceled || QuotaManager::IsShuttingDown()) {
    366        resolver->Resolve(NS_ERROR_ABORT);
    367        break;
    368      }
    369 
    370      QM_TRY(QuotaManager::EnsureCreated(), QM_PROPAGATE,
    371             [&resolver](const auto rv) { resolver->Resolve(rv); });
    372 
    373      auto* const quotaManager = QuotaManager::Get();
    374      MOZ_DIAGNOSTIC_ASSERT(quotaManager);
    375 
    376      QM_TRY_UNWRAP(auto principalMetadata,
    377                    quota::GetInfoFromValidatedPrincipalInfo(*quotaManager,
    378                                                             *mPrincipalInfo));
    379 
    380      mDirectoryMetadata.emplace(std::move(principalMetadata));
    381 
    382      mState = STATE_WAIT_FOR_DIRECTORY_LOCK;
    383 
    384      quotaManager
    385          ->OpenClientDirectory({*mDirectoryMetadata, quota::Client::DOMCACHE})
    386          ->Then(
    387              GetCurrentSerialEventTarget(), __func__,
    388              [self = RefPtr(this)](
    389                  QuotaManager::ClientDirectoryLockHandlePromise::
    390                      ResolveOrRejectValue&& aValue) {
    391                if (aValue.IsResolve()) {
    392                  self->DirectoryLockAcquired(std::move(aValue.ResolveValue()));
    393                } else {
    394                  self->DirectoryLockFailed();
    395                }
    396              });
    397 
    398      break;
    399    }
    400    // ----------------------------------
    401    case STATE_ENSURE_ORIGIN_INITIALIZED: {
    402      AssertIsOnIOThread();
    403 
    404      auto res = [this]() -> Result<Ok, nsresult> {
    405        if (mCanceled) {
    406          return Err(NS_ERROR_ABORT);
    407        }
    408 
    409        QuotaManager* quotaManager = QuotaManager::Get();
    410        MOZ_DIAGNOSTIC_ASSERT(quotaManager);
    411 
    412        QM_TRY_UNWRAP(mDirectoryMetadata->mDir,
    413                      quotaManager->GetOrCreateTemporaryOriginDirectory(
    414                          *mDirectoryMetadata));
    415 
    416        auto* cacheQuotaClient = CacheQuotaClient::Get();
    417        MOZ_DIAGNOSTIC_ASSERT(cacheQuotaClient);
    418 
    419        mCipherKeyManager =
    420            cacheQuotaClient->GetOrCreateCipherKeyManager(*mDirectoryMetadata);
    421 
    422        SleepIfEnabled(
    423            StaticPrefs::dom_cache_databaseInitialization_pauseOnIOThreadMs());
    424 
    425        mState = STATE_RUN_ON_TARGET;
    426 
    427        MOZ_ALWAYS_SUCCEEDS(
    428            mTarget->Dispatch(this, nsIThread::DISPATCH_NORMAL));
    429 
    430        return Ok{};
    431      }();
    432 
    433      if (res.isErr()) {
    434        resolver->Resolve(res.inspectErr());
    435      }
    436 
    437      break;
    438    }
    439    // -------------------
    440    case STATE_RUN_ON_TARGET: {
    441      MOZ_ASSERT(mTarget->IsOnCurrentThread());
    442 
    443      mState = STATE_RUNNING;
    444 
    445      // Execute the provided initialization Action.  The Action must Resolve()
    446      // before returning.
    447 
    448      mInitAction->RunOnTarget(
    449          resolver.clonePtr(), mDirectoryMetadata, mData,
    450          mCipherKeyManager ? Some(mCipherKeyManager->Ensure()) : Nothing{});
    451 
    452      MOZ_DIAGNOSTIC_ASSERT(resolver->Resolved());
    453 
    454      mData = nullptr;
    455 
    456      // If the database was opened, then we should always succeed when creating
    457      // the marker file.  If it wasn't opened successfully, then no need to
    458      // create a marker file anyway.
    459      if (NS_SUCCEEDED(resolver->Result())) {
    460        MOZ_ALWAYS_SUCCEEDS(CreateMarkerFile(*mDirectoryMetadata));
    461      }
    462 
    463      break;
    464    }
    465    // -------------------
    466    case STATE_COMPLETING: {
    467      NS_ASSERT_OWNINGTHREAD(QuotaInitRunnable);
    468      mInitAction->CompleteOnInitiatingThread(mResult);
    469 
    470      mContext->OnQuotaInit(mResult, mDirectoryMetadata,
    471                            std::move(mDirectoryLockHandle),
    472                            std::move(mCipherKeyManager));
    473 
    474      mState = STATE_COMPLETE;
    475 
    476      // Explicitly cleanup here as the destructor could fire on any of
    477      // the threads we have bounced through.
    478      Clear();
    479      break;
    480    }
    481    // -----
    482    case STATE_WAIT_FOR_DIRECTORY_LOCK:
    483    default: {
    484      MOZ_CRASH("unexpected state in QuotaInitRunnable");
    485    }
    486  }
    487 
    488  if (resolver->Resolved()) {
    489    Complete(resolver->Result());
    490  }
    491 
    492  return NS_OK;
    493 }
    494 
    495 // Runnable wrapper around Action objects dispatched on the Context.  This
    496 // runnable executes the Action on the appropriate threads while the Context
    497 // is initialized.
    498 class Context::ActionRunnable final : public nsIRunnable,
    499                                      public Action::Resolver,
    500                                      public Context::Activity {
    501 public:
    502  ActionRunnable(SafeRefPtr<Context> aContext, Data* aData,
    503                 nsISerialEventTarget* aTarget, SafeRefPtr<Action> aAction,
    504                 const Maybe<CacheDirectoryMetadata>& aDirectoryMetadata,
    505                 RefPtr<CipherKeyManager> aCipherKeyManager)
    506      : mContext(std::move(aContext)),
    507        mData(aData),
    508        mTarget(aTarget),
    509        mAction(std::move(aAction)),
    510        mDirectoryMetadata(aDirectoryMetadata),
    511        mCipherKeyManager(std::move(aCipherKeyManager)),
    512        mInitiatingThread(GetCurrentSerialEventTarget()),
    513        mState(STATE_INIT),
    514        mResult(NS_OK),
    515        mExecutingRunOnTarget(false) {
    516    MOZ_DIAGNOSTIC_ASSERT(mContext);
    517    // mData may be nullptr
    518    MOZ_DIAGNOSTIC_ASSERT(mTarget);
    519    MOZ_DIAGNOSTIC_ASSERT(mAction);
    520    // mDirectoryMetadata.mDir may be nullptr if QuotaInitRunnable failed
    521    MOZ_DIAGNOSTIC_ASSERT(mInitiatingThread);
    522  }
    523 
    524  nsresult Dispatch() {
    525    NS_ASSERT_OWNINGTHREAD(ActionRunnable);
    526    MOZ_DIAGNOSTIC_ASSERT(mState == STATE_INIT);
    527 
    528    mState = STATE_RUN_ON_TARGET;
    529    nsresult rv = mTarget->Dispatch(this, nsIEventTarget::DISPATCH_NORMAL);
    530    if (NS_WARN_IF(NS_FAILED(rv))) {
    531      mState = STATE_COMPLETE;
    532      Clear();
    533    }
    534    return rv;
    535  }
    536 
    537  virtual bool MatchesCacheId(CacheId aCacheId) const override {
    538    NS_ASSERT_OWNINGTHREAD(ActionRunnable);
    539    return mAction->MatchesCacheId(aCacheId);
    540  }
    541 
    542  virtual void Cancel() override {
    543    NS_ASSERT_OWNINGTHREAD(ActionRunnable);
    544    mAction->CancelOnInitiatingThread();
    545  }
    546 
    547  virtual void Resolve(nsresult aRv) override {
    548    MOZ_ASSERT(mTarget->IsOnCurrentThread());
    549    MOZ_DIAGNOSTIC_ASSERT(mState == STATE_RUNNING);
    550 
    551    mResult = aRv;
    552 
    553    // We ultimately must complete on the initiating thread, but bounce through
    554    // the current thread again to ensure that we don't destroy objects and
    555    // state out from under the currently running action's stack.
    556    mState = STATE_RESOLVING;
    557 
    558    // If we were resolved synchronously within Action::RunOnTarget() then we
    559    // can avoid a thread bounce and just resolve once RunOnTarget() returns.
    560    // The Run() method will handle this by looking at mState after
    561    // RunOnTarget() returns.
    562    if (mExecutingRunOnTarget) {
    563      return;
    564    }
    565 
    566    // Otherwise we are in an asynchronous resolve.  And must perform a thread
    567    // bounce to run on the target thread again.
    568    MOZ_ALWAYS_SUCCEEDS(mTarget->Dispatch(this, nsIThread::DISPATCH_NORMAL));
    569  }
    570 
    571 private:
    572  ~ActionRunnable() {
    573    MOZ_DIAGNOSTIC_ASSERT(mState == STATE_COMPLETE);
    574    MOZ_DIAGNOSTIC_ASSERT(!mContext);
    575    MOZ_DIAGNOSTIC_ASSERT(!mAction);
    576  }
    577 
    578  void DoStringify(nsACString& aData) override {
    579    aData.Append("ActionRunnable ("_ns +
    580                 //
    581                 "State:"_ns + IntToCString(mState) + kStringifyDelimiter +
    582                 //
    583                 "Action:"_ns + IntToCString(static_cast<bool>(mAction)) +
    584                 kStringifyDelimiter +
    585                 // TODO: We might want to have Action::Stringify, too.
    586                 //
    587                 "Context:"_ns + IntToCString(static_cast<bool>(mContext)) +
    588                 kStringifyDelimiter +
    589                 // We do not print out mContext as we most probably were called
    590                 // by its Stringify.
    591                 ")"_ns);
    592  }
    593 
    594  void Clear() {
    595    NS_ASSERT_OWNINGTHREAD(ActionRunnable);
    596    MOZ_DIAGNOSTIC_ASSERT(mContext);
    597    MOZ_DIAGNOSTIC_ASSERT(mAction);
    598    mContext->RemoveActivity(*this);
    599    mContext = nullptr;
    600    mAction = nullptr;
    601  }
    602 
    603  enum State {
    604    STATE_INIT,
    605    STATE_RUN_ON_TARGET,
    606    STATE_RUNNING,
    607    STATE_RESOLVING,
    608    STATE_COMPLETING,
    609    STATE_COMPLETE
    610  };
    611 
    612  SafeRefPtr<Context> mContext;
    613  RefPtr<Data> mData;
    614  nsCOMPtr<nsISerialEventTarget> mTarget;
    615  SafeRefPtr<Action> mAction;
    616  const Maybe<CacheDirectoryMetadata> mDirectoryMetadata;
    617  RefPtr<CipherKeyManager> mCipherKeyManager;
    618  nsCOMPtr<nsIEventTarget> mInitiatingThread;
    619  State mState;
    620  nsresult mResult;
    621 
    622  // Only accessible on target thread;
    623  bool mExecutingRunOnTarget;
    624 
    625 public:
    626  NS_DECL_THREADSAFE_ISUPPORTS
    627  NS_DECL_NSIRUNNABLE
    628 };
    629 
    630 NS_IMPL_ISUPPORTS(mozilla::dom::cache::Context::ActionRunnable, nsIRunnable);
    631 
    632 // The ActionRunnable has a simpler state machine.  It basically needs to run
    633 // the action on the target thread and then complete on the original thread.
    634 //
    635 //   +-------------+
    636 //   |    Start    |
    637 //   |(Orig Thread)|
    638 //   +-----+-------+
    639 //         |
    640 // +-------v---------+
    641 // |  RunOnTarget    |
    642 // |Target IO Thread)+---+ Resolve()
    643 // +-------+---------+   |
    644 //         |             |
    645 // +-------v----------+  |
    646 // |     Running      |  |
    647 // |(Target IO Thread)|  |
    648 // +------------------+  |
    649 //         | Resolve()   |
    650 // +-------v----------+  |
    651 // |     Resolving    <--+                   +-------------+
    652 // |                  |                      |  Completing |
    653 // |(Target IO Thread)+---------------------->(Orig Thread)|
    654 // +------------------+                      +-------+-----+
    655 //                                                   |
    656 //                                                   |
    657 //                                              +----v---+
    658 //                                              |Complete|
    659 //                                              +--------+
    660 //
    661 // Its important to note that synchronous actions will effectively Resolve()
    662 // out of the Running state immediately.  Asynchronous Actions may remain
    663 // in the Running state for some time, but normally the ActionRunnable itself
    664 // does not see any execution there.  Its all handled internal to the Action.
    665 NS_IMETHODIMP
    666 Context::ActionRunnable::Run() {
    667  switch (mState) {
    668    // ----------------------
    669    case STATE_RUN_ON_TARGET: {
    670      MOZ_ASSERT(mTarget->IsOnCurrentThread());
    671      MOZ_DIAGNOSTIC_ASSERT(!mExecutingRunOnTarget);
    672 
    673      // Note that we are calling RunOnTarget().  This lets us detect
    674      // if Resolve() is called synchronously.
    675      AutoRestore<bool> executingRunOnTarget(mExecutingRunOnTarget);
    676      mExecutingRunOnTarget = true;
    677 
    678      mState = STATE_RUNNING;
    679      mAction->RunOnTarget(
    680          SafeRefPtrFromThis(), mDirectoryMetadata, mData,
    681          mCipherKeyManager ? Some(mCipherKeyManager->Ensure()) : Nothing{});
    682 
    683      mData = nullptr;
    684 
    685      // Resolve was called synchronously from RunOnTarget().  We can
    686      // immediately move to completing now since we are sure RunOnTarget()
    687      // completed.
    688      if (mState == STATE_RESOLVING) {
    689        // Use recursion instead of switch case fall-through...  Seems slightly
    690        // easier to understand.
    691        Run();
    692      }
    693 
    694      break;
    695    }
    696    // -----------------
    697    case STATE_RESOLVING: {
    698      MOZ_ASSERT(mTarget->IsOnCurrentThread());
    699      // The call to Action::RunOnTarget() must have returned now if we
    700      // are running on the target thread again.  We may now proceed
    701      // with completion.
    702      mState = STATE_COMPLETING;
    703      // Shutdown must be delayed until all Contexts are destroyed.  Crash
    704      // for this invariant violation.
    705      MOZ_ALWAYS_SUCCEEDS(
    706          mInitiatingThread->Dispatch(this, nsIThread::DISPATCH_NORMAL));
    707      break;
    708    }
    709    // -------------------
    710    case STATE_COMPLETING: {
    711      NS_ASSERT_OWNINGTHREAD(ActionRunnable);
    712      mAction->CompleteOnInitiatingThread(mResult);
    713      mState = STATE_COMPLETE;
    714      // Explicitly cleanup here as the destructor could fire on any of
    715      // the threads we have bounced through.
    716      Clear();
    717      break;
    718    }
    719    // -----------------
    720    default: {
    721      MOZ_CRASH("unexpected state in ActionRunnable");
    722      break;
    723    }
    724  }
    725  return NS_OK;
    726 }
    727 
    728 void Context::ThreadsafeHandle::AllowToClose() {
    729  if (mOwningEventTarget->IsOnCurrentThread()) {
    730    AllowToCloseOnOwningThread();
    731    return;
    732  }
    733 
    734  // Dispatch is guaranteed to succeed here because we block shutdown until
    735  // all Contexts have been destroyed.
    736  nsCOMPtr<nsIRunnable> runnable = NewRunnableMethod(
    737      "dom::cache::Context::ThreadsafeHandle::AllowToCloseOnOwningThread", this,
    738      &ThreadsafeHandle::AllowToCloseOnOwningThread);
    739  MOZ_ALWAYS_SUCCEEDS(mOwningEventTarget->Dispatch(runnable.forget(),
    740                                                   nsIThread::DISPATCH_NORMAL));
    741 }
    742 
    743 void Context::ThreadsafeHandle::InvalidateAndAllowToClose() {
    744  if (mOwningEventTarget->IsOnCurrentThread()) {
    745    InvalidateAndAllowToCloseOnOwningThread();
    746    return;
    747  }
    748 
    749  // Dispatch is guaranteed to succeed here because we block shutdown until
    750  // all Contexts have been destroyed.
    751  nsCOMPtr<nsIRunnable> runnable = NewRunnableMethod(
    752      "dom::cache::Context::ThreadsafeHandle::"
    753      "InvalidateAndAllowToCloseOnOwningThread",
    754      this, &ThreadsafeHandle::InvalidateAndAllowToCloseOnOwningThread);
    755  MOZ_ALWAYS_SUCCEEDS(mOwningEventTarget->Dispatch(runnable.forget(),
    756                                                   nsIThread::DISPATCH_NORMAL));
    757 }
    758 
    759 Context::ThreadsafeHandle::ThreadsafeHandle(SafeRefPtr<Context> aContext)
    760    : mStrongRef(std::move(aContext)),
    761      mWeakRef(mStrongRef.unsafeGetRawPtr()),
    762      mOwningEventTarget(GetCurrentSerialEventTarget()) {}
    763 
    764 Context::ThreadsafeHandle::~ThreadsafeHandle() {
    765  // Normally we only touch mStrongRef on the owning thread.  This is safe,
    766  // however, because when we do use mStrongRef on the owning thread we are
    767  // always holding a strong ref to the ThreadsafeHandle via the owning
    768  // runnable.  So we cannot run the ThreadsafeHandle destructor simultaneously.
    769  if (!mStrongRef || mOwningEventTarget->IsOnCurrentThread()) {
    770    return;
    771  }
    772 
    773  // Dispatch in NS_ProxyRelease is guaranteed to succeed here because we block
    774  // shutdown until all Contexts have been destroyed. Therefore it is ok to have
    775  // MOZ_ALWAYS_SUCCEED here.
    776  MOZ_ALWAYS_SUCCEEDS(NS_ProxyRelease("Context::ThreadsafeHandle::mStrongRef",
    777                                      mOwningEventTarget, mStrongRef.forget()));
    778 }
    779 
    780 void Context::ThreadsafeHandle::AllowToCloseOnOwningThread() {
    781  MOZ_ASSERT(mOwningEventTarget->IsOnCurrentThread());
    782 
    783  // A Context "closes" when its ref count drops to zero.  Dropping this
    784  // strong ref is necessary, but not sufficient for the close to occur.
    785  // Any outstanding IO will continue and keep the Context alive.  Once
    786  // the Context is idle, it will be destroyed.
    787 
    788  // First, tell the context to flush any target thread shared data.  This
    789  // data must be released on the target thread prior to running the Context
    790  // destructor.  This will schedule an Action which ensures that the
    791  // ~Context() is not immediately executed when we drop the strong ref.
    792  if (mStrongRef) {
    793    mStrongRef->DoomTargetData();
    794  }
    795 
    796  // Now drop our strong ref and let Context finish running any outstanding
    797  // Actions.
    798  mStrongRef = nullptr;
    799 }
    800 
    801 void Context::ThreadsafeHandle::InvalidateAndAllowToCloseOnOwningThread() {
    802  MOZ_ASSERT(mOwningEventTarget->IsOnCurrentThread());
    803  // Cancel the Context through the weak reference.  This means we can
    804  // allow the Context to close by dropping the strong ref, but then
    805  // still cancel ongoing IO if necessary.
    806  if (mWeakRef) {
    807    mWeakRef->Invalidate();
    808  }
    809  // We should synchronously have AllowToCloseOnOwningThread called when
    810  // the Context is canceled.
    811  MOZ_DIAGNOSTIC_ASSERT(!mStrongRef);
    812 }
    813 
    814 void Context::ThreadsafeHandle::ContextDestroyed(Context& aContext) {
    815  MOZ_ASSERT(mOwningEventTarget->IsOnCurrentThread());
    816  MOZ_DIAGNOSTIC_ASSERT(!mStrongRef);
    817  MOZ_DIAGNOSTIC_ASSERT(mWeakRef);
    818  MOZ_DIAGNOSTIC_ASSERT(mWeakRef == &aContext);
    819  mWeakRef = nullptr;
    820 }
    821 
    822 // static
    823 SafeRefPtr<Context> Context::Create(SafeRefPtr<Manager> aManager,
    824                                    nsISerialEventTarget* aTarget,
    825                                    SafeRefPtr<Action> aInitAction,
    826                                    Maybe<Context&> aOldContext) {
    827  auto context = MakeSafeRefPtr<Context>(std::move(aManager), aTarget,
    828                                         std::move(aInitAction));
    829  context->Init(aOldContext);
    830  return context;
    831 }
    832 
    833 Context::Context(SafeRefPtr<Manager> aManager, nsISerialEventTarget* aTarget,
    834                 SafeRefPtr<Action> aInitAction)
    835    : mManager(std::move(aManager)),
    836      mTarget(aTarget),
    837      mData(new Data(aTarget)),
    838      mState(STATE_CONTEXT_PREINIT),
    839      mOrphanedData(false),
    840      mInitAction(std::move(aInitAction)) {
    841  MOZ_DIAGNOSTIC_ASSERT(mManager);
    842  MOZ_DIAGNOSTIC_ASSERT(mTarget);
    843 }
    844 
    845 void Context::Dispatch(SafeRefPtr<Action> aAction) {
    846  NS_ASSERT_OWNINGTHREAD(Context);
    847  MOZ_DIAGNOSTIC_ASSERT(aAction);
    848  MOZ_DIAGNOSTIC_ASSERT(mState != STATE_CONTEXT_CANCELED);
    849 
    850  if (mState == STATE_CONTEXT_CANCELED) {
    851    return;
    852  }
    853 
    854  if (mState == STATE_CONTEXT_INIT || mState == STATE_CONTEXT_PREINIT) {
    855    PendingAction* pending = mPendingActions.AppendElement();
    856    pending->mAction = std::move(aAction);
    857    return;
    858  }
    859 
    860  MOZ_DIAGNOSTIC_ASSERT(mState == STATE_CONTEXT_READY);
    861  DispatchAction(std::move(aAction));
    862 }
    863 
    864 Maybe<ClientDirectoryLock&> Context::MaybeDirectoryLockRef() const {
    865  NS_ASSERT_OWNINGTHREAD(Context);
    866 
    867  if (mState == STATE_CONTEXT_PREINIT) {
    868    MOZ_DIAGNOSTIC_ASSERT(!mInitRunnable);
    869    MOZ_DIAGNOSTIC_ASSERT(!mDirectoryLockHandle);
    870 
    871    return Nothing();
    872  }
    873 
    874  if (mState == STATE_CONTEXT_INIT) {
    875    MOZ_DIAGNOSTIC_ASSERT(!mDirectoryLockHandle);
    876 
    877    return mInitRunnable->MaybeDirectoryLockRef();
    878  }
    879 
    880  return ToMaybeRef(mDirectoryLockHandle.get());
    881 }
    882 
    883 CipherKeyManager& Context::MutableCipherKeyManagerRef() {
    884  MOZ_ASSERT(mTarget->IsOnCurrentThread());
    885  MOZ_DIAGNOSTIC_ASSERT(mCipherKeyManager);
    886 
    887  return *mCipherKeyManager;
    888 }
    889 
    890 const Maybe<CacheDirectoryMetadata>& Context::MaybeCacheDirectoryMetadataRef()
    891    const {
    892  MOZ_ASSERT(mTarget->IsOnCurrentThread());
    893  return mDirectoryMetadata;
    894 }
    895 
    896 void Context::CancelAll() {
    897  NS_ASSERT_OWNINGTHREAD(Context);
    898 
    899  // In PREINIT state we have not dispatch the init action yet.  Just
    900  // forget it.
    901  if (mState == STATE_CONTEXT_PREINIT) {
    902    MOZ_DIAGNOSTIC_ASSERT(!mInitRunnable);
    903    mInitAction = nullptr;
    904 
    905    // In INIT state we have dispatched the runnable, but not received the
    906    // async completion yet.  Cancel the runnable, but don't forget about it
    907    // until we get OnQuotaInit() callback.
    908  } else if (mState == STATE_CONTEXT_INIT) {
    909    mInitRunnable->Cancel();
    910  }
    911 
    912  mState = STATE_CONTEXT_CANCELED;
    913  // Allow completion of pending actions in Context::OnQuotaInit
    914  if (!mInitRunnable) {
    915    mPendingActions.Clear();
    916  }
    917  for (const auto& activity : mActivityList.ForwardRange()) {
    918    activity->Cancel();
    919  }
    920  AllowToClose();
    921 }
    922 
    923 bool Context::IsCanceled() const {
    924  NS_ASSERT_OWNINGTHREAD(Context);
    925  return mState == STATE_CONTEXT_CANCELED;
    926 }
    927 
    928 void Context::Invalidate() {
    929  NS_ASSERT_OWNINGTHREAD(Context);
    930  mManager->NoteClosing();
    931  CancelAll();
    932 }
    933 
    934 void Context::AllowToClose() {
    935  NS_ASSERT_OWNINGTHREAD(Context);
    936  if (mThreadsafeHandle) {
    937    mThreadsafeHandle->AllowToClose();
    938  }
    939 }
    940 
    941 void Context::CancelForCacheId(CacheId aCacheId) {
    942  NS_ASSERT_OWNINGTHREAD(Context);
    943 
    944  // Remove matching pending actions
    945  mPendingActions.RemoveElementsBy([aCacheId](const auto& pendingAction) {
    946    return pendingAction.mAction->MatchesCacheId(aCacheId);
    947  });
    948 
    949  // Cancel activities and let them remove themselves
    950  for (const auto& activity : mActivityList.ForwardRange()) {
    951    if (activity->MatchesCacheId(aCacheId)) {
    952      activity->Cancel();
    953    }
    954  }
    955 }
    956 
    957 Context::~Context() {
    958  NS_ASSERT_OWNINGTHREAD(Context);
    959  MOZ_DIAGNOSTIC_ASSERT(mManager);
    960  MOZ_DIAGNOSTIC_ASSERT(!mData);
    961 
    962  if (mThreadsafeHandle) {
    963    mThreadsafeHandle->ContextDestroyed(*this);
    964  }
    965 
    966  {
    967    auto destroyingDirectoryLockHandle = std::move(mDirectoryLockHandle);
    968  }
    969 
    970  // Note, this may set the mOrphanedData flag.
    971  mManager->RemoveContext(*this);
    972 
    973  if (mDirectoryMetadata && mDirectoryMetadata->mDir && !mOrphanedData) {
    974    MOZ_ALWAYS_SUCCEEDS(DeleteMarkerFile(*mDirectoryMetadata));
    975  }
    976 
    977  if (mNextContext) {
    978    mNextContext->Start();
    979  }
    980 }
    981 
    982 void Context::Init(Maybe<Context&> aOldContext) {
    983  NS_ASSERT_OWNINGTHREAD(Context);
    984 
    985  if (aOldContext) {
    986    aOldContext->SetNextContext(SafeRefPtrFromThis());
    987    return;
    988  }
    989 
    990  Start();
    991 }
    992 
    993 void Context::Start() {
    994  NS_ASSERT_OWNINGTHREAD(Context);
    995 
    996  // Previous context closing delayed our start, but then we were canceled.
    997  // In this case, just do nothing here.
    998  if (mState == STATE_CONTEXT_CANCELED) {
    999    MOZ_DIAGNOSTIC_ASSERT(!mInitRunnable);
   1000    MOZ_DIAGNOSTIC_ASSERT(!mInitAction);
   1001    // If we can't initialize the quota subsystem we will never be able to
   1002    // clear our shared data object via the target IO thread.  Instead just
   1003    // clear it here to maintain the invariant that the shared data is
   1004    // cleared before Context destruction.
   1005    mData = nullptr;
   1006    return;
   1007  }
   1008 
   1009  MOZ_DIAGNOSTIC_ASSERT(mState == STATE_CONTEXT_PREINIT);
   1010  MOZ_DIAGNOSTIC_ASSERT(!mInitRunnable);
   1011 
   1012  mInitRunnable =
   1013      new QuotaInitRunnable(SafeRefPtrFromThis(), mManager.clonePtr(), mData,
   1014                            mTarget, std::move(mInitAction));
   1015  mState = STATE_CONTEXT_INIT;
   1016 
   1017  nsresult rv = mInitRunnable->Dispatch();
   1018  if (NS_FAILED(rv)) {
   1019    // Shutdown must be delayed until all Contexts are destroyed.  Shutdown
   1020    // must also prevent any new Contexts from being constructed.  Crash
   1021    // for this invariant violation.
   1022    MOZ_CRASH("Failed to dispatch QuotaInitRunnable.");
   1023  }
   1024 }
   1025 
   1026 void Context::DispatchAction(SafeRefPtr<Action> aAction, bool aDoomData) {
   1027  NS_ASSERT_OWNINGTHREAD(Context);
   1028 
   1029  auto runnable = MakeSafeRefPtr<ActionRunnable>(
   1030      SafeRefPtrFromThis(), mData, mTarget, std::move(aAction),
   1031      mDirectoryMetadata, mCipherKeyManager);
   1032 
   1033  if (aDoomData) {
   1034    mData = nullptr;
   1035  }
   1036 
   1037  nsresult rv = runnable->Dispatch();
   1038  if (NS_FAILED(rv)) {
   1039    // Shutdown must be delayed until all Contexts are destroyed.  Crash
   1040    // for this invariant violation.
   1041    MOZ_CRASH("Failed to dispatch ActionRunnable to target thread.");
   1042  }
   1043  AddActivity(*runnable);
   1044 }
   1045 
   1046 void Context::OnQuotaInit(
   1047    nsresult aRv, const Maybe<CacheDirectoryMetadata>& aDirectoryMetadata,
   1048    ClientDirectoryLockHandle aDirectoryLockHandle,
   1049    RefPtr<CipherKeyManager> aCipherKeyManager) {
   1050  NS_ASSERT_OWNINGTHREAD(Context);
   1051 
   1052  MOZ_DIAGNOSTIC_ASSERT(mInitRunnable);
   1053  mInitRunnable = nullptr;
   1054 
   1055  MOZ_DIAGNOSTIC_ASSERT(!mDirectoryMetadata);
   1056  mDirectoryMetadata = aDirectoryMetadata;
   1057 
   1058  // Always save the directory lock to ensure QuotaManager does not shutdown
   1059  // before the Context has gone away.
   1060  MOZ_DIAGNOSTIC_ASSERT(!mDirectoryLockHandle);
   1061  mDirectoryLockHandle = std::move(aDirectoryLockHandle);
   1062 
   1063  MOZ_DIAGNOSTIC_ASSERT(!mCipherKeyManager);
   1064  mCipherKeyManager = std::move(aCipherKeyManager);
   1065 
   1066  // If we opening the context failed, but we were not explicitly canceled,
   1067  // still treat the entire context as canceled.  We don't want to allow
   1068  // new actions to be dispatched.  We also cannot leave the context in
   1069  // the INIT state after failing to open.
   1070  if (NS_FAILED(aRv)) {
   1071    mState = STATE_CONTEXT_CANCELED;
   1072  }
   1073 
   1074  if (mState == STATE_CONTEXT_CANCELED) {
   1075    if (NS_SUCCEEDED(aRv)) {
   1076      aRv = NS_ERROR_ABORT;
   1077    }
   1078    for (uint32_t i = 0; i < mPendingActions.Length(); ++i) {
   1079      mPendingActions[i].mAction->CompleteOnInitiatingThread(aRv);
   1080    }
   1081    mPendingActions.Clear();
   1082    mThreadsafeHandle->AllowToClose();
   1083    // Context will destruct after return here and last ref is released.
   1084    return;
   1085  }
   1086 
   1087  // We could only assert below if quota initialization was a success which
   1088  // is ensured by NS_FAILED(aRv) above
   1089  MOZ_DIAGNOSTIC_ASSERT(mDirectoryMetadata);
   1090  MOZ_DIAGNOSTIC_ASSERT(mDirectoryLockHandle);
   1091  MOZ_DIAGNOSTIC_ASSERT(!mDirectoryLockHandle->Invalidated());
   1092  MOZ_DIAGNOSTIC_ASSERT_IF(mDirectoryMetadata->mIsPrivate, mCipherKeyManager);
   1093 
   1094  MOZ_DIAGNOSTIC_ASSERT(mState == STATE_CONTEXT_INIT);
   1095  mState = STATE_CONTEXT_READY;
   1096 
   1097  for (uint32_t i = 0; i < mPendingActions.Length(); ++i) {
   1098    DispatchAction(std::move(mPendingActions[i].mAction));
   1099  }
   1100  mPendingActions.Clear();
   1101 }
   1102 
   1103 void Context::AddActivity(Activity& aActivity) {
   1104  NS_ASSERT_OWNINGTHREAD(Context);
   1105  MOZ_ASSERT(!mActivityList.Contains(&aActivity));
   1106  mActivityList.AppendElement(WrapNotNullUnchecked(&aActivity));
   1107 }
   1108 
   1109 void Context::RemoveActivity(Activity& aActivity) {
   1110  NS_ASSERT_OWNINGTHREAD(Context);
   1111  MOZ_ALWAYS_TRUE(mActivityList.RemoveElement(&aActivity));
   1112  MOZ_ASSERT(!mActivityList.Contains(&aActivity));
   1113 }
   1114 
   1115 void Context::NoteOrphanedData() {
   1116  NS_ASSERT_OWNINGTHREAD(Context);
   1117  // This may be called more than once
   1118  mOrphanedData = true;
   1119 }
   1120 
   1121 SafeRefPtr<Context::ThreadsafeHandle> Context::CreateThreadsafeHandle() {
   1122  NS_ASSERT_OWNINGTHREAD(Context);
   1123  if (!mThreadsafeHandle) {
   1124    mThreadsafeHandle = MakeSafeRefPtr<ThreadsafeHandle>(SafeRefPtrFromThis());
   1125  }
   1126  return mThreadsafeHandle.clonePtr();
   1127 }
   1128 
   1129 void Context::SetNextContext(SafeRefPtr<Context> aNextContext) {
   1130  NS_ASSERT_OWNINGTHREAD(Context);
   1131  MOZ_DIAGNOSTIC_ASSERT(aNextContext);
   1132  MOZ_DIAGNOSTIC_ASSERT(!mNextContext);
   1133  mNextContext = std::move(aNextContext);
   1134 }
   1135 
   1136 void Context::DoomTargetData() {
   1137  NS_ASSERT_OWNINGTHREAD(Context);
   1138  MOZ_DIAGNOSTIC_ASSERT(mData);
   1139 
   1140  // We are about to drop our reference to the Data.  We need to ensure that
   1141  // the ~Context() destructor does not run until contents of Data have been
   1142  // released on the Target thread.
   1143 
   1144  // Dispatch a no-op Action.  This will hold the Context alive through a
   1145  // roundtrip to the target thread and back to the owning thread.  The
   1146  // ref to the Data object is cleared on the owning thread after creating
   1147  // the ActionRunnable, but before dispatching it.
   1148  DispatchAction(MakeSafeRefPtr<NullAction>(), true /* doomed data */);
   1149 
   1150  MOZ_DIAGNOSTIC_ASSERT(!mData);
   1151 }
   1152 
   1153 void Context::DoStringify(nsACString& aData) {
   1154  NS_ASSERT_OWNINGTHREAD(Context);
   1155 
   1156  aData.Append(
   1157      "Context "_ns + kStringifyStartInstance +
   1158      //
   1159      "State:"_ns + IntToCString(mState) + kStringifyDelimiter +
   1160      //
   1161      "OrphanedData:"_ns + IntToCString(mOrphanedData) + kStringifyDelimiter +
   1162      //
   1163      "InitRunnable:"_ns + IntToCString(static_cast<bool>(mInitRunnable)) +
   1164      kStringifyDelimiter +
   1165      //
   1166      "InitAction:"_ns + IntToCString(static_cast<bool>(mInitAction)) +
   1167      kStringifyDelimiter +
   1168      //
   1169      "PendingActions:"_ns +
   1170      IntToCString(static_cast<uint64_t>(mPendingActions.Length())) +
   1171      kStringifyDelimiter +
   1172      //
   1173      "ActivityList:"_ns +
   1174      IntToCString(static_cast<uint64_t>(mActivityList.Length())));
   1175 
   1176  if (mActivityList.Length() > 0) {
   1177    aData.Append(kStringifyStartSet);
   1178    for (auto activity : mActivityList.ForwardRange()) {
   1179      activity->Stringify(aData);
   1180      aData.Append(kStringifyDelimiter);
   1181    }
   1182    aData.Append(kStringifyEndSet);
   1183  };
   1184 
   1185  aData.Append(kStringifyDelimiter +
   1186               //
   1187               "DirectoryLock:"_ns +
   1188               IntToCString(static_cast<bool>(mDirectoryLockHandle)) +
   1189               kStringifyDelimiter +
   1190               //
   1191               "NextContext:"_ns +
   1192               IntToCString(static_cast<bool>(mNextContext)) +
   1193               //
   1194               kStringifyEndInstance);
   1195 
   1196  if (mNextContext) {
   1197    aData.Append(kStringifyDelimiter);
   1198    mNextContext->Stringify(aData);
   1199  };
   1200 
   1201  aData.Append(kStringifyEndInstance);
   1202 }
   1203 
   1204 }  // namespace mozilla::dom::cache