tor-browser

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

FileSystemSyncAccessHandle.cpp (22752B)


      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 "FileSystemSyncAccessHandle.h"
      8 
      9 #include "fs/FileSystemAsyncCopy.h"
     10 #include "fs/FileSystemRequestHandler.h"
     11 #include "mozilla/CheckedInt.h"
     12 #include "mozilla/ErrorResult.h"
     13 #include "mozilla/FixedBufferOutputStream.h"
     14 #include "mozilla/MozPromise.h"
     15 #include "mozilla/TaskQueue.h"
     16 #include "mozilla/dom/BindingDeclarations.h"
     17 #include "mozilla/dom/BufferSourceBinding.h"
     18 #include "mozilla/dom/FileSystemAccessHandleChild.h"
     19 #include "mozilla/dom/FileSystemAccessHandleControlChild.h"
     20 #include "mozilla/dom/FileSystemHandleBinding.h"
     21 #include "mozilla/dom/FileSystemLog.h"
     22 #include "mozilla/dom/FileSystemManager.h"
     23 #include "mozilla/dom/FileSystemManagerChild.h"
     24 #include "mozilla/dom/FileSystemSyncAccessHandleBinding.h"
     25 #include "mozilla/dom/Promise.h"
     26 #include "mozilla/dom/UnionTypes.h"
     27 #include "mozilla/dom/WorkerCommon.h"
     28 #include "mozilla/dom/WorkerPrivate.h"
     29 #include "mozilla/dom/WorkerRef.h"
     30 #include "mozilla/dom/fs/IPCRejectReporter.h"
     31 #include "mozilla/dom/quota/QuotaCommon.h"
     32 #include "mozilla/dom/quota/ResultExtensions.h"
     33 #include "mozilla/dom/quota/TargetPtrHolder.h"
     34 #include "mozilla/ipc/RandomAccessStreamUtils.h"
     35 #include "nsNetCID.h"
     36 #include "nsStringStream.h"
     37 
     38 namespace mozilla::dom {
     39 
     40 namespace {
     41 
     42 using SizePromise = Int64Promise;
     43 const auto CreateAndRejectSizePromise = CreateAndRejectInt64Promise;
     44 
     45 }  // namespace
     46 
     47 FileSystemSyncAccessHandle::FileSystemSyncAccessHandle(
     48    nsIGlobalObject* aGlobal, RefPtr<FileSystemManager>& aManager,
     49    mozilla::ipc::RandomAccessStreamParams&& aStreamParams,
     50    RefPtr<FileSystemAccessHandleChild> aActor,
     51    RefPtr<FileSystemAccessHandleControlChild> aControlActor,
     52    RefPtr<TaskQueue> aIOTaskQueue,
     53    const fs::FileSystemEntryMetadata& aMetadata)
     54    : mGlobal(aGlobal),
     55      mManager(aManager),
     56      mActor(std::move(aActor)),
     57      mControlActor(std::move(aControlActor)),
     58      mIOTaskQueue(std::move(aIOTaskQueue)),
     59      mStreamParams(std::move(aStreamParams)),
     60      mMetadata(aMetadata),
     61      mState(State::Initial) {
     62  LOG(("Created SyncAccessHandle %p", this));
     63 
     64  // Connect with the actor directly in the constructor. This way the actor
     65  // can call `FileSystemSyncAccessHandle::ClearActor` when we call
     66  // `PFileSystemAccessHandleChild::Send__delete__` even when
     67  // FileSystemSyncAccessHandle::Create fails, in which case the not yet
     68  // fully constructed FileSystemSyncAccessHandle is being destroyed.
     69  mActor->SetAccessHandle(this);
     70 
     71  mControlActor->SetAccessHandle(this);
     72 }
     73 
     74 FileSystemSyncAccessHandle::~FileSystemSyncAccessHandle() {
     75  MOZ_ASSERT(!mActor);
     76  MOZ_ASSERT(IsClosed());
     77 }
     78 
     79 // static
     80 Result<RefPtr<FileSystemSyncAccessHandle>, nsresult>
     81 FileSystemSyncAccessHandle::Create(
     82    nsIGlobalObject* aGlobal, RefPtr<FileSystemManager>& aManager,
     83    mozilla::ipc::RandomAccessStreamParams&& aStreamParams,
     84    mozilla::ipc::ManagedEndpoint<PFileSystemAccessHandleChild>&&
     85        aAccessHandleChildEndpoint,
     86    mozilla::ipc::Endpoint<PFileSystemAccessHandleControlChild>&&
     87        aAccessHandleControlChildEndpoint,
     88    const fs::FileSystemEntryMetadata& aMetadata) {
     89  WorkerPrivate* const workerPrivate = GetCurrentThreadWorkerPrivate();
     90  MOZ_ASSERT(workerPrivate);
     91 
     92  auto accessHandleChild = MakeRefPtr<FileSystemAccessHandleChild>();
     93 
     94  QM_TRY(MOZ_TO_RESULT(
     95      aManager->ActorStrongRef()->BindPFileSystemAccessHandleEndpoint(
     96          std::move(aAccessHandleChildEndpoint), accessHandleChild)));
     97 
     98  auto accessHandleControlChild =
     99      MakeRefPtr<FileSystemAccessHandleControlChild>();
    100 
    101  aAccessHandleControlChildEndpoint.Bind(accessHandleControlChild,
    102                                         workerPrivate->ControlEventTarget());
    103 
    104  QM_TRY_UNWRAP(auto streamTransportService,
    105                MOZ_TO_RESULT_GET_TYPED(nsCOMPtr<nsIEventTarget>,
    106                                        MOZ_SELECT_OVERLOAD(do_GetService),
    107                                        NS_STREAMTRANSPORTSERVICE_CONTRACTID));
    108 
    109  RefPtr<TaskQueue> ioTaskQueue = TaskQueue::Create(
    110      streamTransportService.forget(), "FileSystemSyncAccessHandle");
    111  QM_TRY(MOZ_TO_RESULT(ioTaskQueue));
    112 
    113  RefPtr<FileSystemSyncAccessHandle> result = new FileSystemSyncAccessHandle(
    114      aGlobal, aManager, std::move(aStreamParams), std::move(accessHandleChild),
    115      std::move(accessHandleControlChild), std::move(ioTaskQueue), aMetadata);
    116 
    117  auto autoClose = MakeScopeExit([result] {
    118    MOZ_ASSERT(result->mState == State::Initial);
    119    result->mState = State::Closed;
    120    result->mActor->SendClose();
    121  });
    122 
    123  workerPrivate->AssertIsOnWorkerThread();
    124 
    125  RefPtr<StrongWorkerRef> workerRef = StrongWorkerRef::Create(
    126      workerPrivate, "FileSystemSyncAccessHandle", [result]() {
    127        if (result->IsOpen()) {
    128          // We don't need to use the result, we just need to begin the closing
    129          // process.
    130          (void)result->BeginClose();
    131        }
    132      });
    133  QM_TRY(MOZ_TO_RESULT(workerRef));
    134 
    135  autoClose.release();
    136 
    137  result->mWorkerRef = std::move(workerRef);
    138  result->mState = State::Open;
    139 
    140  return result;
    141 }
    142 
    143 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(FileSystemSyncAccessHandle)
    144  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
    145  NS_INTERFACE_MAP_ENTRY(nsISupports)
    146 NS_INTERFACE_MAP_END
    147 
    148 NS_IMPL_CYCLE_COLLECTING_ADDREF(FileSystemSyncAccessHandle)
    149 NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_LAST_RELEASE(FileSystemSyncAccessHandle,
    150                                                   LastRelease())
    151 
    152 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(FileSystemSyncAccessHandle)
    153 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(FileSystemSyncAccessHandle)
    154  NS_IMPL_CYCLE_COLLECTION_UNLINK(mGlobal)
    155  // Don't unlink mManager!
    156  NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
    157  if (tmp->IsOpen()) {
    158    // We don't need to use the result, we just need to begin the closing
    159    // process.
    160    (void)tmp->BeginClose();
    161  }
    162 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
    163 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(FileSystemSyncAccessHandle)
    164  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGlobal)
    165  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mManager)
    166 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
    167 
    168 void FileSystemSyncAccessHandle::LastRelease() {
    169  // We can't call `FileSystemSyncAccessHandle::Close` here because it may need
    170  // to keep FileSystemSyncAccessHandle object alive which isn't possible when
    171  // the object is about to be deleted. There are other mechanisms which ensure
    172  // that the object is correctly closed before destruction. For example the
    173  // object unlinking and the worker shutdown (we get notified about it via the
    174  // callback passed to `StrongWorkerRef`) are used to close the object if it
    175  // hasn't been closed yet.
    176 
    177  if (mActor) {
    178    PFileSystemAccessHandleChild::Send__delete__(mActor);
    179 
    180    // `PFileSystemAccessHandleChild::Send__delete__` is supposed to call
    181    // `FileSystemAccessHandleChild::ActorDestroy` which in turn calls
    182    // `FileSystemSyncAccessHandle::ClearActor`, so `mActor` should be be null
    183    // at this point.
    184    MOZ_ASSERT(!mActor);
    185  }
    186 
    187  if (mControlActor) {
    188    mControlActor->Close();
    189 
    190    // `FileSystemAccessHandleControlChild::Close` is supposed to call
    191    // `FileSystemAccessHandleControlChild::ActorDestroy` which in turn calls
    192    // `FileSystemSyncAccessHandle::ClearControlActor`, so `mControlActor`
    193    // should be be null at this point.
    194    MOZ_ASSERT(!mControlActor);
    195  }
    196 }
    197 
    198 void FileSystemSyncAccessHandle::ClearActor() {
    199  MOZ_ASSERT(mActor);
    200 
    201  mActor = nullptr;
    202 }
    203 
    204 void FileSystemSyncAccessHandle::ClearControlActor() {
    205  // `mControlActor` is initialized in the constructor and this method is
    206  // supposed to be called only once.
    207  MOZ_ASSERT(mControlActor);
    208 
    209  mControlActor = nullptr;
    210 }
    211 
    212 bool FileSystemSyncAccessHandle::IsOpen() const {
    213  MOZ_ASSERT(mState != State::Initial);
    214 
    215  return mState == State::Open;
    216 }
    217 
    218 bool FileSystemSyncAccessHandle::IsClosing() const {
    219  MOZ_ASSERT(mState != State::Initial);
    220 
    221  return mState == State::Closing;
    222 }
    223 
    224 bool FileSystemSyncAccessHandle::IsClosed() const {
    225  MOZ_ASSERT(mState != State::Initial);
    226 
    227  return mState == State::Closed;
    228 }
    229 
    230 RefPtr<BoolPromise> FileSystemSyncAccessHandle::BeginClose() {
    231  MOZ_ASSERT(IsOpen());
    232 
    233  mState = State::Closing;
    234 
    235  InvokeAsync(mIOTaskQueue, __func__,
    236              [selfHolder = quota::TargetPtrHolder(this)]() {
    237                if (selfHolder->mStream) {
    238                  LOG(("%p: Closing", selfHolder->mStream.get()));
    239 
    240                  selfHolder->mStream->OutputStream()->Close();
    241                  selfHolder->mStream = nullptr;
    242                } else {
    243                  LOG(("Closing (no stream)"));
    244 
    245                  // If the stream was not deserialized, `mStreamParams` still
    246                  // contains a pre-opened file descriptor which needs to be
    247                  // closed here by moving `mStreamParams` to a local variable
    248                  // (the file descriptor will be closed for real when
    249                  // `streamParams` goes out of scope).
    250 
    251                  mozilla::ipc::RandomAccessStreamParams streamParams(
    252                      std::move(selfHolder->mStreamParams));
    253                }
    254 
    255                return BoolPromise::CreateAndResolve(true, __func__);
    256              })
    257      ->Then(mWorkerRef->Private()->ControlEventTarget(), __func__,
    258             [self = RefPtr(this)](const BoolPromise::ResolveOrRejectValue&) {
    259               return self->mIOTaskQueue->BeginShutdown();
    260             })
    261      ->Then(
    262          mWorkerRef->Private()->ControlEventTarget(), __func__,
    263          [self = RefPtr(this)](const ShutdownPromise::ResolveOrRejectValue&) {
    264            if (self->mControlActor) {
    265              RefPtr<BoolPromise::Private> promise =
    266                  new BoolPromise::Private(__func__);
    267 
    268              self->mControlActor->SendClose(
    269                  [promise](void_t&&) { promise->Resolve(true, __func__); },
    270                  [promise](const mozilla::ipc::ResponseRejectReason& aReason) {
    271                    fs::IPCRejectReporter(aReason);
    272 
    273                    promise->Reject(NS_ERROR_FAILURE, __func__);
    274                  });
    275 
    276              return RefPtr<BoolPromise>(promise);
    277            }
    278 
    279            return BoolPromise::CreateAndResolve(true, __func__);
    280          })
    281      ->Then(mWorkerRef->Private()->ControlEventTarget(), __func__,
    282             [self = RefPtr(this)](const BoolPromise::ResolveOrRejectValue&) {
    283               self->mWorkerRef = nullptr;
    284 
    285               self->mState = State::Closed;
    286 
    287               self->mClosePromiseHolder.ResolveIfExists(true, __func__);
    288             });
    289 
    290  return OnClose();
    291 }
    292 
    293 RefPtr<BoolPromise> FileSystemSyncAccessHandle::OnClose() {
    294  MOZ_ASSERT(mState == State::Closing);
    295 
    296  return mClosePromiseHolder.Ensure(__func__);
    297 }
    298 
    299 // WebIDL Boilerplate
    300 
    301 nsIGlobalObject* FileSystemSyncAccessHandle::GetParentObject() const {
    302  return mGlobal;
    303 }
    304 
    305 JSObject* FileSystemSyncAccessHandle::WrapObject(
    306    JSContext* aCx, JS::Handle<JSObject*> aGivenProto) {
    307  return FileSystemSyncAccessHandle_Binding::Wrap(aCx, this, aGivenProto);
    308 }
    309 
    310 // WebIDL Interface
    311 
    312 uint64_t FileSystemSyncAccessHandle::Read(
    313    const AllowSharedBufferSource& aBuffer,
    314    const FileSystemReadWriteOptions& aOptions, ErrorResult& aRv) {
    315  return ReadOrWrite(aBuffer, aOptions, /* aRead */ true, aRv);
    316 }
    317 
    318 uint64_t FileSystemSyncAccessHandle::Write(
    319    const AllowSharedBufferSource& aBuffer,
    320    const FileSystemReadWriteOptions& aOptions, ErrorResult& aRv) {
    321  return ReadOrWrite(aBuffer, aOptions, /* aRead */ false, aRv);
    322 }
    323 
    324 void FileSystemSyncAccessHandle::Truncate(uint64_t aSize, ErrorResult& aError) {
    325  if (!IsOpen()) {
    326    aError.ThrowInvalidStateError("SyncAccessHandle is closed");
    327    return;
    328  }
    329 
    330  MOZ_ASSERT(mWorkerRef);
    331 
    332  AutoSyncLoopHolder syncLoop(mWorkerRef->Private(), Canceling);
    333 
    334  nsCOMPtr<nsISerialEventTarget> syncLoopTarget =
    335      syncLoop.GetSerialEventTarget();
    336  QM_TRY(MOZ_TO_RESULT(syncLoopTarget), [&aError](nsresult) {
    337    aError.ThrowInvalidStateError("Worker is shutting down");
    338  });
    339 
    340  InvokeAsync(
    341      mIOTaskQueue, __func__,
    342      [selfHolder = quota::TargetPtrHolder(this), aSize]() {
    343        QM_TRY(MOZ_TO_RESULT(selfHolder->EnsureStream()),
    344               CreateAndRejectBoolPromise);
    345 
    346        LOG(("%p: Truncate to %" PRIu64, selfHolder->mStream.get(), aSize));
    347        int64_t offset = 0;
    348        QM_TRY(MOZ_TO_RESULT(selfHolder->mStream->Tell(&offset)),
    349               CreateAndRejectBoolPromise);
    350        QM_TRY(MOZ_TO_RESULT(selfHolder->mStream->Seek(
    351                   nsISeekableStream::NS_SEEK_SET, aSize)),
    352               CreateAndRejectBoolPromise);
    353 
    354        QM_TRY(MOZ_TO_RESULT(selfHolder->mStream->SetEOF()),
    355               CreateAndRejectBoolPromise);
    356        // restore cursor position (clamp to end of file)
    357        QM_TRY(MOZ_TO_RESULT(selfHolder->mStream->Seek(
    358                   nsISeekableStream::NS_SEEK_SET,
    359                   std::min((uint64_t)offset, aSize))),
    360               CreateAndRejectBoolPromise);
    361 
    362        return BoolPromise::CreateAndResolve(true, __func__);
    363      })
    364      ->Then(syncLoopTarget, __func__,
    365             [this, &syncLoopTarget](
    366                 const BoolPromise::ResolveOrRejectValue& aValue) {
    367               MOZ_ASSERT(mWorkerRef);
    368 
    369               mWorkerRef->Private()->AssertIsOnWorkerThread();
    370 
    371               mWorkerRef->Private()->StopSyncLoop(
    372                   syncLoopTarget,
    373                   aValue.IsResolve() ? NS_OK : aValue.RejectValue());
    374             });
    375 
    376  QM_TRY(MOZ_TO_RESULT(syncLoop.Run()),
    377         [&aError](const nsresult rv) { aError.Throw(rv); });
    378 }
    379 
    380 uint64_t FileSystemSyncAccessHandle::GetSize(ErrorResult& aError) {
    381  if (!IsOpen()) {
    382    aError.ThrowInvalidStateError("SyncAccessHandle is closed");
    383    return 0;
    384  }
    385 
    386  MOZ_ASSERT(mWorkerRef);
    387 
    388  AutoSyncLoopHolder syncLoop(mWorkerRef->Private(), Canceling);
    389 
    390  nsCOMPtr<nsISerialEventTarget> syncLoopTarget =
    391      syncLoop.GetSerialEventTarget();
    392  QM_TRY(MOZ_TO_RESULT(syncLoopTarget), [&aError](nsresult) {
    393    aError.ThrowInvalidStateError("Worker is shutting down");
    394    return 0;
    395  });
    396 
    397  // XXX Could we somehow pass the size to `StopSyncLoop` and then get it via
    398  // `QM_TRY_INSPECT(const auto& size, syncLoop.Run)` ?
    399  // Could we use Result<UniquePtr<...>, nsresult> for that ?
    400  int64_t size;
    401 
    402  InvokeAsync(mIOTaskQueue, __func__,
    403              [selfHolder = quota::TargetPtrHolder(this)]() {
    404                QM_TRY(MOZ_TO_RESULT(selfHolder->EnsureStream()),
    405                       CreateAndRejectSizePromise);
    406 
    407                nsCOMPtr<nsIFileMetadata> fileMetadata =
    408                    do_QueryInterface(selfHolder->mStream);
    409                MOZ_ASSERT(fileMetadata);
    410 
    411                QM_TRY_INSPECT(
    412                    const auto& size,
    413                    MOZ_TO_RESULT_INVOKE_MEMBER(fileMetadata, GetSize),
    414                    CreateAndRejectSizePromise);
    415 
    416                LOG(("%p: GetSize %" PRIu64, selfHolder->mStream.get(), size));
    417 
    418                return SizePromise::CreateAndResolve(size, __func__);
    419              })
    420      ->Then(syncLoopTarget, __func__,
    421             [this, &syncLoopTarget,
    422              &size](const Int64Promise::ResolveOrRejectValue& aValue) {
    423               MOZ_ASSERT(mWorkerRef);
    424 
    425               mWorkerRef->Private()->AssertIsOnWorkerThread();
    426 
    427               if (aValue.IsResolve()) {
    428                 size = aValue.ResolveValue();
    429 
    430                 mWorkerRef->Private()->StopSyncLoop(syncLoopTarget, NS_OK);
    431               } else {
    432                 mWorkerRef->Private()->StopSyncLoop(syncLoopTarget,
    433                                                     aValue.RejectValue());
    434               }
    435             });
    436 
    437  QM_TRY(MOZ_TO_RESULT(syncLoop.Run()), [&aError](const nsresult rv) {
    438    aError.Throw(rv);
    439    return 0;
    440  });
    441 
    442  return size;
    443 }
    444 
    445 void FileSystemSyncAccessHandle::Flush(ErrorResult& aError) {
    446  if (!IsOpen()) {
    447    aError.ThrowInvalidStateError("SyncAccessHandle is closed");
    448    return;
    449  }
    450 
    451  MOZ_ASSERT(mWorkerRef);
    452 
    453  AutoSyncLoopHolder syncLoop(mWorkerRef->Private(), Canceling);
    454 
    455  nsCOMPtr<nsISerialEventTarget> syncLoopTarget =
    456      syncLoop.GetSerialEventTarget();
    457  QM_TRY(MOZ_TO_RESULT(syncLoopTarget), [&aError](nsresult) {
    458    aError.ThrowInvalidStateError("Worker is shutting down");
    459  });
    460 
    461  InvokeAsync(mIOTaskQueue, __func__,
    462              [selfHolder = quota::TargetPtrHolder(this)]() {
    463                QM_TRY(MOZ_TO_RESULT(selfHolder->EnsureStream()),
    464                       CreateAndRejectBoolPromise);
    465 
    466                LOG(("%p: Flush", selfHolder->mStream.get()));
    467 
    468                QM_TRY(
    469                    MOZ_TO_RESULT(selfHolder->mStream->OutputStream()->Flush()),
    470                    CreateAndRejectBoolPromise);
    471 
    472                return BoolPromise::CreateAndResolve(true, __func__);
    473              })
    474      ->Then(syncLoopTarget, __func__,
    475             [this, &syncLoopTarget](
    476                 const BoolPromise::ResolveOrRejectValue& aValue) {
    477               MOZ_ASSERT(mWorkerRef);
    478 
    479               mWorkerRef->Private()->AssertIsOnWorkerThread();
    480 
    481               mWorkerRef->Private()->StopSyncLoop(
    482                   syncLoopTarget,
    483                   aValue.IsResolve() ? NS_OK : aValue.RejectValue());
    484             });
    485 
    486  QM_TRY(MOZ_TO_RESULT(syncLoop.Run()),
    487         [&aError](const nsresult rv) { aError.Throw(rv); });
    488 }
    489 
    490 void FileSystemSyncAccessHandle::Close() {
    491  if (!(IsOpen() || IsClosing())) {
    492    return;
    493  }
    494 
    495  MOZ_ASSERT(mWorkerRef);
    496 
    497  // Normally mWorkerRef can be used directly for stopping the sync loop, but
    498  // the async close is special because mWorkerRef is cleared as part of the
    499  // operation. That's why we need to use this extra strong ref to the
    500  // `StrongWorkerRef`.
    501  RefPtr<StrongWorkerRef> workerRef = mWorkerRef;
    502 
    503  AutoSyncLoopHolder syncLoop(workerRef->Private(), Killing);
    504 
    505  nsCOMPtr<nsISerialEventTarget> syncLoopTarget =
    506      syncLoop.GetSerialEventTarget();
    507  MOZ_ASSERT(syncLoopTarget);
    508 
    509  InvokeAsync(syncLoopTarget, __func__, [self = RefPtr(this)]() {
    510    if (self->IsOpen()) {
    511      return self->BeginClose();
    512    }
    513    return self->OnClose();
    514  })->Then(syncLoopTarget, __func__, [&workerRef, &syncLoopTarget]() {
    515    MOZ_ASSERT(workerRef);
    516 
    517    workerRef->Private()->AssertIsOnWorkerThread();
    518 
    519    workerRef->Private()->StopSyncLoop(syncLoopTarget, NS_OK);
    520  });
    521 
    522  MOZ_ALWAYS_SUCCEEDS(syncLoop.Run());
    523 }
    524 
    525 uint64_t FileSystemSyncAccessHandle::ReadOrWrite(
    526    const AllowSharedBufferSource& aBuffer,
    527    const FileSystemReadWriteOptions& aOptions, const bool aRead,
    528    ErrorResult& aRv) {
    529  if (!IsOpen()) {
    530    aRv.ThrowInvalidStateError("SyncAccessHandle is closed");
    531    return 0;
    532  }
    533 
    534  MOZ_ASSERT(mWorkerRef);
    535 
    536  auto throwAndReturn = [&aRv](const nsresult rv) {
    537    aRv.Throw(rv);
    538    return 0;
    539  };
    540 
    541  // Handle seek before read ('at')
    542  const auto at = [&aOptions]() -> uint64_t {
    543    if (aOptions.mAt.WasPassed()) {
    544      return aOptions.mAt.Value();
    545    }
    546    // Spec says default for at is 0 (2.6)
    547    return 0;
    548  }();
    549 
    550  const auto offset = CheckedInt<int64_t>(at);
    551  QM_TRY(MOZ_TO_RESULT(offset.isValid()), throwAndReturn);
    552 
    553  AutoSyncLoopHolder syncLoop(mWorkerRef->Private(), Canceling);
    554 
    555  nsCOMPtr<nsISerialEventTarget> syncLoopTarget =
    556      syncLoop.GetSerialEventTarget();
    557  QM_TRY(MOZ_TO_RESULT(syncLoopTarget), [&aRv](nsresult) {
    558    aRv.ThrowInvalidStateError("Worker is shutting down");
    559    return 0;
    560  });
    561 
    562  uint64_t totalCount = 0;
    563 
    564  ProcessTypedArraysFixed(aBuffer, [&](const Span<uint8_t> aData) {
    565    InvokeAsync(
    566        mIOTaskQueue, __func__,
    567        [selfHolder = quota::TargetPtrHolder(this), aData,
    568         use_offset = aOptions.mAt.WasPassed(), offset, aRead, syncLoopTarget,
    569         &totalCount]() {
    570          QM_TRY(MOZ_TO_RESULT(selfHolder->EnsureStream()),
    571                 CreateAndRejectBoolPromise);
    572          if (use_offset) {
    573            LOG_VERBOSE(("%p: Seeking to %" PRIu64, selfHolder->mStream.get(),
    574                         offset.value()));
    575 
    576            QM_TRY(MOZ_TO_RESULT(selfHolder->mStream->Seek(
    577                       nsISeekableStream::NS_SEEK_SET, offset.value())),
    578                   CreateAndRejectBoolPromise);
    579          }
    580 
    581          nsCOMPtr<nsIInputStream> inputStream;
    582          nsCOMPtr<nsIOutputStream> outputStream;
    583 
    584          if (aRead) {
    585            LOG_VERBOSE(("%p: Reading %zu bytes", selfHolder->mStream.get(),
    586                         aData.Length()));
    587 
    588            inputStream = selfHolder->mStream->InputStream();
    589            outputStream =
    590                FixedBufferOutputStream::Create(AsWritableChars(aData));
    591          } else {
    592            LOG_VERBOSE(("%p: Writing %zu bytes", selfHolder->mStream.get(),
    593                         aData.Length()));
    594 
    595            QM_TRY(MOZ_TO_RESULT(NS_NewByteInputStream(
    596                       getter_AddRefs(inputStream), AsChars(aData),
    597                       NS_ASSIGNMENT_DEPEND)),
    598                   CreateAndRejectBoolPromise);
    599 
    600            outputStream = selfHolder->mStream->OutputStream();
    601          }
    602 
    603          auto promiseHolder = MakeUnique<MozPromiseHolder<BoolPromise>>();
    604          RefPtr<BoolPromise> promise = promiseHolder->Ensure(__func__);
    605 
    606          QM_TRY(MOZ_TO_RESULT(fs::AsyncCopy(
    607                     inputStream, outputStream, GetCurrentSerialEventTarget(),
    608                     aRead ? NS_ASYNCCOPY_VIA_WRITESEGMENTS
    609                           : NS_ASYNCCOPY_VIA_READSEGMENTS,
    610                     /* aCloseSource */ !aRead, /* aCloseSink */ aRead,
    611                     [&totalCount](uint32_t count) { totalCount += count; },
    612                     [promiseHolder = std::move(promiseHolder)](nsresult rv) {
    613                       promiseHolder->ResolveIfExists(true, __func__);
    614                     })),
    615                 CreateAndRejectBoolPromise);
    616 
    617          return promise;
    618        })
    619        ->Then(syncLoopTarget, __func__,
    620               [this, &syncLoopTarget](
    621                   const BoolPromise::ResolveOrRejectValue& aValue) {
    622                 MOZ_ASSERT(mWorkerRef);
    623 
    624                 mWorkerRef->Private()->AssertIsOnWorkerThread();
    625 
    626                 mWorkerRef->Private()->StopSyncLoop(syncLoopTarget, NS_OK);
    627               });
    628 
    629    MOZ_ALWAYS_SUCCEEDS(syncLoop.Run());
    630  });
    631 
    632  return totalCount;
    633 }
    634 
    635 nsresult FileSystemSyncAccessHandle::EnsureStream() {
    636  if (!mStream) {
    637    QM_TRY_UNWRAP(mStream, DeserializeRandomAccessStream(mStreamParams),
    638                  NS_ERROR_FAILURE);
    639 
    640    mozilla::ipc::RandomAccessStreamParams streamParams(
    641        std::move(mStreamParams));
    642  }
    643 
    644  return NS_OK;
    645 }
    646 
    647 }  // namespace mozilla::dom