diff options
Diffstat (limited to '')
-rw-r--r-- | dom/fs/api/FileSystemSyncAccessHandle.cpp | 650 |
1 files changed, 650 insertions, 0 deletions
diff --git a/dom/fs/api/FileSystemSyncAccessHandle.cpp b/dom/fs/api/FileSystemSyncAccessHandle.cpp new file mode 100644 index 0000000000..c04f8e11a5 --- /dev/null +++ b/dom/fs/api/FileSystemSyncAccessHandle.cpp @@ -0,0 +1,650 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "FileSystemSyncAccessHandle.h" + +#include "fs/FileSystemAsyncCopy.h" +#include "fs/FileSystemRequestHandler.h" +#include "mozilla/CheckedInt.h" +#include "mozilla/ErrorResult.h" +#include "mozilla/FixedBufferOutputStream.h" +#include "mozilla/MozPromise.h" +#include "mozilla/TaskQueue.h" +#include "mozilla/dom/FileSystemAccessHandleChild.h" +#include "mozilla/dom/FileSystemAccessHandleControlChild.h" +#include "mozilla/dom/FileSystemHandleBinding.h" +#include "mozilla/dom/FileSystemLog.h" +#include "mozilla/dom/FileSystemManager.h" +#include "mozilla/dom/FileSystemManagerChild.h" +#include "mozilla/dom/FileSystemSyncAccessHandleBinding.h" +#include "mozilla/dom/Promise.h" +#include "mozilla/dom/UnionTypes.h" +#include "mozilla/dom/WorkerCommon.h" +#include "mozilla/dom/WorkerPrivate.h" +#include "mozilla/dom/WorkerRef.h" +#include "mozilla/dom/fs/IPCRejectReporter.h" +#include "mozilla/dom/fs/TargetPtrHolder.h" +#include "mozilla/dom/quota/QuotaCommon.h" +#include "mozilla/dom/quota/ResultExtensions.h" +#include "mozilla/ipc/RandomAccessStreamUtils.h" +#include "nsNetCID.h" +#include "nsStringStream.h" + +namespace mozilla::dom { + +namespace { + +using SizePromise = Int64Promise; +const auto CreateAndRejectSizePromise = CreateAndRejectInt64Promise; + +} // namespace + +FileSystemSyncAccessHandle::FileSystemSyncAccessHandle( + nsIGlobalObject* aGlobal, RefPtr<FileSystemManager>& aManager, + mozilla::ipc::RandomAccessStreamParams&& aStreamParams, + RefPtr<FileSystemAccessHandleChild> aActor, + RefPtr<FileSystemAccessHandleControlChild> aControlActor, + RefPtr<TaskQueue> aIOTaskQueue, + const fs::FileSystemEntryMetadata& aMetadata) + : mGlobal(aGlobal), + mManager(aManager), + mActor(std::move(aActor)), + mControlActor(std::move(aControlActor)), + mIOTaskQueue(std::move(aIOTaskQueue)), + mStreamParams(std::move(aStreamParams)), + mMetadata(aMetadata), + mState(State::Initial) { + LOG(("Created SyncAccessHandle %p", this)); + + // Connect with the actor directly in the constructor. This way the actor + // can call `FileSystemSyncAccessHandle::ClearActor` when we call + // `PFileSystemAccessHandleChild::Send__delete__` even when + // FileSystemSyncAccessHandle::Create fails, in which case the not yet + // fully constructed FileSystemSyncAccessHandle is being destroyed. + mActor->SetAccessHandle(this); + + mControlActor->SetAccessHandle(this); +} + +FileSystemSyncAccessHandle::~FileSystemSyncAccessHandle() { + MOZ_ASSERT(!mActor); + MOZ_ASSERT(IsClosed()); +} + +// static +Result<RefPtr<FileSystemSyncAccessHandle>, nsresult> +FileSystemSyncAccessHandle::Create( + nsIGlobalObject* aGlobal, RefPtr<FileSystemManager>& aManager, + mozilla::ipc::RandomAccessStreamParams&& aStreamParams, + mozilla::ipc::ManagedEndpoint<PFileSystemAccessHandleChild>&& + aAccessHandleChildEndpoint, + mozilla::ipc::Endpoint<PFileSystemAccessHandleControlChild>&& + aAccessHandleControlChildEndpoint, + const fs::FileSystemEntryMetadata& aMetadata) { + WorkerPrivate* const workerPrivate = GetCurrentThreadWorkerPrivate(); + MOZ_ASSERT(workerPrivate); + + auto accessHandleChild = MakeRefPtr<FileSystemAccessHandleChild>(); + + QM_TRY(MOZ_TO_RESULT( + aManager->ActorStrongRef()->BindPFileSystemAccessHandleEndpoint( + std::move(aAccessHandleChildEndpoint), accessHandleChild))); + + auto accessHandleControlChild = + MakeRefPtr<FileSystemAccessHandleControlChild>(); + + aAccessHandleControlChildEndpoint.Bind(accessHandleControlChild, + workerPrivate->ControlEventTarget()); + + QM_TRY_UNWRAP(auto streamTransportService, + MOZ_TO_RESULT_GET_TYPED(nsCOMPtr<nsIEventTarget>, + MOZ_SELECT_OVERLOAD(do_GetService), + NS_STREAMTRANSPORTSERVICE_CONTRACTID)); + + RefPtr<TaskQueue> ioTaskQueue = TaskQueue::Create( + streamTransportService.forget(), "FileSystemSyncAccessHandle"); + QM_TRY(MOZ_TO_RESULT(ioTaskQueue)); + + RefPtr<FileSystemSyncAccessHandle> result = new FileSystemSyncAccessHandle( + aGlobal, aManager, std::move(aStreamParams), std::move(accessHandleChild), + std::move(accessHandleControlChild), std::move(ioTaskQueue), aMetadata); + + auto autoClose = MakeScopeExit([result] { + MOZ_ASSERT(result->mState == State::Initial); + result->mState = State::Closed; + result->mActor->SendClose(); + }); + + workerPrivate->AssertIsOnWorkerThread(); + + RefPtr<StrongWorkerRef> workerRef = StrongWorkerRef::Create( + workerPrivate, "FileSystemSyncAccessHandle", [result]() { + if (result->IsOpen()) { + // We don't need to use the result, we just need to begin the closing + // process. + Unused << result->BeginClose(); + } + }); + QM_TRY(MOZ_TO_RESULT(workerRef)); + + autoClose.release(); + + result->mWorkerRef = std::move(workerRef); + result->mState = State::Open; + + return result; +} + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(FileSystemSyncAccessHandle) + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END + +NS_IMPL_CYCLE_COLLECTING_ADDREF(FileSystemSyncAccessHandle) +NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_LAST_RELEASE(FileSystemSyncAccessHandle, + LastRelease()) + +NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(FileSystemSyncAccessHandle) +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(FileSystemSyncAccessHandle) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mGlobal) + // Don't unlink mManager! + NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER + if (tmp->IsOpen()) { + // We don't need to use the result, we just need to begin the closing + // process. + Unused << tmp->BeginClose(); + } +NS_IMPL_CYCLE_COLLECTION_UNLINK_END +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(FileSystemSyncAccessHandle) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGlobal) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mManager) +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END + +void FileSystemSyncAccessHandle::LastRelease() { + // We can't call `FileSystemSyncAccessHandle::Close` here because it may need + // to keep FileSystemSyncAccessHandle object alive which isn't possible when + // the object is about to be deleted. There are other mechanisms which ensure + // that the object is correctly closed before destruction. For example the + // object unlinking and the worker shutdown (we get notified about it via the + // callback passed to `StrongWorkerRef`) are used to close the object if it + // hasn't been closed yet. + + if (mActor) { + PFileSystemAccessHandleChild::Send__delete__(mActor); + + // `PFileSystemAccessHandleChild::Send__delete__` is supposed to call + // `FileSystemAccessHandleChild::ActorDestroy` which in turn calls + // `FileSystemSyncAccessHandle::ClearActor`, so `mActor` should be be null + // at this point. + MOZ_ASSERT(!mActor); + } + + if (mControlActor) { + mControlActor->Close(); + + // `FileSystemAccessHandleControlChild::Close` is supposed to call + // `FileSystemAccessHandleControlChild::ActorDestroy` which in turn calls + // `FileSystemSyncAccessHandle::ClearControlActor`, so `mControlActor` + // should be be null at this point. + MOZ_ASSERT(!mControlActor); + } +} + +void FileSystemSyncAccessHandle::ClearActor() { + MOZ_ASSERT(mActor); + + mActor = nullptr; +} + +void FileSystemSyncAccessHandle::ClearControlActor() { + // `mControlActor` is initialized in the constructor and this method is + // supposed to be called only once. + MOZ_ASSERT(mControlActor); + + mControlActor = nullptr; +} + +bool FileSystemSyncAccessHandle::IsOpen() const { + MOZ_ASSERT(mState != State::Initial); + + return mState == State::Open; +} + +bool FileSystemSyncAccessHandle::IsClosing() const { + MOZ_ASSERT(mState != State::Initial); + + return mState == State::Closing; +} + +bool FileSystemSyncAccessHandle::IsClosed() const { + MOZ_ASSERT(mState != State::Initial); + + return mState == State::Closed; +} + +RefPtr<BoolPromise> FileSystemSyncAccessHandle::BeginClose() { + MOZ_ASSERT(IsOpen()); + + mState = State::Closing; + + InvokeAsync(mIOTaskQueue, __func__, + [selfHolder = fs::TargetPtrHolder(this)]() { + if (selfHolder->mStream) { + LOG(("%p: Closing", selfHolder->mStream.get())); + + selfHolder->mStream->OutputStream()->Close(); + selfHolder->mStream = nullptr; + } else { + LOG(("Closing (no stream)")); + + // If the stream was not deserialized, `mStreamParams` still + // contains a pre-opened file descriptor which needs to be + // closed here by moving `mStreamParams` to a local variable + // (the file descriptor will be closed for real when + // `streamParams` goes out of scope). + + mozilla::ipc::RandomAccessStreamParams streamParams( + std::move(selfHolder->mStreamParams)); + } + + return BoolPromise::CreateAndResolve(true, __func__); + }) + ->Then(mWorkerRef->Private()->ControlEventTarget(), __func__, + [self = RefPtr(this)](const BoolPromise::ResolveOrRejectValue&) { + return self->mIOTaskQueue->BeginShutdown(); + }) + ->Then( + mWorkerRef->Private()->ControlEventTarget(), __func__, + [self = RefPtr(this)](const ShutdownPromise::ResolveOrRejectValue&) { + if (self->mControlActor) { + RefPtr<BoolPromise::Private> promise = + new BoolPromise::Private(__func__); + + self->mControlActor->SendClose( + [promise](void_t&&) { promise->Resolve(true, __func__); }, + [promise](const mozilla::ipc::ResponseRejectReason& aReason) { + fs::IPCRejectReporter(aReason); + + promise->Reject(NS_ERROR_FAILURE, __func__); + }); + + return RefPtr<BoolPromise>(promise); + } + + return BoolPromise::CreateAndResolve(true, __func__); + }) + ->Then(mWorkerRef->Private()->ControlEventTarget(), __func__, + [self = RefPtr(this)](const BoolPromise::ResolveOrRejectValue&) { + self->mWorkerRef = nullptr; + + self->mState = State::Closed; + + self->mClosePromiseHolder.ResolveIfExists(true, __func__); + }); + + return OnClose(); +} + +RefPtr<BoolPromise> FileSystemSyncAccessHandle::OnClose() { + MOZ_ASSERT(mState == State::Closing); + + return mClosePromiseHolder.Ensure(__func__); +} + +// WebIDL Boilerplate + +nsIGlobalObject* FileSystemSyncAccessHandle::GetParentObject() const { + return mGlobal; +} + +JSObject* FileSystemSyncAccessHandle::WrapObject( + JSContext* aCx, JS::Handle<JSObject*> aGivenProto) { + return FileSystemSyncAccessHandle_Binding::Wrap(aCx, this, aGivenProto); +} + +// WebIDL Interface + +uint64_t FileSystemSyncAccessHandle::Read( + const MaybeSharedArrayBufferViewOrMaybeSharedArrayBuffer& aBuffer, + const FileSystemReadWriteOptions& aOptions, ErrorResult& aRv) { + return ReadOrWrite(aBuffer, aOptions, /* aRead */ true, aRv); +} + +uint64_t FileSystemSyncAccessHandle::Write( + const MaybeSharedArrayBufferViewOrMaybeSharedArrayBuffer& aBuffer, + const FileSystemReadWriteOptions& aOptions, ErrorResult& aRv) { + return ReadOrWrite(aBuffer, aOptions, /* aRead */ false, aRv); +} + +void FileSystemSyncAccessHandle::Truncate(uint64_t aSize, ErrorResult& aError) { + if (!IsOpen()) { + aError.ThrowInvalidStateError("SyncAccessHandle is closed"); + return; + } + + MOZ_ASSERT(mWorkerRef); + + AutoSyncLoopHolder syncLoop(mWorkerRef->Private(), Canceling); + + nsCOMPtr<nsISerialEventTarget> syncLoopTarget = + syncLoop.GetSerialEventTarget(); + QM_TRY(MOZ_TO_RESULT(syncLoopTarget), [&aError](nsresult) { + aError.ThrowInvalidStateError("Worker is shutting down"); + }); + + InvokeAsync( + mIOTaskQueue, __func__, + [selfHolder = fs::TargetPtrHolder(this), aSize]() { + QM_TRY(MOZ_TO_RESULT(selfHolder->EnsureStream()), + CreateAndRejectBoolPromise); + + LOG(("%p: Truncate to %" PRIu64, selfHolder->mStream.get(), aSize)); + int64_t offset = 0; + QM_TRY(MOZ_TO_RESULT(selfHolder->mStream->Tell(&offset)), + CreateAndRejectBoolPromise); + QM_TRY(MOZ_TO_RESULT(selfHolder->mStream->Seek( + nsISeekableStream::NS_SEEK_SET, aSize)), + CreateAndRejectBoolPromise); + + QM_TRY(MOZ_TO_RESULT(selfHolder->mStream->SetEOF()), + CreateAndRejectBoolPromise); + // restore cursor position (clamp to end of file) + QM_TRY(MOZ_TO_RESULT(selfHolder->mStream->Seek( + nsISeekableStream::NS_SEEK_SET, + std::min((uint64_t)offset, aSize))), + CreateAndRejectBoolPromise); + + return BoolPromise::CreateAndResolve(true, __func__); + }) + ->Then(syncLoopTarget, __func__, + [this, &syncLoopTarget]( + const BoolPromise::ResolveOrRejectValue& aValue) { + MOZ_ASSERT(mWorkerRef); + + mWorkerRef->Private()->AssertIsOnWorkerThread(); + + mWorkerRef->Private()->StopSyncLoop( + syncLoopTarget, + aValue.IsResolve() ? NS_OK : aValue.RejectValue()); + }); + + QM_TRY(MOZ_TO_RESULT(syncLoop.Run()), + [&aError](const nsresult rv) { aError.Throw(rv); }); +} + +uint64_t FileSystemSyncAccessHandle::GetSize(ErrorResult& aError) { + if (!IsOpen()) { + aError.ThrowInvalidStateError("SyncAccessHandle is closed"); + return 0; + } + + MOZ_ASSERT(mWorkerRef); + + AutoSyncLoopHolder syncLoop(mWorkerRef->Private(), Canceling); + + nsCOMPtr<nsISerialEventTarget> syncLoopTarget = + syncLoop.GetSerialEventTarget(); + QM_TRY(MOZ_TO_RESULT(syncLoopTarget), [&aError](nsresult) { + aError.ThrowInvalidStateError("Worker is shutting down"); + return 0; + }); + + // XXX Could we somehow pass the size to `StopSyncLoop` and then get it via + // `QM_TRY_INSPECT(const auto& size, syncLoop.Run)` ? + // Could we use Result<UniquePtr<...>, nsresult> for that ? + int64_t size; + + InvokeAsync(mIOTaskQueue, __func__, + [selfHolder = fs::TargetPtrHolder(this)]() { + QM_TRY(MOZ_TO_RESULT(selfHolder->EnsureStream()), + CreateAndRejectSizePromise); + + nsCOMPtr<nsIFileMetadata> fileMetadata = + do_QueryInterface(selfHolder->mStream); + MOZ_ASSERT(fileMetadata); + + QM_TRY_INSPECT( + const auto& size, + MOZ_TO_RESULT_INVOKE_MEMBER(fileMetadata, GetSize), + CreateAndRejectSizePromise); + + LOG(("%p: GetSize %" PRIu64, selfHolder->mStream.get(), size)); + + return SizePromise::CreateAndResolve(size, __func__); + }) + ->Then(syncLoopTarget, __func__, + [this, &syncLoopTarget, + &size](const Int64Promise::ResolveOrRejectValue& aValue) { + MOZ_ASSERT(mWorkerRef); + + mWorkerRef->Private()->AssertIsOnWorkerThread(); + + if (aValue.IsResolve()) { + size = aValue.ResolveValue(); + + mWorkerRef->Private()->StopSyncLoop(syncLoopTarget, NS_OK); + } else { + mWorkerRef->Private()->StopSyncLoop(syncLoopTarget, + aValue.RejectValue()); + } + }); + + QM_TRY(MOZ_TO_RESULT(syncLoop.Run()), [&aError](const nsresult rv) { + aError.Throw(rv); + return 0; + }); + + return size; +} + +void FileSystemSyncAccessHandle::Flush(ErrorResult& aError) { + if (!IsOpen()) { + aError.ThrowInvalidStateError("SyncAccessHandle is closed"); + return; + } + + MOZ_ASSERT(mWorkerRef); + + AutoSyncLoopHolder syncLoop(mWorkerRef->Private(), Canceling); + + nsCOMPtr<nsISerialEventTarget> syncLoopTarget = + syncLoop.GetSerialEventTarget(); + QM_TRY(MOZ_TO_RESULT(syncLoopTarget), [&aError](nsresult) { + aError.ThrowInvalidStateError("Worker is shutting down"); + }); + + InvokeAsync(mIOTaskQueue, __func__, + [selfHolder = fs::TargetPtrHolder(this)]() { + QM_TRY(MOZ_TO_RESULT(selfHolder->EnsureStream()), + CreateAndRejectBoolPromise); + + LOG(("%p: Flush", selfHolder->mStream.get())); + + QM_TRY( + MOZ_TO_RESULT(selfHolder->mStream->OutputStream()->Flush()), + CreateAndRejectBoolPromise); + + return BoolPromise::CreateAndResolve(true, __func__); + }) + ->Then(syncLoopTarget, __func__, + [this, &syncLoopTarget]( + const BoolPromise::ResolveOrRejectValue& aValue) { + MOZ_ASSERT(mWorkerRef); + + mWorkerRef->Private()->AssertIsOnWorkerThread(); + + mWorkerRef->Private()->StopSyncLoop( + syncLoopTarget, + aValue.IsResolve() ? NS_OK : aValue.RejectValue()); + }); + + QM_TRY(MOZ_TO_RESULT(syncLoop.Run()), + [&aError](const nsresult rv) { aError.Throw(rv); }); +} + +void FileSystemSyncAccessHandle::Close() { + if (!(IsOpen() || IsClosing())) { + return; + } + + MOZ_ASSERT(mWorkerRef); + + // Normally mWorkerRef can be used directly for stopping the sync loop, but + // the async close is special because mWorkerRef is cleared as part of the + // operation. That's why we need to use this extra strong ref to the + // `StrongWorkerRef`. + RefPtr<StrongWorkerRef> workerRef = mWorkerRef; + + AutoSyncLoopHolder syncLoop(workerRef->Private(), Killing); + + nsCOMPtr<nsISerialEventTarget> syncLoopTarget = + syncLoop.GetSerialEventTarget(); + MOZ_ASSERT(syncLoopTarget); + + InvokeAsync(syncLoopTarget, __func__, [self = RefPtr(this)]() { + if (self->IsOpen()) { + return self->BeginClose(); + } + return self->OnClose(); + })->Then(syncLoopTarget, __func__, [&workerRef, &syncLoopTarget]() { + MOZ_ASSERT(workerRef); + + workerRef->Private()->AssertIsOnWorkerThread(); + + workerRef->Private()->StopSyncLoop(syncLoopTarget, NS_OK); + }); + + MOZ_ALWAYS_SUCCEEDS(syncLoop.Run()); +} + +uint64_t FileSystemSyncAccessHandle::ReadOrWrite( + const MaybeSharedArrayBufferViewOrMaybeSharedArrayBuffer& aBuffer, + const FileSystemReadWriteOptions& aOptions, const bool aRead, + ErrorResult& aRv) { + if (!IsOpen()) { + aRv.ThrowInvalidStateError("SyncAccessHandle is closed"); + return 0; + } + + MOZ_ASSERT(mWorkerRef); + + auto throwAndReturn = [&aRv](const nsresult rv) { + aRv.Throw(rv); + return 0; + }; + + const auto dataSpan = [&aBuffer]() { + if (aBuffer.IsArrayBuffer()) { + const ArrayBuffer& buffer = aBuffer.GetAsArrayBuffer(); + buffer.ComputeState(); + return Span{buffer.Data(), buffer.Length()}; + } + MOZ_ASSERT(aBuffer.IsArrayBufferView()); + const ArrayBufferView& buffer = aBuffer.GetAsArrayBufferView(); + buffer.ComputeState(); + return Span{buffer.Data(), buffer.Length()}; + }(); + + CheckedInt<int64_t> offset = 0; + if (aOptions.mAt.WasPassed()) { + // Handle seek before read ('at') + offset = CheckedInt<int64_t>(aOptions.mAt.Value()); + QM_TRY(MOZ_TO_RESULT(offset.isValid()), throwAndReturn); + } + AutoSyncLoopHolder syncLoop(mWorkerRef->Private(), Canceling); + + nsCOMPtr<nsISerialEventTarget> syncLoopTarget = + syncLoop.GetSerialEventTarget(); + QM_TRY(MOZ_TO_RESULT(syncLoopTarget), [&aRv](nsresult) { + aRv.ThrowInvalidStateError("Worker is shutting down"); + return 0; + }); + + uint64_t totalCount = 0; + + InvokeAsync( + mIOTaskQueue, __func__, + [selfHolder = fs::TargetPtrHolder(this), dataSpan, + use_offset = aOptions.mAt.WasPassed(), offset, aRead, &totalCount]() { + QM_TRY(MOZ_TO_RESULT(selfHolder->EnsureStream()), + CreateAndRejectBoolPromise); + + if (use_offset) { + LOG_VERBOSE(("%p: Seeking to %" PRIu64, selfHolder->mStream.get(), + offset.value())); + + QM_TRY(MOZ_TO_RESULT(selfHolder->mStream->Seek( + nsISeekableStream::NS_SEEK_SET, offset.value())), + CreateAndRejectBoolPromise); + } + + nsCOMPtr<nsIInputStream> inputStream; + nsCOMPtr<nsIOutputStream> outputStream; + + if (aRead) { + LOG_VERBOSE(("%p: Reading %zu bytes", selfHolder->mStream.get(), + dataSpan.Length())); + + inputStream = selfHolder->mStream->InputStream(); + + outputStream = + FixedBufferOutputStream::Create(AsWritableChars(dataSpan)); + } else { + LOG_VERBOSE(("%p: Writing %zu bytes", selfHolder->mStream.get(), + dataSpan.Length())); + + QM_TRY(MOZ_TO_RESULT(NS_NewByteInputStream( + getter_AddRefs(inputStream), AsChars(dataSpan), + NS_ASSIGNMENT_DEPEND)), + CreateAndRejectBoolPromise); + + outputStream = selfHolder->mStream->OutputStream(); + } + + auto promiseHolder = MakeUnique<MozPromiseHolder<BoolPromise>>(); + RefPtr<BoolPromise> promise = promiseHolder->Ensure(__func__); + + QM_TRY(MOZ_TO_RESULT(fs::AsyncCopy( + inputStream, outputStream, GetCurrentSerialEventTarget(), + aRead ? NS_ASYNCCOPY_VIA_WRITESEGMENTS + : NS_ASYNCCOPY_VIA_READSEGMENTS, + /* aCloseSource */ !aRead, /* aCloseSink */ aRead, + [&totalCount](uint32_t count) { totalCount += count; }, + [promiseHolder = std::move(promiseHolder)](nsresult rv) { + promiseHolder->ResolveIfExists(true, __func__); + })), + CreateAndRejectBoolPromise); + + return promise; + }) + ->Then(syncLoopTarget, __func__, + [this, &syncLoopTarget]( + const BoolPromise::ResolveOrRejectValue& aValue) { + MOZ_ASSERT(mWorkerRef); + + mWorkerRef->Private()->AssertIsOnWorkerThread(); + + mWorkerRef->Private()->StopSyncLoop(syncLoopTarget, NS_OK); + }); + + MOZ_ALWAYS_SUCCEEDS(syncLoop.Run()); + + return totalCount; +} + +nsresult FileSystemSyncAccessHandle::EnsureStream() { + if (!mStream) { + QM_TRY_UNWRAP(mStream, DeserializeRandomAccessStream(mStreamParams), + NS_ERROR_FAILURE); + + mozilla::ipc::RandomAccessStreamParams streamParams( + std::move(mStreamParams)); + } + + return NS_OK; +} + +} // namespace mozilla::dom |