tor-browser

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

ActorsParent.cpp (47355B)


      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 file,
      5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "ActorsParent.h"
      8 
      9 // Local includes
     10 #include "SimpleDBCommon.h"
     11 
     12 // Global includes
     13 #include <cstdint>
     14 #include <new>
     15 #include <utility>
     16 
     17 #include "ErrorList.h"
     18 #include "MainThreadUtils.h"
     19 #include "NotifyUtils.h"
     20 #include "mozilla/AlreadyAddRefed.h"
     21 #include "mozilla/Assertions.h"
     22 #include "mozilla/Atomics.h"
     23 #include "mozilla/DebugOnly.h"
     24 #include "mozilla/FixedBufferOutputStream.h"
     25 #include "mozilla/Maybe.h"
     26 #include "mozilla/Preferences.h"
     27 #include "mozilla/RefPtr.h"
     28 #include "mozilla/Result.h"
     29 #include "mozilla/ResultExtensions.h"
     30 #include "mozilla/SpinEventLoopUntil.h"
     31 #include "mozilla/StaticPtr.h"
     32 #include "mozilla/dom/PBackgroundSDBConnection.h"
     33 #include "mozilla/dom/PBackgroundSDBConnectionParent.h"
     34 #include "mozilla/dom/PBackgroundSDBRequestParent.h"
     35 #include "mozilla/dom/ipc/IdType.h"
     36 #include "mozilla/dom/quota/Client.h"
     37 #include "mozilla/dom/quota/ClientDirectoryLock.h"
     38 #include "mozilla/dom/quota/ClientDirectoryLockHandle.h"
     39 #include "mozilla/dom/quota/ClientImpl.h"
     40 #include "mozilla/dom/quota/FileStreams.h"
     41 #include "mozilla/dom/quota/PrincipalUtils.h"
     42 #include "mozilla/dom/quota/QuotaCommon.h"
     43 #include "mozilla/dom/quota/QuotaManager.h"
     44 #include "mozilla/dom/quota/ResultExtensions.h"
     45 #include "mozilla/dom/quota/ThreadUtils.h"
     46 #include "mozilla/dom/quota/UsageInfo.h"
     47 #include "mozilla/ipc/BackgroundParent.h"
     48 #include "mozilla/ipc/BackgroundUtils.h"
     49 #include "mozilla/ipc/PBackgroundParent.h"
     50 #include "mozilla/ipc/PBackgroundSharedTypes.h"
     51 #include "mozilla/ipc/ProtocolUtils.h"
     52 #include "nsCOMPtr.h"
     53 #include "nsDebug.h"
     54 #include "nsError.h"
     55 #include "nsIDirectoryEnumerator.h"
     56 #include "nsIEventTarget.h"
     57 #include "nsIFile.h"
     58 #include "nsIFileStreams.h"
     59 #include "nsIInputStream.h"
     60 #include "nsIOutputStream.h"
     61 #include "nsIRunnable.h"
     62 #include "nsISeekableStream.h"
     63 #include "nsISupports.h"
     64 #include "nsIThread.h"
     65 #include "nsLiteralString.h"
     66 #include "nsString.h"
     67 #include "nsStringFwd.h"
     68 #include "nsStringStream.h"
     69 #include "nsTArray.h"
     70 #include "nsTLiteralString.h"
     71 #include "nsTStringRepr.h"
     72 #include "nsThreadUtils.h"
     73 #include "nscore.h"
     74 #include "prio.h"
     75 
     76 namespace mozilla::dom {
     77 
     78 using namespace mozilla::dom::quota;
     79 using namespace mozilla::ipc;
     80 
     81 namespace {
     82 
     83 /*******************************************************************************
     84 * Constants
     85 ******************************************************************************/
     86 
     87 const uint32_t kCopyBufferSize = 32768;
     88 
     89 constexpr auto kSDBSuffix = u".sdb"_ns;
     90 
     91 /*******************************************************************************
     92 * Actor class declarations
     93 ******************************************************************************/
     94 
     95 class StreamHelper final : public Runnable {
     96  nsCOMPtr<nsIEventTarget> mOwningEventTarget;
     97  nsCOMPtr<nsIFileRandomAccessStream> mFileRandomAccessStream;
     98  nsCOMPtr<nsIRunnable> mCallback;
     99 
    100 public:
    101  StreamHelper(nsIFileRandomAccessStream* aFileRandomAccessStream,
    102               nsIRunnable* aCallback);
    103 
    104  void AsyncClose();
    105 
    106 private:
    107  ~StreamHelper() override;
    108 
    109  void RunOnBackgroundThread();
    110 
    111  void RunOnIOThread();
    112 
    113  NS_DECL_NSIRUNNABLE
    114 };
    115 
    116 class Connection final : public PBackgroundSDBConnectionParent {
    117  ClientDirectoryLockHandle mDirectoryLockHandle;
    118  nsCOMPtr<nsIFileRandomAccessStream> mFileRandomAccessStream;
    119  const PrincipalInfo mPrincipalInfo;
    120  nsCString mOrigin;
    121  nsString mName;
    122 
    123  PersistenceType mPersistenceType;
    124  bool mRunningRequest;
    125  bool mOpen;
    126  bool mAllowedToClose;
    127  bool mActorDestroyed;
    128 
    129 public:
    130  Connection(PersistenceType aPersistenceType,
    131             const PrincipalInfo& aPrincipalInfo);
    132 
    133  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(mozilla::dom::Connection, override)
    134 
    135  Maybe<ClientDirectoryLock&> MaybeDirectoryLockRef() const {
    136    AssertIsOnBackgroundThread();
    137 
    138    return ToMaybeRef(mDirectoryLockHandle.get());
    139  }
    140 
    141  nsIFileRandomAccessStream* GetFileRandomAccessStream() const {
    142    AssertIsOnIOThread();
    143 
    144    return mFileRandomAccessStream;
    145  }
    146 
    147  PersistenceType GetPersistenceType() const { return mPersistenceType; }
    148 
    149  const PrincipalInfo& GetPrincipalInfo() const {
    150    AssertIsOnBackgroundThread();
    151 
    152    return mPrincipalInfo;
    153  }
    154 
    155  const nsCString& Origin() const {
    156    AssertIsOnBackgroundThread();
    157    MOZ_ASSERT(!mOrigin.IsEmpty());
    158 
    159    return mOrigin;
    160  }
    161 
    162  const nsString& Name() const {
    163    AssertIsOnBackgroundThread();
    164    MOZ_ASSERT(!mName.IsEmpty());
    165 
    166    return mName;
    167  }
    168 
    169  void OnNewRequest();
    170 
    171  void OnRequestFinished();
    172 
    173  void OnOpen(
    174      const nsACString& aOrigin, const nsAString& aName,
    175      ClientDirectoryLockHandle aDirectoryLockHandle,
    176      already_AddRefed<nsIFileRandomAccessStream> aFileRandomAccessStream);
    177 
    178  void OnClose();
    179 
    180  void AllowToClose();
    181 
    182 private:
    183  ~Connection();
    184 
    185  void MaybeCloseStream();
    186 
    187  bool VerifyRequestParams(const SDBRequestParams& aParams) const;
    188 
    189  // IPDL methods.
    190  virtual void ActorDestroy(ActorDestroyReason aWhy) override;
    191 
    192  mozilla::ipc::IPCResult RecvDeleteMe() override;
    193 
    194  virtual PBackgroundSDBRequestParent* AllocPBackgroundSDBRequestParent(
    195      const SDBRequestParams& aParams) override;
    196 
    197  virtual mozilla::ipc::IPCResult RecvPBackgroundSDBRequestConstructor(
    198      PBackgroundSDBRequestParent* aActor,
    199      const SDBRequestParams& aParams) override;
    200 
    201  virtual bool DeallocPBackgroundSDBRequestParent(
    202      PBackgroundSDBRequestParent* aActor) override;
    203 };
    204 
    205 class ConnectionOperationBase : public Runnable,
    206                                public PBackgroundSDBRequestParent {
    207  nsCOMPtr<nsIEventTarget> mOwningEventTarget;
    208  RefPtr<Connection> mConnection;
    209  nsresult mResultCode;
    210  Atomic<bool> mOperationMayProceed;
    211  bool mActorDestroyed;
    212 
    213 public:
    214  nsIEventTarget* OwningEventTarget() const {
    215    MOZ_ASSERT(mOwningEventTarget);
    216 
    217    return mOwningEventTarget;
    218  }
    219 
    220  bool IsOnOwningThread() const {
    221    MOZ_ASSERT(mOwningEventTarget);
    222 
    223    bool current;
    224    return NS_SUCCEEDED(mOwningEventTarget->IsOnCurrentThread(&current)) &&
    225           current;
    226  }
    227 
    228  void AssertIsOnOwningThread() const {
    229    MOZ_ASSERT(IsOnBackgroundThread());
    230    MOZ_ASSERT(IsOnOwningThread());
    231  }
    232 
    233  Connection* GetConnection() const {
    234    MOZ_ASSERT(mConnection);
    235 
    236    return mConnection;
    237  }
    238 
    239  nsresult ResultCode() const { return mResultCode; }
    240 
    241  void MaybeSetFailureCode(nsresult aErrorCode) {
    242    MOZ_ASSERT(NS_FAILED(aErrorCode));
    243 
    244    if (NS_SUCCEEDED(mResultCode)) {
    245      mResultCode = aErrorCode;
    246    }
    247  }
    248 
    249  // May be called on any thread, but you should call IsActorDestroyed() if
    250  // you know you're on the background thread because it is slightly faster.
    251  bool OperationMayProceed() const { return mOperationMayProceed; }
    252 
    253  bool IsActorDestroyed() const {
    254    AssertIsOnOwningThread();
    255 
    256    return mActorDestroyed;
    257  }
    258 
    259  // May be overridden by subclasses if they need to perform work on the
    260  // background thread before being dispatched but must always call the base
    261  // class implementation. Returning false will kill the child actors and
    262  // prevent dispatch.
    263  virtual bool Init();
    264 
    265  virtual nsresult Dispatch();
    266 
    267  // This callback will be called on the background thread before releasing the
    268  // final reference to this request object. Subclasses may perform any
    269  // additional cleanup here but must always call the base class implementation.
    270  virtual void Cleanup();
    271 
    272 protected:
    273  ConnectionOperationBase(Connection* aConnection)
    274      : Runnable("dom::ConnectionOperationBase"),
    275        mOwningEventTarget(GetCurrentSerialEventTarget()),
    276        mConnection(aConnection),
    277        mResultCode(NS_OK),
    278        mOperationMayProceed(true),
    279        mActorDestroyed(false) {
    280    AssertIsOnOwningThread();
    281  }
    282 
    283  ~ConnectionOperationBase() override;
    284 
    285  void SendResults();
    286 
    287  void DatabaseWork();
    288 
    289  // Methods that subclasses must implement.
    290  virtual nsresult DoDatabaseWork(
    291      nsIFileRandomAccessStream* aFileRandomAccessStream) = 0;
    292 
    293  // Subclasses use this override to set the IPDL response value.
    294  virtual void GetResponse(SDBRequestResponse& aResponse) = 0;
    295 
    296  // A method that subclasses may implement.
    297  virtual void OnSuccess();
    298 
    299 private:
    300  NS_IMETHOD
    301  Run() override;
    302 
    303  // IPDL methods.
    304  void ActorDestroy(ActorDestroyReason aWhy) override;
    305 };
    306 
    307 class OpenOp final : public ConnectionOperationBase {
    308  enum class State {
    309    // Just created on the PBackground thread, dispatched to the main thread.
    310    // Next step is FinishOpen.
    311    Initial,
    312 
    313    // Ensuring quota manager is created and opening directory on the
    314    // PBackground thread. Next step is either SendingResults if quota manager
    315    // is not available or DirectoryOpenPending if quota manager is available.
    316    FinishOpen,
    317 
    318    // Waiting for directory open allowed on the PBackground thread. The next
    319    // step is either SendingResults if directory lock failed to acquire, or
    320    // DatabaseWorkOpen if directory lock is acquired.
    321    DirectoryOpenPending,
    322 
    323    // Waiting to do/doing work on the QuotaManager IO thread. Its next step is
    324    // SendingResults.
    325    DatabaseWorkOpen,
    326 
    327    // Waiting to send/sending results on the PBackground thread. Next step is
    328    // Completed.
    329    SendingResults,
    330 
    331    // All done.
    332    Completed
    333  };
    334 
    335  const SDBRequestOpenParams mParams;
    336  ClientDirectoryLockHandle mDirectoryLockHandle;
    337  nsCOMPtr<nsIFileRandomAccessStream> mFileRandomAccessStream;
    338  // XXX Consider changing this to ClientMetadata.
    339  quota::OriginMetadata mOriginMetadata;
    340  State mState;
    341  bool mFileRandomAccessStreamOpen;
    342 
    343 public:
    344  OpenOp(Connection* aConnection, const SDBRequestParams& aParams);
    345 
    346  nsresult Dispatch() override;
    347 
    348 private:
    349  ~OpenOp() override;
    350 
    351  nsresult Open();
    352 
    353  nsresult FinishOpen();
    354 
    355  nsresult SendToIOThread();
    356 
    357  nsresult DatabaseWork();
    358 
    359  void StreamClosedCallback();
    360 
    361  // ConnectionOperationBase overrides
    362  nsresult DoDatabaseWork(
    363      nsIFileRandomAccessStream* aFileRandomAccessStream) override;
    364 
    365  void GetResponse(SDBRequestResponse& aResponse) override;
    366 
    367  void OnSuccess() override;
    368 
    369  void Cleanup() override;
    370 
    371  NS_IMETHOD
    372  Run() override;
    373 
    374  void DirectoryLockAcquired(ClientDirectoryLockHandle aLockHandle);
    375 
    376  void DirectoryLockFailed();
    377 };
    378 
    379 class SeekOp final : public ConnectionOperationBase {
    380  const SDBRequestSeekParams mParams;
    381 
    382 public:
    383  SeekOp(Connection* aConnection, const SDBRequestParams& aParams);
    384 
    385 private:
    386  ~SeekOp() override = default;
    387 
    388  nsresult DoDatabaseWork(
    389      nsIFileRandomAccessStream* aFileRandomAccessStream) override;
    390 
    391  void GetResponse(SDBRequestResponse& aResponse) override;
    392 };
    393 
    394 class ReadOp final : public ConnectionOperationBase {
    395  const SDBRequestReadParams mParams;
    396 
    397  RefPtr<FixedBufferOutputStream> mOutputStream;
    398 
    399 public:
    400  ReadOp(Connection* aConnection, const SDBRequestParams& aParams);
    401 
    402  bool Init() override;
    403 
    404 private:
    405  ~ReadOp() override = default;
    406 
    407  nsresult DoDatabaseWork(
    408      nsIFileRandomAccessStream* aFileRandomAccessStream) override;
    409 
    410  void GetResponse(SDBRequestResponse& aResponse) override;
    411 };
    412 
    413 class WriteOp final : public ConnectionOperationBase {
    414  const SDBRequestWriteParams mParams;
    415 
    416  nsCOMPtr<nsIInputStream> mInputStream;
    417 
    418  uint64_t mSize;
    419 
    420 public:
    421  WriteOp(Connection* aConnection, const SDBRequestParams& aParams);
    422 
    423  bool Init() override;
    424 
    425 private:
    426  ~WriteOp() override = default;
    427 
    428  nsresult DoDatabaseWork(
    429      nsIFileRandomAccessStream* aFileRandomAccessStream) override;
    430 
    431  void GetResponse(SDBRequestResponse& aResponse) override;
    432 };
    433 
    434 class CloseOp final : public ConnectionOperationBase {
    435 public:
    436  explicit CloseOp(Connection* aConnection);
    437 
    438 private:
    439  ~CloseOp() override = default;
    440 
    441  nsresult DoDatabaseWork(
    442      nsIFileRandomAccessStream* aFileRandomAccessStream) override;
    443 
    444  void GetResponse(SDBRequestResponse& aResponse) override;
    445 
    446  void OnSuccess() override;
    447 };
    448 
    449 /*******************************************************************************
    450 * Other class declarations
    451 ******************************************************************************/
    452 
    453 class QuotaClient final : public mozilla::dom::quota::Client {
    454  static QuotaClient* sInstance;
    455 
    456 public:
    457  QuotaClient();
    458 
    459  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(QuotaClient, override)
    460 
    461  Type GetType() override;
    462 
    463  Result<UsageInfo, nsresult> InitOrigin(PersistenceType aPersistenceType,
    464                                         const OriginMetadata& aOriginMetadata,
    465                                         const AtomicBool& aCanceled) override;
    466 
    467  nsresult InitOriginWithoutTracking(PersistenceType aPersistenceType,
    468                                     const OriginMetadata& aOriginMetadata,
    469                                     const AtomicBool& aCanceled) override;
    470 
    471  Result<UsageInfo, nsresult> GetUsageForOrigin(
    472      PersistenceType aPersistenceType, const OriginMetadata& aOriginMetadata,
    473      const AtomicBool& aCanceled) override;
    474 
    475  void OnOriginClearCompleted(const OriginMetadata& aOriginMetadata) override;
    476 
    477  void OnRepositoryClearCompleted(PersistenceType aPersistenceType) override;
    478 
    479  void ReleaseIOThreadObjects() override;
    480 
    481  void AbortOperationsForLocks(
    482      const DirectoryLockIdTable& aDirectoryLockIds) override;
    483 
    484  void AbortOperationsForProcess(ContentParentId aContentParentId) override;
    485 
    486  void AbortAllOperations() override;
    487 
    488  void StartIdleMaintenance() override;
    489 
    490  void StopIdleMaintenance() override;
    491 
    492 private:
    493  ~QuotaClient() override;
    494 
    495  void InitiateShutdown() override;
    496  bool IsShutdownCompleted() const override;
    497  nsCString GetShutdownStatus() const override;
    498  void ForceKillActors() override;
    499  void FinalizeShutdown() override;
    500 };
    501 
    502 /*******************************************************************************
    503 * Globals
    504 ******************************************************************************/
    505 
    506 using ConnectionArray = nsTArray<NotNull<RefPtr<Connection>>>;
    507 
    508 StaticAutoPtr<ConnectionArray> gOpenConnections;
    509 
    510 template <typename Condition>
    511 void AllowToCloseConnectionsMatching(const Condition& aCondition) {
    512  AssertIsOnBackgroundThread();
    513 
    514  if (gOpenConnections) {
    515    for (const auto& connection : *gOpenConnections) {
    516      if (aCondition(*connection)) {
    517        connection->AllowToClose();
    518      }
    519    }
    520  }
    521 }
    522 
    523 }  // namespace
    524 
    525 /*******************************************************************************
    526 * Exported functions
    527 ******************************************************************************/
    528 
    529 already_AddRefed<PBackgroundSDBConnectionParent>
    530 AllocPBackgroundSDBConnectionParent(const PersistenceType& aPersistenceType,
    531                                    const PrincipalInfo& aPrincipalInfo) {
    532  AssertIsOnBackgroundThread();
    533 
    534  if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread())) {
    535    return nullptr;
    536  }
    537 
    538  if (NS_WARN_IF(!IsValidPersistenceType(aPersistenceType))) {
    539    MOZ_CRASH_UNLESS_FUZZING();
    540    return nullptr;
    541  }
    542 
    543  if (NS_WARN_IF(aPrincipalInfo.type() == PrincipalInfo::TNullPrincipalInfo)) {
    544    MOZ_CRASH_UNLESS_FUZZING();
    545    return nullptr;
    546  }
    547 
    548  if (NS_WARN_IF(!quota::IsPrincipalInfoValid(aPrincipalInfo))) {
    549    MOZ_CRASH_UNLESS_FUZZING();
    550    return nullptr;
    551  }
    552 
    553  RefPtr<Connection> actor = new Connection(aPersistenceType, aPrincipalInfo);
    554 
    555  return actor.forget();
    556 }
    557 
    558 bool RecvPBackgroundSDBConnectionConstructor(
    559    PBackgroundSDBConnectionParent* aActor,
    560    const PersistenceType& aPersistenceType,
    561    const PrincipalInfo& aPrincipalInfo) {
    562  AssertIsOnBackgroundThread();
    563  MOZ_ASSERT(aActor);
    564  MOZ_ASSERT(!QuotaClient::IsShuttingDownOnBackgroundThread());
    565 
    566  return true;
    567 }
    568 
    569 namespace simpledb {
    570 
    571 already_AddRefed<mozilla::dom::quota::Client> CreateQuotaClient() {
    572  AssertIsOnBackgroundThread();
    573 
    574  RefPtr<QuotaClient> client = new QuotaClient();
    575  return client.forget();
    576 }
    577 
    578 }  // namespace simpledb
    579 
    580 /*******************************************************************************
    581 * StreamHelper
    582 ******************************************************************************/
    583 
    584 StreamHelper::StreamHelper(nsIFileRandomAccessStream* aFileRandomAccessStream,
    585                           nsIRunnable* aCallback)
    586    : Runnable("dom::StreamHelper"),
    587      mOwningEventTarget(GetCurrentSerialEventTarget()),
    588      mFileRandomAccessStream(aFileRandomAccessStream),
    589      mCallback(aCallback) {
    590  AssertIsOnBackgroundThread();
    591  MOZ_ASSERT(aFileRandomAccessStream);
    592  MOZ_ASSERT(aCallback);
    593 }
    594 
    595 StreamHelper::~StreamHelper() {
    596  MOZ_ASSERT(!mFileRandomAccessStream);
    597  MOZ_ASSERT(!mCallback);
    598 }
    599 
    600 void StreamHelper::AsyncClose() {
    601  AssertIsOnBackgroundThread();
    602 
    603  QuotaManager* quotaManager = QuotaManager::Get();
    604  MOZ_ASSERT(quotaManager);
    605 
    606  MOZ_ALWAYS_SUCCEEDS(
    607      quotaManager->IOThread()->Dispatch(this, NS_DISPATCH_NORMAL));
    608 }
    609 
    610 void StreamHelper::RunOnBackgroundThread() {
    611  AssertIsOnBackgroundThread();
    612 
    613  nsCOMPtr<nsIFileRandomAccessStream> fileRandomAccessStream;
    614  mFileRandomAccessStream.swap(fileRandomAccessStream);
    615 
    616  nsCOMPtr<nsIRunnable> callback;
    617  mCallback.swap(callback);
    618 
    619  callback->Run();
    620 }
    621 
    622 void StreamHelper::RunOnIOThread() {
    623  AssertIsOnIOThread();
    624  MOZ_ASSERT(mFileRandomAccessStream);
    625 
    626  nsCOMPtr<nsIInputStream> inputStream =
    627      do_QueryInterface(mFileRandomAccessStream);
    628  MOZ_ASSERT(inputStream);
    629 
    630  nsresult rv = inputStream->Close();
    631  (void)NS_WARN_IF(NS_FAILED(rv));
    632 
    633  MOZ_ALWAYS_SUCCEEDS(mOwningEventTarget->Dispatch(this, NS_DISPATCH_NORMAL));
    634 }
    635 
    636 NS_IMETHODIMP
    637 StreamHelper::Run() {
    638  MOZ_ASSERT(mCallback);
    639 
    640  if (IsOnBackgroundThread()) {
    641    RunOnBackgroundThread();
    642  } else {
    643    RunOnIOThread();
    644  }
    645 
    646  return NS_OK;
    647 }
    648 
    649 /*******************************************************************************
    650 * Connection
    651 ******************************************************************************/
    652 
    653 Connection::Connection(PersistenceType aPersistenceType,
    654                       const PrincipalInfo& aPrincipalInfo)
    655    : mPrincipalInfo(aPrincipalInfo),
    656      mPersistenceType(aPersistenceType),
    657      mRunningRequest(false),
    658      mOpen(false),
    659      mAllowedToClose(false),
    660      mActorDestroyed(false) {
    661  AssertIsOnBackgroundThread();
    662  MOZ_ASSERT(!QuotaClient::IsShuttingDownOnBackgroundThread());
    663 }
    664 
    665 Connection::~Connection() {
    666  MOZ_ASSERT(!mRunningRequest);
    667  MOZ_ASSERT(!mOpen);
    668  MOZ_ASSERT(mActorDestroyed);
    669 }
    670 
    671 void Connection::OnNewRequest() {
    672  AssertIsOnBackgroundThread();
    673  MOZ_ASSERT(!mRunningRequest);
    674 
    675  mRunningRequest = true;
    676 }
    677 
    678 void Connection::OnRequestFinished() {
    679  AssertIsOnBackgroundThread();
    680  MOZ_ASSERT(mRunningRequest);
    681 
    682  mRunningRequest = false;
    683 
    684  MaybeCloseStream();
    685 }
    686 
    687 void Connection::OnOpen(
    688    const nsACString& aOrigin, const nsAString& aName,
    689    ClientDirectoryLockHandle aDirectoryLockHandle,
    690    already_AddRefed<nsIFileRandomAccessStream> aFileRandomAccessStream) {
    691  AssertIsOnBackgroundThread();
    692  MOZ_ASSERT(!aOrigin.IsEmpty());
    693  MOZ_ASSERT(!aName.IsEmpty());
    694  MOZ_ASSERT(mOrigin.IsEmpty());
    695  MOZ_ASSERT(mName.IsEmpty());
    696  MOZ_ASSERT(!mDirectoryLockHandle);
    697  MOZ_ASSERT(!mFileRandomAccessStream);
    698  MOZ_ASSERT(!mOpen);
    699 
    700  mOrigin = aOrigin;
    701  mName = aName;
    702  mDirectoryLockHandle = std::move(aDirectoryLockHandle);
    703  mFileRandomAccessStream = aFileRandomAccessStream;
    704  mOpen = true;
    705 
    706  if (!gOpenConnections) {
    707    gOpenConnections = new ConnectionArray();
    708  }
    709 
    710  gOpenConnections->AppendElement(WrapNotNullUnchecked(this));
    711 
    712  if (mDirectoryLockHandle->Invalidated()) {
    713    AllowToClose();
    714  }
    715 }
    716 
    717 void Connection::OnClose() {
    718  AssertIsOnBackgroundThread();
    719  MOZ_ASSERT(!mOrigin.IsEmpty());
    720  MOZ_ASSERT(mDirectoryLockHandle);
    721  MOZ_ASSERT(mFileRandomAccessStream);
    722  MOZ_ASSERT(mOpen);
    723 
    724  mOrigin.Truncate();
    725  mName.Truncate();
    726 
    727  {
    728    auto destroyingDirectoryLockHandle = std::move(mDirectoryLockHandle);
    729  }
    730 
    731  mFileRandomAccessStream = nullptr;
    732  mOpen = false;
    733 
    734  MOZ_ASSERT(gOpenConnections);
    735  gOpenConnections->RemoveElement(this);
    736 
    737  if (gOpenConnections->IsEmpty()) {
    738    gOpenConnections = nullptr;
    739  }
    740 
    741  if (mAllowedToClose && !mActorDestroyed) {
    742    (void)SendClosed();
    743  }
    744 }
    745 
    746 void Connection::AllowToClose() {
    747  AssertIsOnBackgroundThread();
    748 
    749  if (mAllowedToClose) {
    750    return;
    751  }
    752 
    753  mAllowedToClose = true;
    754 
    755  if (!mActorDestroyed) {
    756    (void)SendAllowToClose();
    757  }
    758 
    759  MaybeCloseStream();
    760 }
    761 
    762 void Connection::MaybeCloseStream() {
    763  AssertIsOnBackgroundThread();
    764 
    765  if (!mRunningRequest && mOpen && mAllowedToClose) {
    766    nsCOMPtr<nsIRunnable> callback = NewRunnableMethod(
    767        "dom::Connection::OnClose", this, &Connection::OnClose);
    768 
    769    RefPtr<StreamHelper> helper =
    770        new StreamHelper(mFileRandomAccessStream, callback);
    771    helper->AsyncClose();
    772  }
    773 }
    774 
    775 bool Connection::VerifyRequestParams(const SDBRequestParams& aParams) const {
    776  AssertIsOnBackgroundThread();
    777  MOZ_ASSERT(aParams.type() != SDBRequestParams::T__None);
    778 
    779  switch (aParams.type()) {
    780    case SDBRequestParams::TSDBRequestOpenParams: {
    781      if (NS_WARN_IF(mOpen)) {
    782        MOZ_CRASH_UNLESS_FUZZING();
    783        return false;
    784      }
    785 
    786      break;
    787    }
    788 
    789    case SDBRequestParams::TSDBRequestSeekParams:
    790    case SDBRequestParams::TSDBRequestReadParams:
    791    case SDBRequestParams::TSDBRequestWriteParams:
    792    case SDBRequestParams::TSDBRequestCloseParams: {
    793      if (NS_WARN_IF(!mOpen)) {
    794        MOZ_CRASH_UNLESS_FUZZING();
    795        return false;
    796      }
    797 
    798      break;
    799    }
    800 
    801    default:
    802      MOZ_CRASH("Should never get here!");
    803  }
    804 
    805  return true;
    806 }
    807 
    808 void Connection::ActorDestroy(ActorDestroyReason aWhy) {
    809  AssertIsOnBackgroundThread();
    810  MOZ_ASSERT(!mActorDestroyed);
    811 
    812  mActorDestroyed = true;
    813 
    814  AllowToClose();
    815 }
    816 
    817 mozilla::ipc::IPCResult Connection::RecvDeleteMe() {
    818  AssertIsOnBackgroundThread();
    819  MOZ_ASSERT(!mActorDestroyed);
    820 
    821  IProtocol* mgr = Manager();
    822  if (!PBackgroundSDBConnectionParent::Send__delete__(this)) {
    823    return IPC_FAIL_NO_REASON(mgr);
    824  }
    825 
    826  return IPC_OK();
    827 }
    828 
    829 PBackgroundSDBRequestParent* Connection::AllocPBackgroundSDBRequestParent(
    830    const SDBRequestParams& aParams) {
    831  AssertIsOnBackgroundThread();
    832  MOZ_ASSERT(aParams.type() != SDBRequestParams::T__None);
    833 
    834  if (aParams.type() == SDBRequestParams::TSDBRequestOpenParams &&
    835      NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread())) {
    836    return nullptr;
    837  }
    838 
    839  if (mAllowedToClose) {
    840    return nullptr;
    841  }
    842 
    843 #ifdef DEBUG
    844  // Always verify parameters in DEBUG builds!
    845  bool trustParams = false;
    846 #else
    847  PBackgroundParent* backgroundActor = Manager();
    848  MOZ_ASSERT(backgroundActor);
    849 
    850  bool trustParams = !BackgroundParent::IsOtherProcessActor(backgroundActor);
    851 #endif
    852 
    853  if (NS_WARN_IF(!trustParams && !VerifyRequestParams(aParams))) {
    854    MOZ_CRASH_UNLESS_FUZZING();
    855    return nullptr;
    856  }
    857 
    858  if (NS_WARN_IF(mRunningRequest)) {
    859    MOZ_CRASH_UNLESS_FUZZING();
    860    return nullptr;
    861  }
    862 
    863  QM_TRY(QuotaManager::EnsureCreated(), nullptr);
    864 
    865  RefPtr<ConnectionOperationBase> actor;
    866 
    867  switch (aParams.type()) {
    868    case SDBRequestParams::TSDBRequestOpenParams:
    869      actor = new OpenOp(this, aParams);
    870      break;
    871 
    872    case SDBRequestParams::TSDBRequestSeekParams:
    873      actor = new SeekOp(this, aParams);
    874      break;
    875 
    876    case SDBRequestParams::TSDBRequestReadParams:
    877      actor = new ReadOp(this, aParams);
    878      break;
    879 
    880    case SDBRequestParams::TSDBRequestWriteParams:
    881      actor = new WriteOp(this, aParams);
    882      break;
    883 
    884    case SDBRequestParams::TSDBRequestCloseParams:
    885      actor = new CloseOp(this);
    886      break;
    887 
    888    default:
    889      MOZ_CRASH("Should never get here!");
    890  }
    891 
    892  // Transfer ownership to IPDL.
    893  return actor.forget().take();
    894 }
    895 
    896 mozilla::ipc::IPCResult Connection::RecvPBackgroundSDBRequestConstructor(
    897    PBackgroundSDBRequestParent* aActor, const SDBRequestParams& aParams) {
    898  AssertIsOnBackgroundThread();
    899  MOZ_ASSERT(aActor);
    900  MOZ_ASSERT(aParams.type() != SDBRequestParams::T__None);
    901  MOZ_ASSERT_IF(aParams.type() == SDBRequestParams::TSDBRequestOpenParams,
    902                !QuotaClient::IsShuttingDownOnBackgroundThread());
    903  MOZ_ASSERT(!mAllowedToClose);
    904  MOZ_ASSERT(!mRunningRequest);
    905 
    906  auto* op = static_cast<ConnectionOperationBase*>(aActor);
    907 
    908  if (NS_WARN_IF(!op->Init())) {
    909    op->Cleanup();
    910    return IPC_FAIL_NO_REASON(this);
    911  }
    912 
    913  if (NS_WARN_IF(NS_FAILED(op->Dispatch()))) {
    914    op->Cleanup();
    915    return IPC_FAIL_NO_REASON(this);
    916  }
    917 
    918  return IPC_OK();
    919 }
    920 
    921 bool Connection::DeallocPBackgroundSDBRequestParent(
    922    PBackgroundSDBRequestParent* aActor) {
    923  AssertIsOnBackgroundThread();
    924  MOZ_ASSERT(aActor);
    925 
    926  // Transfer ownership back from IPDL.
    927  RefPtr<ConnectionOperationBase> actor =
    928      dont_AddRef(static_cast<ConnectionOperationBase*>(aActor));
    929  return true;
    930 }
    931 
    932 /*******************************************************************************
    933 * ConnectionOperationBase
    934 ******************************************************************************/
    935 
    936 ConnectionOperationBase::~ConnectionOperationBase() {
    937  MOZ_ASSERT(
    938      !mConnection,
    939      "ConnectionOperationBase::Cleanup() was not called by a subclass!");
    940  MOZ_ASSERT(mActorDestroyed);
    941 }
    942 
    943 bool ConnectionOperationBase::Init() {
    944  AssertIsOnBackgroundThread();
    945  MOZ_ASSERT(mConnection);
    946 
    947  mConnection->OnNewRequest();
    948 
    949  return true;
    950 }
    951 
    952 nsresult ConnectionOperationBase::Dispatch() {
    953  if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) ||
    954      IsActorDestroyed()) {
    955    return NS_ERROR_ABORT;
    956  }
    957 
    958  QuotaManager* quotaManager = QuotaManager::Get();
    959  MOZ_ASSERT(quotaManager);
    960 
    961  nsresult rv = quotaManager->IOThread()->Dispatch(this, NS_DISPATCH_NORMAL);
    962  if (NS_WARN_IF(NS_FAILED(rv))) {
    963    return rv;
    964  }
    965 
    966  return NS_OK;
    967 }
    968 
    969 void ConnectionOperationBase::Cleanup() {
    970  AssertIsOnOwningThread();
    971  MOZ_ASSERT(mConnection);
    972 
    973  mConnection->OnRequestFinished();
    974 
    975  mConnection = nullptr;
    976 }
    977 
    978 void ConnectionOperationBase::SendResults() {
    979  AssertIsOnOwningThread();
    980 
    981  if (IsActorDestroyed()) {
    982    MaybeSetFailureCode(NS_ERROR_FAILURE);
    983  } else {
    984    SDBRequestResponse response;
    985 
    986    if (NS_SUCCEEDED(mResultCode)) {
    987      GetResponse(response);
    988 
    989      MOZ_ASSERT(response.type() != SDBRequestResponse::T__None);
    990      MOZ_ASSERT(response.type() != SDBRequestResponse::Tnsresult);
    991 
    992      OnSuccess();
    993    } else {
    994      response = mResultCode;
    995    }
    996 
    997    (void)PBackgroundSDBRequestParent::Send__delete__(this, response);
    998  }
    999 
   1000  Cleanup();
   1001 }
   1002 
   1003 void ConnectionOperationBase::DatabaseWork() {
   1004  AssertIsOnIOThread();
   1005  MOZ_ASSERT(NS_SUCCEEDED(mResultCode));
   1006 
   1007  if (!OperationMayProceed()) {
   1008    // The operation was canceled in some way, likely because the child process
   1009    // has crashed.
   1010    mResultCode = NS_ERROR_ABORT;
   1011  } else {
   1012    nsIFileRandomAccessStream* fileRandomAccessStream =
   1013        mConnection->GetFileRandomAccessStream();
   1014    MOZ_ASSERT(fileRandomAccessStream);
   1015 
   1016    nsresult rv = DoDatabaseWork(fileRandomAccessStream);
   1017    if (NS_FAILED(rv)) {
   1018      mResultCode = rv;
   1019    }
   1020  }
   1021 
   1022  MOZ_ALWAYS_SUCCEEDS(mOwningEventTarget->Dispatch(this, NS_DISPATCH_NORMAL));
   1023 }
   1024 
   1025 void ConnectionOperationBase::OnSuccess() { AssertIsOnOwningThread(); }
   1026 
   1027 NS_IMETHODIMP
   1028 ConnectionOperationBase::Run() {
   1029  if (IsOnBackgroundThread()) {
   1030    SendResults();
   1031  } else {
   1032    DatabaseWork();
   1033  }
   1034 
   1035  return NS_OK;
   1036 }
   1037 
   1038 void ConnectionOperationBase::ActorDestroy(ActorDestroyReason aWhy) {
   1039  AssertIsOnBackgroundThread();
   1040 
   1041  mOperationMayProceed = false;
   1042  mActorDestroyed = true;
   1043 }
   1044 
   1045 OpenOp::OpenOp(Connection* aConnection, const SDBRequestParams& aParams)
   1046    : ConnectionOperationBase(aConnection),
   1047      mParams(aParams.get_SDBRequestOpenParams()),
   1048      mState(State::Initial),
   1049      mFileRandomAccessStreamOpen(false) {
   1050  MOZ_ASSERT(aParams.type() == SDBRequestParams::TSDBRequestOpenParams);
   1051 }
   1052 
   1053 OpenOp::~OpenOp() {
   1054  MOZ_ASSERT(mDirectoryLockHandle.IsInert());
   1055  MOZ_ASSERT(!mFileRandomAccessStream);
   1056  MOZ_ASSERT(!mFileRandomAccessStreamOpen);
   1057  MOZ_ASSERT_IF(OperationMayProceed(),
   1058                mState == State::Initial || mState == State::Completed);
   1059 }
   1060 
   1061 nsresult OpenOp::Dispatch() {
   1062  MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(this));
   1063 
   1064  return NS_OK;
   1065 }
   1066 
   1067 nsresult OpenOp::Open() {
   1068  MOZ_ASSERT(NS_IsMainThread());
   1069  MOZ_ASSERT(mState == State::Initial);
   1070 
   1071  if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonBackgroundThread()) ||
   1072      !OperationMayProceed()) {
   1073    return NS_ERROR_ABORT;
   1074  }
   1075 
   1076  if (NS_WARN_IF(!Preferences::GetBool(kPrefSimpleDBEnabled, false))) {
   1077    return NS_ERROR_UNEXPECTED;
   1078  }
   1079 
   1080  mState = State::FinishOpen;
   1081  MOZ_ALWAYS_SUCCEEDS(OwningEventTarget()->Dispatch(this, NS_DISPATCH_NORMAL));
   1082 
   1083  return NS_OK;
   1084 }
   1085 
   1086 nsresult OpenOp::FinishOpen() {
   1087  AssertIsOnOwningThread();
   1088  MOZ_ASSERT(mOriginMetadata.mOrigin.IsEmpty());
   1089  MOZ_ASSERT(!mDirectoryLockHandle);
   1090  MOZ_ASSERT(mState == State::FinishOpen);
   1091 
   1092  if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) ||
   1093      IsActorDestroyed()) {
   1094    return NS_ERROR_ABORT;
   1095  }
   1096 
   1097  QuotaManager* quotaManager = QuotaManager::Get();
   1098  MOZ_ASSERT(quotaManager);
   1099 
   1100  const PrincipalInfo& principalInfo = GetConnection()->GetPrincipalInfo();
   1101 
   1102  PersistenceType persistenceType = GetConnection()->GetPersistenceType();
   1103 
   1104  if (principalInfo.type() == PrincipalInfo::TSystemPrincipalInfo) {
   1105    mOriginMetadata = {quota::GetInfoForChrome(), persistenceType};
   1106  } else {
   1107    MOZ_ASSERT(principalInfo.type() == PrincipalInfo::TContentPrincipalInfo);
   1108 
   1109    QM_TRY_UNWRAP(
   1110        auto principalMetadata,
   1111        quota::GetInfoFromValidatedPrincipalInfo(*quotaManager, principalInfo));
   1112 
   1113    mOriginMetadata = {std::move(principalMetadata), persistenceType};
   1114  }
   1115 
   1116  if (gOpenConnections) {
   1117    for (const auto& connection : *gOpenConnections) {
   1118      if (connection->Origin() == mOriginMetadata.mOrigin &&
   1119          connection->Name() == mParams.name()) {
   1120        return NS_ERROR_STORAGE_BUSY;
   1121      }
   1122    }
   1123  }
   1124 
   1125  // Open the directory
   1126 
   1127  mState = State::DirectoryOpenPending;
   1128 
   1129  quotaManager
   1130      ->OpenClientDirectory({mOriginMetadata, mozilla::dom::quota::Client::SDB})
   1131      ->Then(
   1132          GetCurrentSerialEventTarget(), __func__,
   1133          [self = RefPtr(this)](QuotaManager::ClientDirectoryLockHandlePromise::
   1134                                    ResolveOrRejectValue&& aValue) {
   1135            if (aValue.IsResolve()) {
   1136              self->DirectoryLockAcquired(std::move(aValue.ResolveValue()));
   1137            } else {
   1138              self->DirectoryLockFailed();
   1139            }
   1140          });
   1141 
   1142  return NS_OK;
   1143 }
   1144 
   1145 nsresult OpenOp::SendToIOThread() {
   1146  AssertIsOnOwningThread();
   1147  MOZ_ASSERT(mState == State::DirectoryOpenPending);
   1148 
   1149  if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) ||
   1150      IsActorDestroyed()) {
   1151    return NS_ERROR_ABORT;
   1152  }
   1153 
   1154  mFileRandomAccessStream = new FileRandomAccessStream(
   1155      GetConnection()->GetPersistenceType(), mOriginMetadata,
   1156      mozilla::dom::quota::Client::SDB);
   1157 
   1158  QuotaManager* quotaManager = QuotaManager::Get();
   1159  MOZ_ASSERT(quotaManager);
   1160 
   1161  // Must set this before dispatching otherwise we will race with the IO thread.
   1162  mState = State::DatabaseWorkOpen;
   1163 
   1164  nsresult rv = quotaManager->IOThread()->Dispatch(this, NS_DISPATCH_NORMAL);
   1165  if (NS_WARN_IF(NS_FAILED(rv))) {
   1166    return rv;
   1167  }
   1168 
   1169  simpledb::NotifyDatabaseWorkStarted();
   1170 
   1171  return NS_OK;
   1172 }
   1173 
   1174 nsresult OpenOp::DatabaseWork() {
   1175  AssertIsOnIOThread();
   1176  MOZ_ASSERT(mState == State::DatabaseWorkOpen);
   1177  MOZ_ASSERT(mFileRandomAccessStream);
   1178  MOZ_ASSERT(!mFileRandomAccessStreamOpen);
   1179 
   1180  if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonBackgroundThread()) ||
   1181      !OperationMayProceed()) {
   1182    return NS_ERROR_ABORT;
   1183  }
   1184 
   1185  QuotaManager* quotaManager = QuotaManager::Get();
   1186  MOZ_ASSERT(quotaManager);
   1187 
   1188  QM_TRY_INSPECT(
   1189      const auto& dbDirectory,
   1190      ([persistenceType = GetConnection()->GetPersistenceType(), &quotaManager,
   1191        this]() -> mozilla::Result<nsCOMPtr<nsIFile>, nsresult> {
   1192        if (persistenceType == PERSISTENCE_TYPE_PERSISTENT) {
   1193          QM_TRY_RETURN(quotaManager->GetOriginDirectory(mOriginMetadata));
   1194        }
   1195 
   1196        QM_TRY_RETURN(
   1197            quotaManager->GetOrCreateTemporaryOriginDirectory(mOriginMetadata));
   1198      }()));
   1199 
   1200  nsresult rv =
   1201      dbDirectory->Append(NS_LITERAL_STRING_FROM_CSTRING(SDB_DIRECTORY_NAME));
   1202  if (NS_WARN_IF(NS_FAILED(rv))) {
   1203    return rv;
   1204  }
   1205 
   1206  bool exists;
   1207  rv = dbDirectory->Exists(&exists);
   1208  if (NS_WARN_IF(NS_FAILED(rv))) {
   1209    return rv;
   1210  }
   1211 
   1212  if (!exists) {
   1213    rv = dbDirectory->Create(nsIFile::DIRECTORY_TYPE, 0755);
   1214    if (NS_WARN_IF(NS_FAILED(rv))) {
   1215      return rv;
   1216    }
   1217  }
   1218 #ifdef DEBUG
   1219  else {
   1220    bool isDirectory;
   1221    MOZ_ASSERT(NS_SUCCEEDED(dbDirectory->IsDirectory(&isDirectory)));
   1222    MOZ_ASSERT(isDirectory);
   1223  }
   1224 #endif
   1225 
   1226  nsCOMPtr<nsIFile> dbFile;
   1227  rv = dbDirectory->Clone(getter_AddRefs(dbFile));
   1228  if (NS_WARN_IF(NS_FAILED(rv))) {
   1229    return rv;
   1230  }
   1231 
   1232  rv = dbFile->Append(mParams.name() + kSDBSuffix);
   1233  if (NS_WARN_IF(NS_FAILED(rv))) {
   1234    return rv;
   1235  }
   1236 
   1237  nsString databaseFilePath;
   1238  rv = dbFile->GetPath(databaseFilePath);
   1239  if (NS_WARN_IF(NS_FAILED(rv))) {
   1240    return rv;
   1241  }
   1242 
   1243  rv = mFileRandomAccessStream->Init(dbFile, PR_RDWR | PR_CREATE_FILE, 0644, 0);
   1244  if (NS_WARN_IF(NS_FAILED(rv))) {
   1245    return rv;
   1246  }
   1247 
   1248  mFileRandomAccessStreamOpen = true;
   1249 
   1250  rv = DoDatabaseWork(mFileRandomAccessStream);
   1251  if (NS_WARN_IF(NS_FAILED(rv))) {
   1252    return rv;
   1253  }
   1254 
   1255  // Must set mState before dispatching otherwise we will race with the owning
   1256  // thread.
   1257  mState = State::SendingResults;
   1258 
   1259  rv = OwningEventTarget()->Dispatch(this, NS_DISPATCH_NORMAL);
   1260  if (NS_WARN_IF(NS_FAILED(rv))) {
   1261    return rv;
   1262  }
   1263 
   1264  return NS_OK;
   1265 }
   1266 
   1267 void OpenOp::StreamClosedCallback() {
   1268  AssertIsOnOwningThread();
   1269  MOZ_ASSERT(NS_FAILED(ResultCode()));
   1270  MOZ_ASSERT(mDirectoryLockHandle);
   1271  MOZ_ASSERT(mFileRandomAccessStream);
   1272  MOZ_ASSERT(mFileRandomAccessStreamOpen);
   1273 
   1274  {
   1275    auto destroyingDirectoryLockHandle = std::move(mDirectoryLockHandle);
   1276  }
   1277 
   1278  mFileRandomAccessStream = nullptr;
   1279  mFileRandomAccessStreamOpen = false;
   1280 }
   1281 
   1282 nsresult OpenOp::DoDatabaseWork(
   1283    nsIFileRandomAccessStream* aFileRandomAccessStream) {
   1284  AssertIsOnIOThread();
   1285 
   1286  SleepIfEnabled(
   1287      StaticPrefs::dom_simpledb_databaseInitialization_pauseOnIOThreadMs());
   1288 
   1289  return NS_OK;
   1290 }
   1291 
   1292 void OpenOp::GetResponse(SDBRequestResponse& aResponse) {
   1293  AssertIsOnOwningThread();
   1294 
   1295  aResponse = SDBRequestOpenResponse();
   1296 }
   1297 
   1298 void OpenOp::OnSuccess() {
   1299  AssertIsOnOwningThread();
   1300  MOZ_ASSERT(NS_SUCCEEDED(ResultCode()));
   1301  MOZ_ASSERT(!mOriginMetadata.mOrigin.IsEmpty());
   1302  MOZ_ASSERT(mDirectoryLockHandle);
   1303  MOZ_ASSERT(mFileRandomAccessStream);
   1304  MOZ_ASSERT(mFileRandomAccessStreamOpen);
   1305 
   1306  ClientDirectoryLockHandle directoryLockHandle;
   1307  nsCOMPtr<nsIFileRandomAccessStream> fileRandomAccessStream;
   1308 
   1309  directoryLockHandle = std::move(mDirectoryLockHandle);
   1310  mFileRandomAccessStream.swap(fileRandomAccessStream);
   1311  mFileRandomAccessStreamOpen = false;
   1312 
   1313  GetConnection()->OnOpen(mOriginMetadata.mOrigin, mParams.name(),
   1314                          std::move(directoryLockHandle),
   1315                          fileRandomAccessStream.forget());
   1316 }
   1317 
   1318 void OpenOp::Cleanup() {
   1319  AssertIsOnOwningThread();
   1320  MOZ_ASSERT_IF(mFileRandomAccessStreamOpen, mFileRandomAccessStream);
   1321 
   1322  if (mFileRandomAccessStream && mFileRandomAccessStreamOpen) {
   1323    // If we have an initialized file stream then the operation must have failed
   1324    // and there must be a directory lock too.
   1325    MOZ_ASSERT(NS_FAILED(ResultCode()));
   1326    MOZ_ASSERT(mDirectoryLockHandle);
   1327 
   1328    // We must close the stream on the I/O thread before releasing it on this
   1329    // thread. The directory lock can't be released either.
   1330    nsCOMPtr<nsIRunnable> callback =
   1331        NewRunnableMethod("dom::OpenOp::StreamClosedCallback", this,
   1332                          &OpenOp::StreamClosedCallback);
   1333 
   1334    RefPtr<StreamHelper> helper =
   1335        new StreamHelper(mFileRandomAccessStream, callback);
   1336    helper->AsyncClose();
   1337  } else {
   1338    {
   1339      auto destroyingDirectoryLockHandle = std::move(mDirectoryLockHandle);
   1340    }
   1341 
   1342    mFileRandomAccessStream = nullptr;
   1343    MOZ_ASSERT(!mFileRandomAccessStreamOpen);
   1344  }
   1345 
   1346  ConnectionOperationBase::Cleanup();
   1347 }
   1348 
   1349 NS_IMETHODIMP
   1350 OpenOp::Run() {
   1351  nsresult rv;
   1352 
   1353  switch (mState) {
   1354    case State::Initial:
   1355      rv = Open();
   1356      break;
   1357 
   1358    case State::FinishOpen:
   1359      rv = FinishOpen();
   1360      break;
   1361 
   1362    case State::DatabaseWorkOpen:
   1363      rv = DatabaseWork();
   1364      break;
   1365 
   1366    case State::SendingResults:
   1367      SendResults();
   1368      return NS_OK;
   1369 
   1370    default:
   1371      MOZ_CRASH("Bad state!");
   1372  }
   1373 
   1374  if (NS_WARN_IF(NS_FAILED(rv)) && mState != State::SendingResults) {
   1375    MaybeSetFailureCode(rv);
   1376 
   1377    // Must set mState before dispatching otherwise we will race with the owning
   1378    // thread.
   1379    mState = State::SendingResults;
   1380 
   1381    if (IsOnOwningThread()) {
   1382      SendResults();
   1383    } else {
   1384      MOZ_ALWAYS_SUCCEEDS(
   1385          OwningEventTarget()->Dispatch(this, NS_DISPATCH_NORMAL));
   1386    }
   1387  }
   1388 
   1389  return NS_OK;
   1390 }
   1391 
   1392 void OpenOp::DirectoryLockAcquired(ClientDirectoryLockHandle aLockHandle) {
   1393  AssertIsOnOwningThread();
   1394  MOZ_ASSERT(mState == State::DirectoryOpenPending);
   1395  MOZ_ASSERT(!mDirectoryLockHandle);
   1396 
   1397  mDirectoryLockHandle = std::move(aLockHandle);
   1398 
   1399  auto cleanupAndReturn = [self = RefPtr(this)](const nsresult rv) {
   1400    self->MaybeSetFailureCode(rv);
   1401 
   1402    // The caller holds a strong reference to us, no need for a self reference
   1403    // before calling Run().
   1404 
   1405    self->mState = State::SendingResults;
   1406    MOZ_ALWAYS_SUCCEEDS(self->Run());
   1407  };
   1408 
   1409  if (mDirectoryLockHandle->Invalidated()) {
   1410    return cleanupAndReturn(NS_ERROR_ABORT);
   1411  }
   1412 
   1413  QM_TRY(MOZ_TO_RESULT(SendToIOThread()), cleanupAndReturn);
   1414 }
   1415 
   1416 void OpenOp::DirectoryLockFailed() {
   1417  AssertIsOnOwningThread();
   1418  MOZ_ASSERT(mState == State::DirectoryOpenPending);
   1419  MOZ_ASSERT(!mDirectoryLockHandle);
   1420 
   1421  MaybeSetFailureCode(NS_ERROR_FAILURE);
   1422 
   1423  // The caller holds a strong reference to us, no need for a self reference
   1424  // before calling Run().
   1425 
   1426  mState = State::SendingResults;
   1427  MOZ_ALWAYS_SUCCEEDS(Run());
   1428 }
   1429 
   1430 SeekOp::SeekOp(Connection* aConnection, const SDBRequestParams& aParams)
   1431    : ConnectionOperationBase(aConnection),
   1432      mParams(aParams.get_SDBRequestSeekParams()) {
   1433  MOZ_ASSERT(aParams.type() == SDBRequestParams::TSDBRequestSeekParams);
   1434 }
   1435 
   1436 nsresult SeekOp::DoDatabaseWork(
   1437    nsIFileRandomAccessStream* aFileRandomAccessStream) {
   1438  AssertIsOnIOThread();
   1439  MOZ_ASSERT(aFileRandomAccessStream);
   1440 
   1441  nsresult rv = aFileRandomAccessStream->Seek(nsISeekableStream::NS_SEEK_SET,
   1442                                              mParams.offset());
   1443 
   1444  if (NS_WARN_IF(NS_FAILED(rv))) {
   1445    return rv;
   1446  }
   1447 
   1448  return NS_OK;
   1449 }
   1450 
   1451 void SeekOp::GetResponse(SDBRequestResponse& aResponse) {
   1452  aResponse = SDBRequestSeekResponse();
   1453 }
   1454 
   1455 ReadOp::ReadOp(Connection* aConnection, const SDBRequestParams& aParams)
   1456    : ConnectionOperationBase(aConnection),
   1457      mParams(aParams.get_SDBRequestReadParams()) {
   1458  MOZ_ASSERT(aParams.type() == SDBRequestParams::TSDBRequestReadParams);
   1459 }
   1460 
   1461 bool ReadOp::Init() {
   1462  AssertIsOnOwningThread();
   1463 
   1464  if (NS_WARN_IF(!ConnectionOperationBase::Init())) {
   1465    return false;
   1466  }
   1467 
   1468  if (NS_WARN_IF(mParams.size() > std::numeric_limits<std::size_t>::max())) {
   1469    return false;
   1470  }
   1471 
   1472  mOutputStream = FixedBufferOutputStream::Create(mParams.size(), fallible);
   1473  if (NS_WARN_IF(!mOutputStream)) {
   1474    return false;
   1475  }
   1476 
   1477  return true;
   1478 }
   1479 
   1480 nsresult ReadOp::DoDatabaseWork(
   1481    nsIFileRandomAccessStream* aFileRandomAccessStream) {
   1482  AssertIsOnIOThread();
   1483  MOZ_ASSERT(aFileRandomAccessStream);
   1484 
   1485  nsCOMPtr<nsIInputStream> inputStream =
   1486      do_QueryInterface(aFileRandomAccessStream);
   1487  MOZ_ASSERT(inputStream);
   1488 
   1489  nsresult rv;
   1490 
   1491  uint64_t offset = 0;
   1492 
   1493  do {
   1494    char copyBuffer[kCopyBufferSize];
   1495 
   1496    uint64_t max = mParams.size() - offset;
   1497    if (max == 0) {
   1498      break;
   1499    }
   1500 
   1501    uint32_t count = sizeof(copyBuffer);
   1502    if (count > max) {
   1503      count = max;
   1504    }
   1505 
   1506    uint32_t numRead;
   1507    rv = inputStream->Read(copyBuffer, count, &numRead);
   1508    if (NS_WARN_IF(NS_FAILED(rv))) {
   1509      return rv;
   1510    }
   1511 
   1512    if (!numRead) {
   1513      break;
   1514    }
   1515 
   1516    uint32_t numWrite;
   1517    rv = mOutputStream->Write(copyBuffer, numRead, &numWrite);
   1518    if (NS_WARN_IF(NS_FAILED(rv))) {
   1519      return rv;
   1520    }
   1521 
   1522    if (NS_WARN_IF(numWrite != numRead)) {
   1523      return NS_ERROR_FAILURE;
   1524    }
   1525 
   1526    offset += numWrite;
   1527  } while (true);
   1528 
   1529  MOZ_ASSERT(offset == mParams.size());
   1530 
   1531  MOZ_ALWAYS_SUCCEEDS(mOutputStream->Close());
   1532 
   1533  return NS_OK;
   1534 }
   1535 
   1536 void ReadOp::GetResponse(SDBRequestResponse& aResponse) {
   1537  aResponse = SDBRequestReadResponse(nsCString(mOutputStream->WrittenData()));
   1538 }
   1539 
   1540 WriteOp::WriteOp(Connection* aConnection, const SDBRequestParams& aParams)
   1541    : ConnectionOperationBase(aConnection),
   1542      mParams(aParams.get_SDBRequestWriteParams()),
   1543      mSize(0) {
   1544  MOZ_ASSERT(aParams.type() == SDBRequestParams::TSDBRequestWriteParams);
   1545 }
   1546 
   1547 bool WriteOp::Init() {
   1548  AssertIsOnOwningThread();
   1549 
   1550  if (NS_WARN_IF(!ConnectionOperationBase::Init())) {
   1551    return false;
   1552  }
   1553 
   1554  const nsCString& string = mParams.data();
   1555 
   1556  nsCOMPtr<nsIInputStream> inputStream;
   1557  nsresult rv = NS_NewCStringInputStream(getter_AddRefs(inputStream), string);
   1558  if (NS_WARN_IF(NS_FAILED(rv))) {
   1559    return false;
   1560  }
   1561 
   1562  mInputStream = std::move(inputStream);
   1563  mSize = string.Length();
   1564 
   1565  return true;
   1566 }
   1567 
   1568 nsresult WriteOp::DoDatabaseWork(
   1569    nsIFileRandomAccessStream* aFileRandomAccessStream) {
   1570  AssertIsOnIOThread();
   1571  MOZ_ASSERT(aFileRandomAccessStream);
   1572 
   1573  nsCOMPtr<nsIOutputStream> outputStream =
   1574      do_QueryInterface(aFileRandomAccessStream);
   1575  MOZ_ASSERT(outputStream);
   1576 
   1577  nsresult rv;
   1578 
   1579  do {
   1580    char copyBuffer[kCopyBufferSize];
   1581 
   1582    uint32_t numRead;
   1583    rv = mInputStream->Read(copyBuffer, sizeof(copyBuffer), &numRead);
   1584    if (NS_WARN_IF(NS_FAILED(rv))) {
   1585      break;
   1586    }
   1587 
   1588    if (!numRead) {
   1589      break;
   1590    }
   1591 
   1592    uint32_t numWrite;
   1593    rv = outputStream->Write(copyBuffer, numRead, &numWrite);
   1594    if (NS_WARN_IF(NS_FAILED(rv))) {
   1595      return rv;
   1596    }
   1597 
   1598    if (NS_WARN_IF(numWrite != numRead)) {
   1599      return NS_ERROR_FAILURE;
   1600    }
   1601  } while (true);
   1602 
   1603  MOZ_ALWAYS_SUCCEEDS(mInputStream->Close());
   1604 
   1605  return NS_OK;
   1606 }
   1607 
   1608 void WriteOp::GetResponse(SDBRequestResponse& aResponse) {
   1609  aResponse = SDBRequestWriteResponse();
   1610 }
   1611 
   1612 CloseOp::CloseOp(Connection* aConnection)
   1613    : ConnectionOperationBase(aConnection) {}
   1614 
   1615 nsresult CloseOp::DoDatabaseWork(
   1616    nsIFileRandomAccessStream* aFileRandomAccessStream) {
   1617  AssertIsOnIOThread();
   1618  MOZ_ASSERT(aFileRandomAccessStream);
   1619 
   1620  nsCOMPtr<nsIInputStream> inputStream =
   1621      do_QueryInterface(aFileRandomAccessStream);
   1622  MOZ_ASSERT(inputStream);
   1623 
   1624  nsresult rv = inputStream->Close();
   1625  if (NS_WARN_IF(NS_FAILED(rv))) {
   1626    return rv;
   1627  }
   1628 
   1629  return NS_OK;
   1630 }
   1631 
   1632 void CloseOp::GetResponse(SDBRequestResponse& aResponse) {
   1633  aResponse = SDBRequestCloseResponse();
   1634 }
   1635 
   1636 void CloseOp::OnSuccess() {
   1637  AssertIsOnOwningThread();
   1638 
   1639  GetConnection()->OnClose();
   1640 }
   1641 
   1642 /*******************************************************************************
   1643 * QuotaClient
   1644 ******************************************************************************/
   1645 
   1646 QuotaClient* QuotaClient::sInstance = nullptr;
   1647 
   1648 QuotaClient::QuotaClient() {
   1649  AssertIsOnBackgroundThread();
   1650  MOZ_ASSERT(!sInstance, "We expect this to be a singleton!");
   1651 
   1652  sInstance = this;
   1653 }
   1654 
   1655 QuotaClient::~QuotaClient() {
   1656  AssertIsOnBackgroundThread();
   1657  MOZ_ASSERT(sInstance == this, "We expect this to be a singleton!");
   1658 
   1659  sInstance = nullptr;
   1660 }
   1661 
   1662 mozilla::dom::quota::Client::Type QuotaClient::GetType() {
   1663  return QuotaClient::SDB;
   1664 }
   1665 
   1666 Result<UsageInfo, nsresult> QuotaClient::InitOrigin(
   1667    PersistenceType aPersistenceType, const OriginMetadata& aOriginMetadata,
   1668    const AtomicBool& aCanceled) {
   1669  AssertIsOnIOThread();
   1670 
   1671  return GetUsageForOrigin(aPersistenceType, aOriginMetadata, aCanceled);
   1672 }
   1673 
   1674 nsresult QuotaClient::InitOriginWithoutTracking(
   1675    PersistenceType aPersistenceType, const OriginMetadata& aOriginMetadata,
   1676    const AtomicBool& aCanceled) {
   1677  AssertIsOnIOThread();
   1678 
   1679  return NS_OK;
   1680 }
   1681 
   1682 Result<UsageInfo, nsresult> QuotaClient::GetUsageForOrigin(
   1683    PersistenceType aPersistenceType, const OriginMetadata& aOriginMetadata,
   1684    const AtomicBool& aCanceled) {
   1685  AssertIsOnIOThread();
   1686  MOZ_ASSERT(aOriginMetadata.mPersistenceType == aPersistenceType);
   1687 
   1688  QuotaManager* quotaManager = QuotaManager::Get();
   1689  MOZ_ASSERT(quotaManager);
   1690 
   1691  QM_TRY_UNWRAP(auto directory,
   1692                quotaManager->GetOriginDirectory(aOriginMetadata));
   1693 
   1694  MOZ_ASSERT(directory);
   1695 
   1696  nsresult rv =
   1697      directory->Append(NS_LITERAL_STRING_FROM_CSTRING(SDB_DIRECTORY_NAME));
   1698  if (NS_WARN_IF(NS_FAILED(rv))) {
   1699    return Err(rv);
   1700  }
   1701 
   1702  DebugOnly<bool> exists;
   1703  MOZ_ASSERT(NS_SUCCEEDED(directory->Exists(&exists)) && exists);
   1704 
   1705  QM_TRY_RETURN(ReduceEachFileAtomicCancelable(
   1706      *directory, aCanceled, UsageInfo{},
   1707      [](UsageInfo usageInfo,
   1708         const nsCOMPtr<nsIFile>& file) -> Result<UsageInfo, nsresult> {
   1709        QM_TRY_INSPECT(const bool& isDirectory,
   1710                       MOZ_TO_RESULT_INVOKE_MEMBER(file, IsDirectory));
   1711 
   1712        if (isDirectory) {
   1713          (void)WARN_IF_FILE_IS_UNKNOWN(*file);
   1714          return usageInfo;
   1715        }
   1716 
   1717        nsString leafName;
   1718        QM_TRY(MOZ_TO_RESULT(file->GetLeafName(leafName)));
   1719 
   1720        if (StringEndsWith(leafName, kSDBSuffix)) {
   1721          QM_TRY_INSPECT(const int64_t& fileSize,
   1722                         MOZ_TO_RESULT_INVOKE_MEMBER(file, GetFileSize));
   1723 
   1724          MOZ_ASSERT(fileSize >= 0);
   1725 
   1726          return usageInfo +
   1727                 UsageInfo{DatabaseUsageType(Some(uint64_t(fileSize)))};
   1728        }
   1729 
   1730        (void)WARN_IF_FILE_IS_UNKNOWN(*file);
   1731 
   1732        return usageInfo;
   1733      }));
   1734 }
   1735 
   1736 void QuotaClient::OnOriginClearCompleted(
   1737    const OriginMetadata& aOriginMetadata) {
   1738  AssertIsOnIOThread();
   1739 }
   1740 
   1741 void QuotaClient::OnRepositoryClearCompleted(PersistenceType aPersistenceType) {
   1742  AssertIsOnIOThread();
   1743 }
   1744 
   1745 void QuotaClient::ReleaseIOThreadObjects() { AssertIsOnIOThread(); }
   1746 
   1747 void QuotaClient::AbortOperationsForLocks(
   1748    const DirectoryLockIdTable& aDirectoryLockIds) {
   1749  AssertIsOnBackgroundThread();
   1750 
   1751  AllowToCloseConnectionsMatching([&aDirectoryLockIds](const auto& connection) {
   1752    // If the connections is registered in gOpenConnections then it must have
   1753    // a directory lock.
   1754    return IsLockForObjectContainedInLockTable(connection, aDirectoryLockIds);
   1755  });
   1756 }
   1757 
   1758 void QuotaClient::AbortOperationsForProcess(ContentParentId aContentParentId) {
   1759  AssertIsOnBackgroundThread();
   1760 }
   1761 
   1762 void QuotaClient::AbortAllOperations() {
   1763  AssertIsOnBackgroundThread();
   1764 
   1765  AllowToCloseConnectionsMatching([](const auto&) { return true; });
   1766 }
   1767 
   1768 void QuotaClient::StartIdleMaintenance() { AssertIsOnBackgroundThread(); }
   1769 
   1770 void QuotaClient::StopIdleMaintenance() { AssertIsOnBackgroundThread(); }
   1771 
   1772 void QuotaClient::InitiateShutdown() {
   1773  AssertIsOnBackgroundThread();
   1774 
   1775  if (gOpenConnections) {
   1776    for (const auto& connection : *gOpenConnections) {
   1777      connection->AllowToClose();
   1778    }
   1779  }
   1780 }
   1781 
   1782 bool QuotaClient::IsShutdownCompleted() const { return !gOpenConnections; }
   1783 
   1784 void QuotaClient::ForceKillActors() {
   1785  // Currently we don't implement killing actors (are there any to kill here?).
   1786 }
   1787 
   1788 nsCString QuotaClient::GetShutdownStatus() const {
   1789  // XXX Gather information here.
   1790  return "To be implemented"_ns;
   1791 }
   1792 
   1793 void QuotaClient::FinalizeShutdown() {
   1794  // Nothing to do here.
   1795 }
   1796 
   1797 }  // namespace mozilla::dom