diff options
Diffstat (limited to '')
19 files changed, 1977 insertions, 0 deletions
diff --git a/dom/fs/child/FileSystemAccessHandleChild.cpp b/dom/fs/child/FileSystemAccessHandleChild.cpp new file mode 100644 index 0000000000..b32d9b2dc6 --- /dev/null +++ b/dom/fs/child/FileSystemAccessHandleChild.cpp @@ -0,0 +1,34 @@ +/* -*- 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 "FileSystemAccessHandleChild.h" + +#include "mozilla/dom/FileSystemSyncAccessHandle.h" +#include "private/pprio.h" + +namespace mozilla::dom { + +FileSystemAccessHandleChild::FileSystemAccessHandleChild() + : mAccessHandle(nullptr) {} + +FileSystemAccessHandleChild::~FileSystemAccessHandleChild() = default; + +void FileSystemAccessHandleChild::SetAccessHandle( + FileSystemSyncAccessHandle* aAccessHandle) { + MOZ_ASSERT(aAccessHandle); + MOZ_ASSERT(!mAccessHandle); + + mAccessHandle = aAccessHandle; +} + +void FileSystemAccessHandleChild::ActorDestroy(ActorDestroyReason aWhy) { + if (mAccessHandle) { + mAccessHandle->ClearActor(); + mAccessHandle = nullptr; + } +} + +} // namespace mozilla::dom diff --git a/dom/fs/child/FileSystemAccessHandleChild.h b/dom/fs/child/FileSystemAccessHandleChild.h new file mode 100644 index 0000000000..d616178646 --- /dev/null +++ b/dom/fs/child/FileSystemAccessHandleChild.h @@ -0,0 +1,41 @@ +/* -*- 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/. */ + +#ifndef DOM_FS_CHILD_FILESYSTEMACCESSHANDLECHILD_H_ +#define DOM_FS_CHILD_FILESYSTEMACCESSHANDLECHILD_H_ + +#include "mozilla/dom/PFileSystemAccessHandleChild.h" + +namespace mozilla::dom { + +class FileSystemSyncAccessHandle; + +class FileSystemAccessHandleChild : public PFileSystemAccessHandleChild { + public: + FileSystemAccessHandleChild(); + + NS_INLINE_DECL_REFCOUNTING(FileSystemAccessHandleChild, override) + + FileSystemSyncAccessHandle* MutableAccessHandlePtr() const { + MOZ_ASSERT(mAccessHandle); + return mAccessHandle; + } + + void SetAccessHandle(FileSystemSyncAccessHandle* aAccessHandle); + + void ActorDestroy(ActorDestroyReason aWhy) override; + + private: + virtual ~FileSystemAccessHandleChild(); + + // Use a weak ref so actor does not hold DOM object alive past content use. + // The weak reference is cleared in FileSystemSyncAccessHandle::LastRelease. + FileSystemSyncAccessHandle* MOZ_NON_OWNING_REF mAccessHandle; +}; + +} // namespace mozilla::dom + +#endif // DOM_FS_CHILD_FILESYSTEMACCESSHANDLECHILD_H_ diff --git a/dom/fs/child/FileSystemAccessHandleControlChild.cpp b/dom/fs/child/FileSystemAccessHandleControlChild.cpp new file mode 100644 index 0000000000..60cb4e621b --- /dev/null +++ b/dom/fs/child/FileSystemAccessHandleControlChild.cpp @@ -0,0 +1,37 @@ +/* -*- 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 "FileSystemAccessHandleControlChild.h" + +#include "mozilla/dom/FileSystemSyncAccessHandle.h" + +namespace mozilla::dom { + +void FileSystemAccessHandleControlChild::SetAccessHandle( + FileSystemSyncAccessHandle* aAccessHandle) { + MOZ_ASSERT(aAccessHandle); + MOZ_ASSERT(!mAccessHandle); + + mAccessHandle = aAccessHandle; +} + +void FileSystemAccessHandleControlChild::Shutdown() { + if (!CanSend()) { + return; + } + + Close(); +} + +void FileSystemAccessHandleControlChild::ActorDestroy( + ActorDestroyReason /* aWhy */) { + if (mAccessHandle) { + mAccessHandle->ClearControlActor(); + mAccessHandle = nullptr; + } +} + +} // namespace mozilla::dom diff --git a/dom/fs/child/FileSystemAccessHandleControlChild.h b/dom/fs/child/FileSystemAccessHandleControlChild.h new file mode 100644 index 0000000000..93ddb36e55 --- /dev/null +++ b/dom/fs/child/FileSystemAccessHandleControlChild.h @@ -0,0 +1,47 @@ +/* -*- 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/. */ + +#ifndef DOM_FS_CHILD_FILESYSTEMACCESSHANDLECONTROLCHILD_H_ +#define DOM_FS_CHILD_FILESYSTEMACCESSHANDLECONTROLCHILD_H_ + +#include "mozilla/dom/PFileSystemAccessHandleControlChild.h" +#include "nsISupportsImpl.h" + +namespace mozilla::dom { + +class FileSystemSyncAccessHandle; + +class FileSystemAccessHandleControlChild + : public PFileSystemAccessHandleControlChild { + public: + NS_INLINE_DECL_REFCOUNTING_WITH_DESTROY(FileSystemAccessHandleControlChild, + Destroy(), override) + + void SetAccessHandle(FileSystemSyncAccessHandle* aAccessHandle); + + void Shutdown(); + + void ActorDestroy(ActorDestroyReason aWhy) override; + + protected: + virtual ~FileSystemAccessHandleControlChild() = default; + + // This is called when the object's refcount drops to zero. We use this custom + // callback to close the top level actor if it hasn't been explicitly closed + // yet. For example when `FileSystemSyncAccessHandle::Create` fails after + // creating and binding the top level actor. + virtual void Destroy() { + Shutdown(); + delete this; + } + + // The weak reference is cleared in ActorDestroy. + FileSystemSyncAccessHandle* MOZ_NON_OWNING_REF mAccessHandle; +}; + +} // namespace mozilla::dom + +#endif // DOM_FS_CHILD_FILESYSTEMACCESSHANDLECONTROLCHILD_H_ diff --git a/dom/fs/child/FileSystemAsyncCopy.cpp b/dom/fs/child/FileSystemAsyncCopy.cpp new file mode 100644 index 0000000000..782d67b5cd --- /dev/null +++ b/dom/fs/child/FileSystemAsyncCopy.cpp @@ -0,0 +1,66 @@ +/* -*- 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 "fs/FileSystemAsyncCopy.h" + +#include "fs/FileSystemConstants.h" +#include "mozilla/MozPromise.h" +#include "mozilla/dom/Promise.h" +#include "mozilla/dom/quota/QuotaCommon.h" +#include "mozilla/dom/quota/ResultExtensions.h" + +namespace mozilla::dom::fs { + +nsresult AsyncCopy(nsIInputStream* aSource, nsIOutputStream* aSink, + nsISerialEventTarget* aIOTarget, const nsAsyncCopyMode aMode, + const bool aCloseSource, const bool aCloseSink, + std::function<void(uint32_t)>&& aProgressCallback, + MoveOnlyFunction<void(nsresult)>&& aCompleteCallback) { + struct CallbackClosure { + CallbackClosure(std::function<void(uint32_t)>&& aProgressCallback, + MoveOnlyFunction<void(nsresult)>&& aCompleteCallback) { + mProgressCallbackWrapper = MakeUnique<std::function<void(uint32_t)>>( + [progressCallback = std::move(aProgressCallback)](uint32_t count) { + progressCallback(count); + }); + + mCompleteCallbackWrapper = MakeUnique<MoveOnlyFunction<void(nsresult)>>( + [completeCallback = + std::move(aCompleteCallback)](nsresult rv) mutable { + auto callback = std::move(completeCallback); + callback(rv); + }); + } + + UniquePtr<std::function<void(uint32_t)>> mProgressCallbackWrapper; + UniquePtr<MoveOnlyFunction<void(nsresult)>> mCompleteCallbackWrapper; + }; + + auto* callbackClosure = new CallbackClosure(std::move(aProgressCallback), + std::move(aCompleteCallback)); + + QM_TRY( + MOZ_TO_RESULT(NS_AsyncCopy( + aSource, aSink, aIOTarget, aMode, kStreamCopyBlockSize, + [](void* aClosure, nsresult aRv) { + auto* callbackClosure = static_cast<CallbackClosure*>(aClosure); + (*callbackClosure->mCompleteCallbackWrapper)(aRv); + delete callbackClosure; + }, + callbackClosure, aCloseSource, aCloseSink, /* aCopierCtx */ nullptr, + [](void* aClosure, uint32_t aCount) { + auto* callbackClosure = static_cast<CallbackClosure*>(aClosure); + (*callbackClosure->mProgressCallbackWrapper)(aCount); + })), + [callbackClosure](nsresult rv) { + delete callbackClosure; + return rv; + }); + + return NS_OK; +} + +} // namespace mozilla::dom::fs diff --git a/dom/fs/child/FileSystemBackgroundRequestHandler.cpp b/dom/fs/child/FileSystemBackgroundRequestHandler.cpp new file mode 100644 index 0000000000..d88ec05a8c --- /dev/null +++ b/dom/fs/child/FileSystemBackgroundRequestHandler.cpp @@ -0,0 +1,142 @@ +/* -*- 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 "FileSystemBackgroundRequestHandler.h" + +#include "fs/FileSystemChildFactory.h" +#include "mozilla/dom/FileSystemManagerChild.h" +#include "mozilla/dom/PFileSystemManager.h" +#include "mozilla/ipc/BackgroundChild.h" +#include "mozilla/ipc/Endpoint.h" +#include "mozilla/ipc/PBackgroundChild.h" + +namespace mozilla::dom { + +FileSystemBackgroundRequestHandler::FileSystemBackgroundRequestHandler( + fs::FileSystemChildFactory* aChildFactory) + : mChildFactory(aChildFactory), mCreatingFileSystemManagerChild(false) {} + +FileSystemBackgroundRequestHandler::FileSystemBackgroundRequestHandler( + RefPtr<FileSystemManagerChild> aFileSystemManagerChild) + : mFileSystemManagerChild(std::move(aFileSystemManagerChild)), + mCreatingFileSystemManagerChild(false) {} + +FileSystemBackgroundRequestHandler::FileSystemBackgroundRequestHandler() + : FileSystemBackgroundRequestHandler(new fs::FileSystemChildFactory()) {} + +FileSystemBackgroundRequestHandler::~FileSystemBackgroundRequestHandler() = + default; + +void FileSystemBackgroundRequestHandler::ClearActor() { + MOZ_ASSERT(mFileSystemManagerChild); + + mFileSystemManagerChild = nullptr; +} + +void FileSystemBackgroundRequestHandler::Shutdown() { + mShutdown.Flip(); + + if (mFileSystemManagerChild) { + MOZ_ASSERT(!mCreatingFileSystemManagerChild); + + mFileSystemManagerChild->Shutdown(); + + mFileSystemManagerChild = nullptr; + + return; + } + + if (mCreatingFileSystemManagerChild) { + MOZ_ASSERT(!mFileSystemManagerChild); + + mCreateFileSystemManagerParentPromiseRequestHolder.Disconnect(); + + mCreatingFileSystemManagerChild = false; + + // We must either resolve/reject the promise or steal the internal promise + // before the holder is destroyed. The former isn't possible during + // shutdown. + Unused << mCreateFileSystemManagerChildPromiseHolder.Steal(); + } +} + +const RefPtr<FileSystemManagerChild>& +FileSystemBackgroundRequestHandler::FileSystemManagerChildStrongRef() const { + return mFileSystemManagerChild; +} + +RefPtr<BoolPromise> +FileSystemBackgroundRequestHandler::CreateFileSystemManagerChild( + const mozilla::ipc::PrincipalInfo& aPrincipalInfo) { + MOZ_ASSERT(!mFileSystemManagerChild); + MOZ_ASSERT(!mShutdown); + + using mozilla::ipc::BackgroundChild; + using mozilla::ipc::Endpoint; + using mozilla::ipc::PBackgroundChild; + + if (!mCreatingFileSystemManagerChild) { + PBackgroundChild* backgroundChild = + BackgroundChild::GetOrCreateForCurrentThread(); + if (NS_WARN_IF(!backgroundChild)) { + return BoolPromise::CreateAndReject(NS_ERROR_FAILURE, __func__); + } + + // Create a new IPC connection + Endpoint<PFileSystemManagerParent> parentEndpoint; + Endpoint<PFileSystemManagerChild> childEndpoint; + MOZ_ALWAYS_SUCCEEDS( + PFileSystemManager::CreateEndpoints(&parentEndpoint, &childEndpoint)); + + RefPtr<FileSystemManagerChild> child = mChildFactory->Create(); + if (!childEndpoint.Bind(child)) { + return BoolPromise::CreateAndReject(NS_ERROR_FAILURE, __func__); + } + + mCreatingFileSystemManagerChild = true; + + backgroundChild + ->SendCreateFileSystemManagerParent(aPrincipalInfo, + std::move(parentEndpoint)) + ->Then( + GetCurrentSerialEventTarget(), __func__, + [self = RefPtr<FileSystemBackgroundRequestHandler>(this), + child](nsresult rv) { + self->mCreateFileSystemManagerParentPromiseRequestHolder + .Complete(); + + self->mCreatingFileSystemManagerChild = false; + + if (NS_FAILED(rv)) { + self->mCreateFileSystemManagerChildPromiseHolder.RejectIfExists( + rv, __func__); + } else { + self->mFileSystemManagerChild = child; + + self->mFileSystemManagerChild->SetBackgroundRequestHandler( + self); + + self->mCreateFileSystemManagerChildPromiseHolder + .ResolveIfExists(true, __func__); + } + }, + [self = RefPtr<FileSystemBackgroundRequestHandler>(this)]( + const mozilla::ipc::ResponseRejectReason&) { + self->mCreateFileSystemManagerParentPromiseRequestHolder + .Complete(); + + self->mCreatingFileSystemManagerChild = false; + + self->mCreateFileSystemManagerChildPromiseHolder.RejectIfExists( + NS_ERROR_FAILURE, __func__); + }) + ->Track(mCreateFileSystemManagerParentPromiseRequestHolder); + } + + return mCreateFileSystemManagerChildPromiseHolder.Ensure(__func__); +} + +} // namespace mozilla::dom diff --git a/dom/fs/child/FileSystemBackgroundRequestHandler.h b/dom/fs/child/FileSystemBackgroundRequestHandler.h new file mode 100644 index 0000000000..a206375384 --- /dev/null +++ b/dom/fs/child/FileSystemBackgroundRequestHandler.h @@ -0,0 +1,73 @@ +/* -*- 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/. */ + +#ifndef DOM_FS_CHILD_FILESYSTEMBACKGROUNDREQUESTHANDLER_H_ +#define DOM_FS_CHILD_FILESYSTEMBACKGROUNDREQUESTHANDLER_H_ + +#include "mozilla/MozPromise.h" +#include "mozilla/UniquePtr.h" +#include "mozilla/dom/quota/ForwardDecls.h" +#include "mozilla/ipc/PBackgroundChild.h" + +template <class T> +class RefPtr; + +namespace mozilla { + +namespace ipc { +class PrincipalInfo; +} // namespace ipc + +namespace dom { + +class FileSystemManagerChild; + +namespace fs { +class FileSystemChildFactory; +} + +class FileSystemBackgroundRequestHandler { + public: + explicit FileSystemBackgroundRequestHandler( + fs::FileSystemChildFactory* aChildFactory); + + explicit FileSystemBackgroundRequestHandler( + RefPtr<FileSystemManagerChild> aFileSystemManagerChild); + + FileSystemBackgroundRequestHandler(); + + NS_INLINE_DECL_REFCOUNTING(FileSystemBackgroundRequestHandler) + + void ClearActor(); + + void Shutdown(); + + const RefPtr<FileSystemManagerChild>& FileSystemManagerChildStrongRef() const; + + virtual RefPtr<mozilla::BoolPromise> CreateFileSystemManagerChild( + const mozilla::ipc::PrincipalInfo& aPrincipalInfo); + + protected: + virtual ~FileSystemBackgroundRequestHandler(); + + const UniquePtr<fs::FileSystemChildFactory> mChildFactory; + + MozPromiseRequestHolder< + mozilla::ipc::PBackgroundChild::CreateFileSystemManagerParentPromise> + mCreateFileSystemManagerParentPromiseRequestHolder; + MozPromiseHolder<BoolPromise> mCreateFileSystemManagerChildPromiseHolder; + + RefPtr<FileSystemManagerChild> mFileSystemManagerChild; + + FlippedOnce<false> mShutdown; + + bool mCreatingFileSystemManagerChild; +}; // class FileSystemBackgroundRequestHandler + +} // namespace dom +} // namespace mozilla + +#endif // DOM_FS_CHILD_FILESYSTEMBACKGROUNDREQUESTHANDLER_H_ diff --git a/dom/fs/child/FileSystemChildFactory.cpp b/dom/fs/child/FileSystemChildFactory.cpp new file mode 100644 index 0000000000..bf4c493dd8 --- /dev/null +++ b/dom/fs/child/FileSystemChildFactory.cpp @@ -0,0 +1,19 @@ +/* -*- 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 "fs/FileSystemChildFactory.h" + +#include "mozilla/RefPtr.h" +#include "mozilla/dom/FileSystemManagerChild.h" + +namespace mozilla::dom::fs { + +already_AddRefed<FileSystemManagerChild> FileSystemChildFactory::Create() + const { + return MakeAndAddRef<FileSystemManagerChild>(); +} + +} // namespace mozilla::dom::fs diff --git a/dom/fs/child/FileSystemDirectoryIteratorFactory.cpp b/dom/fs/child/FileSystemDirectoryIteratorFactory.cpp new file mode 100644 index 0000000000..5c9b9e60bc --- /dev/null +++ b/dom/fs/child/FileSystemDirectoryIteratorFactory.cpp @@ -0,0 +1,243 @@ +/* -*- 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 "FileSystemDirectoryIteratorFactory.h" + +#include "FileSystemEntryMetadataArray.h" +#include "fs/FileSystemRequestHandler.h" +#include "jsapi.h" +#include "mozilla/dom/FileSystemDirectoryHandle.h" +#include "mozilla/dom/FileSystemDirectoryIterator.h" +#include "mozilla/dom/FileSystemFileHandle.h" +#include "mozilla/dom/FileSystemHandle.h" +#include "mozilla/dom/FileSystemLog.h" +#include "mozilla/dom/FileSystemManager.h" +#include "mozilla/dom/IterableIterator.h" +#include "mozilla/dom/Promise-inl.h" +#include "mozilla/dom/Promise.h" +#include "mozilla/dom/PromiseNativeHandler.h" + +namespace mozilla::dom::fs { + +namespace { + +template <IterableIteratorBase::IteratorType Type> +struct ValueResolver; + +template <> +struct ValueResolver<IterableIteratorBase::Keys> { + void operator()(nsIGlobalObject* aGlobal, RefPtr<FileSystemManager>& aManager, + const FileSystemEntryMetadata& aValue, + const RefPtr<Promise>& aPromise) { + aPromise->MaybeResolve(aValue.entryName()); + } +}; + +template <> +struct ValueResolver<IterableIteratorBase::Values> { + void operator()(nsIGlobalObject* aGlobal, RefPtr<FileSystemManager>& aManager, + const FileSystemEntryMetadata& aValue, + const RefPtr<Promise>& aPromise) { + RefPtr<FileSystemHandle> handle; + + if (aValue.directory()) { + handle = new FileSystemDirectoryHandle(aGlobal, aManager, aValue); + } else { + handle = new FileSystemFileHandle(aGlobal, aManager, aValue); + } + + aPromise->MaybeResolve(std::move(handle)); + } +}; + +template <> +struct ValueResolver<IterableIteratorBase::Entries> { + void operator()(nsIGlobalObject* aGlobal, RefPtr<FileSystemManager>& aManager, + const FileSystemEntryMetadata& aValue, + const RefPtr<Promise>& aPromise) { + RefPtr<FileSystemHandle> handle; + + if (aValue.directory()) { + handle = new FileSystemDirectoryHandle(aGlobal, aManager, aValue); + } else { + handle = new FileSystemFileHandle(aGlobal, aManager, aValue); + } + + iterator_utils::ResolvePromiseWithKeyAndValue(aPromise, aValue.entryName(), + handle); + } +}; + +// TODO: PageSize could be compile-time shared between content and parent +template <class ValueResolver, size_t PageSize = 1024u> +class DoubleBufferQueueImpl + : public mozilla::dom::FileSystemDirectoryIterator::Impl { + static_assert(PageSize > 0u); + + public: + using DataType = FileSystemEntryMetadata; + explicit DoubleBufferQueueImpl(const FileSystemEntryMetadata& aMetadata) + : mEntryId(aMetadata.entryId()), + mWithinPageEnd(0u), + mWithinPageIndex(0u), + mCurrentPageIsLastPage(true), + mPageNumber(0u) {} + + // XXX This doesn't have to be public + void ResolveValue(nsIGlobalObject* aGlobal, + RefPtr<FileSystemManager>& aManager, + const Maybe<DataType>& aValue, RefPtr<Promise> aPromise) { + MOZ_ASSERT(aPromise); + MOZ_ASSERT(aPromise.get()); + + if (!aValue) { + iterator_utils::ResolvePromiseForFinished(aPromise); + return; + } + + ValueResolver{}(aGlobal, aManager, *aValue, aPromise); + } + + already_AddRefed<Promise> Next(nsIGlobalObject* aGlobal, + RefPtr<FileSystemManager>& aManager, + ErrorResult& aError) override { + RefPtr<Promise> promise = Promise::Create(aGlobal, aError); + if (aError.Failed()) { + return nullptr; + } + + next(aGlobal, aManager, promise, aError); + if (aError.Failed()) { + return nullptr; + } + + return promise.forget(); + } + + protected: + ~DoubleBufferQueueImpl() override = default; + + void next(nsIGlobalObject* aGlobal, RefPtr<FileSystemManager>& aManager, + RefPtr<Promise> aResult, ErrorResult& aError) { + LOG_VERBOSE(("next")); + MOZ_ASSERT(aResult); + + Maybe<DataType> rawValue; + + // TODO: Would it be better to prefetch the items before + // we hit the end of a page? + // How likely it is that it would that lead to wasted fetch operations? + if (0u == mWithinPageIndex) { + RefPtr<Promise> promise = Promise::Create(aGlobal, aError); + if (aError.Failed()) { + return; + } + + auto newPage = MakeRefPtr<FileSystemEntryMetadataArray>(); + + promise->AddCallbacksWithCycleCollectedArgs( + [self = RefPtr(this), newPage]( + JSContext*, JS::Handle<JS::Value>, ErrorResult&, + RefPtr<FileSystemManager>& aManager, RefPtr<Promise>& aResult) { + MOZ_ASSERT(0u == self->mWithinPageIndex); + MOZ_ASSERT(newPage->Length() <= PageSize); + + const size_t startPos = + self->mCurrentPageIsLastPage ? 0u : PageSize; + if (self->mData.Length() < 2 * PageSize) { + self->mData.InsertElementsAt(startPos, newPage->Elements(), + newPage->Length()); + } else { + self->mData.ReplaceElementsAt(startPos, newPage->Length(), + newPage->Elements(), + newPage->Length()); + } + MOZ_ASSERT(self->mData.Length() <= 2 * PageSize); + self->mWithinPageEnd = newPage->Length(); + + Maybe<DataType> value; + if (0 != newPage->Length()) { + self->nextInternal(value); + } + + self->ResolveValue(aResult->GetGlobalObject(), aManager, value, + aResult); + }, + [](JSContext*, JS::Handle<JS::Value> aValue, ErrorResult&, + RefPtr<FileSystemManager>&, + RefPtr<Promise>& aResult) { aResult->MaybeReject(aValue); }, + aManager, aResult); + + FileSystemRequestHandler{}.GetEntries(aManager, mEntryId, mPageNumber, + promise, newPage, aError); + if (aError.Failed()) { + return; + } + + ++mPageNumber; + return; + } + + nextInternal(rawValue); + + ResolveValue(aGlobal, aManager, rawValue, aResult); + } + + bool nextInternal(Maybe<DataType>& aNext) { + if (mWithinPageIndex >= mWithinPageEnd) { + return false; + } + + const size_t previous = + mWithinPageIndex + (mCurrentPageIsLastPage ? 0 : PageSize); + MOZ_ASSERT(2u * PageSize > previous); + MOZ_ASSERT(previous < mData.Length()); + + ++mWithinPageIndex; + + if (mWithinPageIndex == PageSize) { + // Page end reached + mWithinPageIndex = 0u; + mCurrentPageIsLastPage = !mCurrentPageIsLastPage; + } + + aNext = Some(mData[previous]); + return true; + } + + const EntryId mEntryId; + + nsTArray<DataType> mData; // TODO: Fixed size above one page? + + size_t mWithinPageEnd = 0u; + size_t mWithinPageIndex = 0u; + bool mCurrentPageIsLastPage = true; // In the beginning, first page is free + PageNumber mPageNumber = 0u; +}; + +template <IterableIteratorBase::IteratorType Type> +using UnderlyingQueue = DoubleBufferQueueImpl<ValueResolver<Type>>; + +} // namespace + +already_AddRefed<mozilla::dom::FileSystemDirectoryIterator::Impl> +FileSystemDirectoryIteratorFactory::Create( + const FileSystemEntryMetadata& aMetadata, + IterableIteratorBase::IteratorType aType) { + if (IterableIteratorBase::Entries == aType) { + return MakeAndAddRef<UnderlyingQueue<IterableIteratorBase::Entries>>( + aMetadata); + } + + if (IterableIteratorBase::Values == aType) { + return MakeAndAddRef<UnderlyingQueue<IterableIteratorBase::Values>>( + aMetadata); + } + + return MakeAndAddRef<UnderlyingQueue<IterableIteratorBase::Keys>>(aMetadata); +} + +} // namespace mozilla::dom::fs diff --git a/dom/fs/child/FileSystemDirectoryIteratorFactory.h b/dom/fs/child/FileSystemDirectoryIteratorFactory.h new file mode 100644 index 0000000000..54574ab5d2 --- /dev/null +++ b/dom/fs/child/FileSystemDirectoryIteratorFactory.h @@ -0,0 +1,24 @@ +/* -*- 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/. */ + +#ifndef DOM_FS_CHILD_FILESYSTEMDIRECTORYITERATORFACTORY_H_ +#define DOM_FS_CHILD_FILESYSTEMDIRECTORYITERATORFACTORY_H_ + +#include "mozilla/dom/FileSystemDirectoryIterator.h" + +namespace mozilla::dom::fs { + +class FileSystemEntryMetadata; + +struct FileSystemDirectoryIteratorFactory { + static already_AddRefed<mozilla::dom::FileSystemDirectoryIterator::Impl> + Create(const FileSystemEntryMetadata& aMetadata, + IterableIteratorBase::IteratorType aType); +}; + +} // namespace mozilla::dom::fs + +#endif // DOM_FS_CHILD_FILESYSTEMDIRECTORYITERATORFACTORY_H_ diff --git a/dom/fs/child/FileSystemEntryMetadataArray.h b/dom/fs/child/FileSystemEntryMetadataArray.h new file mode 100644 index 0000000000..cd5370846a --- /dev/null +++ b/dom/fs/child/FileSystemEntryMetadataArray.h @@ -0,0 +1,26 @@ +/* -*- 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/. */ + +#ifndef DOM_FS_CHILD_FILESYSTEMENTRYMETADATAARRAY_H_ +#define DOM_FS_CHILD_FILESYSTEMENTRYMETADATAARRAY_H_ + +#include "nsTArray.h" + +namespace mozilla::dom::fs { + +class FileSystemEntryMetadata; + +class FileSystemEntryMetadataArray : public nsTArray<FileSystemEntryMetadata> { + public: + NS_INLINE_DECL_REFCOUNTING(FileSystemEntryMetadataArray); + + private: + ~FileSystemEntryMetadataArray() = default; +}; + +} // namespace mozilla::dom::fs + +#endif // DOM_FS_CHILD_FILESYSTEMENTRYMETADATAARRAY_H_ diff --git a/dom/fs/child/FileSystemManagerChild.cpp b/dom/fs/child/FileSystemManagerChild.cpp new file mode 100644 index 0000000000..5c1ecc3eea --- /dev/null +++ b/dom/fs/child/FileSystemManagerChild.cpp @@ -0,0 +1,135 @@ +/* -*- 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 "FileSystemManagerChild.h" + +#include "FileSystemAccessHandleChild.h" +#include "FileSystemBackgroundRequestHandler.h" +#include "FileSystemWritableFileStreamChild.h" +#include "mozilla/dom/FileSystemSyncAccessHandle.h" +#include "mozilla/dom/FileSystemWritableFileStream.h" + +namespace mozilla::dom { + +void FileSystemManagerChild::SetBackgroundRequestHandler( + FileSystemBackgroundRequestHandler* aBackgroundRequestHandler) { + MOZ_ASSERT(aBackgroundRequestHandler); + MOZ_ASSERT(!mBackgroundRequestHandler); + + mBackgroundRequestHandler = aBackgroundRequestHandler; +} + +void FileSystemManagerChild::CloseAllWritables( + std::function<void()>&& aCallback) { + nsTArray<RefPtr<BoolPromise>> promises; + CloseAllWritablesImpl(promises); + + BoolPromise::AllSettled(GetCurrentSerialEventTarget(), promises) + ->Then(GetCurrentSerialEventTarget(), __func__, + [callback = std::move(aCallback)]( + const BoolPromise::AllSettledPromiseType::ResolveOrRejectValue& + /* aValues */) { callback(); }); +} + +#ifdef DEBUG +bool FileSystemManagerChild::AllSyncAccessHandlesClosed() const { + for (const auto& item : ManagedPFileSystemAccessHandleChild()) { + auto* child = static_cast<FileSystemAccessHandleChild*>(item); + auto* handle = child->MutableAccessHandlePtr(); + + if (!handle->IsClosed()) { + return false; + } + } + + return true; +} + +bool FileSystemManagerChild::AllWritableFileStreamsClosed() const { + for (const auto& item : ManagedPFileSystemWritableFileStreamChild()) { + auto* const child = static_cast<FileSystemWritableFileStreamChild*>(item); + auto* const handle = child->MutableWritableFileStreamPtr(); + if (!handle) { + continue; + } + + if (!handle->IsDone()) { + return false; + } + } + + return true; +} + +#endif + +void FileSystemManagerChild::Shutdown() { + if (!CanSend()) { + return; + } + + Close(); +} + +already_AddRefed<PFileSystemWritableFileStreamChild> +FileSystemManagerChild::AllocPFileSystemWritableFileStreamChild() { + return MakeAndAddRef<FileSystemWritableFileStreamChild>(); +} + +::mozilla::ipc::IPCResult FileSystemManagerChild::RecvCloseAll( + CloseAllResolver&& aResolver) { + nsTArray<RefPtr<BoolPromise>> promises; + + // NOTE: getFile() creates blobs that read the data from the child; + // we'll need to abort any reads and resolve this call only when all + // blobs are closed. + + for (const auto& item : ManagedPFileSystemAccessHandleChild()) { + auto* child = static_cast<FileSystemAccessHandleChild*>(item); + auto* handle = child->MutableAccessHandlePtr(); + + if (handle->IsOpen()) { + promises.AppendElement(handle->BeginClose()); + } else if (handle->IsClosing()) { + promises.AppendElement(handle->OnClose()); + } + } + + CloseAllWritablesImpl(promises); + + BoolPromise::AllSettled(GetCurrentSerialEventTarget(), promises) + ->Then(GetCurrentSerialEventTarget(), __func__, + [resolver = std::move(aResolver)]( + const BoolPromise::AllSettledPromiseType::ResolveOrRejectValue& + /* aValues */) { resolver(NS_OK); }); + + return IPC_OK(); +} + +void FileSystemManagerChild::ActorDestroy(ActorDestroyReason aWhy) { + if (mBackgroundRequestHandler) { + mBackgroundRequestHandler->ClearActor(); + mBackgroundRequestHandler = nullptr; + } +} + +template <class T> +void FileSystemManagerChild::CloseAllWritablesImpl(T& aPromises) { + for (const auto& item : ManagedPFileSystemWritableFileStreamChild()) { + auto* const child = static_cast<FileSystemWritableFileStreamChild*>(item); + auto* const handle = child->MutableWritableFileStreamPtr(); + + if (handle) { + if (handle->IsOpen()) { + aPromises.AppendElement(handle->BeginAbort()); + } else if (handle->IsFinishing()) { + aPromises.AppendElement(handle->OnDone()); + } + } + } +} + +} // namespace mozilla::dom diff --git a/dom/fs/child/FileSystemManagerChild.h b/dom/fs/child/FileSystemManagerChild.h new file mode 100644 index 0000000000..bd5520a982 --- /dev/null +++ b/dom/fs/child/FileSystemManagerChild.h @@ -0,0 +1,62 @@ +/* -*- 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/. */ + +#ifndef DOM_FS_CHILD_FILESYSTEMMANAGERCHILD_H_ +#define DOM_FS_CHILD_FILESYSTEMMANAGERCHILD_H_ + +#include "mozilla/dom/FileSystemWritableFileStreamChild.h" +#include "mozilla/dom/PFileSystemManagerChild.h" +#include "mozilla/dom/quota/ForwardDecls.h" + +namespace mozilla::dom { + +class FileSystemBackgroundRequestHandler; + +class FileSystemManagerChild : public PFileSystemManagerChild { + public: + NS_INLINE_DECL_REFCOUNTING_WITH_DESTROY(FileSystemManagerChild, Destroy(), + override) + + void SetBackgroundRequestHandler( + FileSystemBackgroundRequestHandler* aBackgroundRequestHandler); + + void CloseAllWritables(std::function<void()>&& aCallback); + +#ifdef DEBUG + virtual bool AllSyncAccessHandlesClosed() const; + + virtual bool AllWritableFileStreamsClosed() const; +#endif + + virtual void Shutdown(); + + already_AddRefed<PFileSystemWritableFileStreamChild> + AllocPFileSystemWritableFileStreamChild(); + + ::mozilla::ipc::IPCResult RecvCloseAll(CloseAllResolver&& aResolver); + + void ActorDestroy(ActorDestroyReason aWhy) override; + + protected: + virtual ~FileSystemManagerChild() = default; + + virtual void Destroy() { + Shutdown(); + delete this; + } + + // The weak reference is cleared in ActorDestroy. + FileSystemBackgroundRequestHandler* MOZ_NON_OWNING_REF + mBackgroundRequestHandler; + + private: + template <class T> + void CloseAllWritablesImpl(T& aPromises); +}; + +} // namespace mozilla::dom + +#endif // DOM_FS_CHILD_FILESYSTEMMANAGERCHILD_H_ diff --git a/dom/fs/child/FileSystemRequestHandler.cpp b/dom/fs/child/FileSystemRequestHandler.cpp new file mode 100644 index 0000000000..939bd41115 --- /dev/null +++ b/dom/fs/child/FileSystemRequestHandler.cpp @@ -0,0 +1,641 @@ +/* -*- 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 "fs/FileSystemRequestHandler.h" + +#include "FileSystemEntryMetadataArray.h" +#include "fs/FileSystemConstants.h" +#include "mozilla/ResultVariant.h" +#include "mozilla/StaticPrefs_dom.h" +#include "mozilla/dom/BlobImpl.h" +#include "mozilla/dom/File.h" +#include "mozilla/dom/FileSystemAccessHandleChild.h" +#include "mozilla/dom/FileSystemDirectoryHandle.h" +#include "mozilla/dom/FileSystemFileHandle.h" +#include "mozilla/dom/FileSystemHandle.h" +#include "mozilla/dom/FileSystemHelpers.h" +#include "mozilla/dom/FileSystemLog.h" +#include "mozilla/dom/FileSystemManager.h" +#include "mozilla/dom/FileSystemManagerChild.h" +#include "mozilla/dom/FileSystemSyncAccessHandle.h" +#include "mozilla/dom/FileSystemWritableFileStream.h" +#include "mozilla/dom/FileSystemWritableFileStreamChild.h" +#include "mozilla/dom/IPCBlobUtils.h" +#include "mozilla/dom/Promise.h" +#include "mozilla/dom/WorkerCommon.h" +#include "mozilla/dom/WorkerRef.h" +#include "mozilla/dom/fs/IPCRejectReporter.h" +#include "mozilla/dom/quota/QuotaCommon.h" +#include "mozilla/dom/quota/ResultExtensions.h" +#include "mozilla/ipc/RandomAccessStreamUtils.h" + +namespace mozilla::dom::fs { + +using mozilla::ipc::RejectCallback; + +namespace { + +void HandleFailedStatus(nsresult aError, const RefPtr<Promise>& aPromise) { + switch (aError) { + case NS_ERROR_FILE_ACCESS_DENIED: + aPromise->MaybeRejectWithNotAllowedError("Permission denied"); + break; + case NS_ERROR_FILE_NOT_FOUND: + [[fallthrough]]; + case NS_ERROR_DOM_NOT_FOUND_ERR: + aPromise->MaybeRejectWithNotFoundError("Entry not found"); + break; + case NS_ERROR_DOM_FILESYSTEM_NO_MODIFICATION_ALLOWED_ERR: + aPromise->MaybeRejectWithInvalidModificationError("Disallowed by system"); + break; + case NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR: + aPromise->MaybeRejectWithNoModificationAllowedError( + "No modification allowed"); + break; + case NS_ERROR_DOM_TYPE_MISMATCH_ERR: + aPromise->MaybeRejectWithTypeMismatchError("Wrong type"); + break; + case NS_ERROR_DOM_INVALID_MODIFICATION_ERR: + aPromise->MaybeRejectWithInvalidModificationError("Invalid modification"); + break; + default: + if (NS_FAILED(aError)) { + aPromise->MaybeRejectWithUnknownError("Unknown failure"); + } else { + aPromise->MaybeResolveWithUndefined(); + } + break; + } +} + +bool MakeResolution(nsIGlobalObject* aGlobal, + FileSystemGetEntriesResponse&& aResponse, + const bool& /* aResult */, + RefPtr<FileSystemEntryMetadataArray>& aSink) { + // TODO: Add page size to FileSystemConstants, preallocate and handle overflow + const auto& listing = aResponse.get_FileSystemDirectoryListing(); + + for (const auto& it : listing.files()) { + aSink->AppendElement(it); + } + + for (const auto& it : listing.directories()) { + aSink->AppendElement(it); + } + + return true; +} + +RefPtr<FileSystemDirectoryHandle> MakeResolution( + nsIGlobalObject* aGlobal, FileSystemGetHandleResponse&& aResponse, + const RefPtr<FileSystemDirectoryHandle>& /* aResult */, + RefPtr<FileSystemManager>& aManager) { + RefPtr<FileSystemDirectoryHandle> result = new FileSystemDirectoryHandle( + aGlobal, aManager, + FileSystemEntryMetadata(aResponse.get_EntryId(), kRootName, + /* directory */ true)); + return result; +} + +RefPtr<FileSystemDirectoryHandle> MakeResolution( + nsIGlobalObject* aGlobal, FileSystemGetHandleResponse&& aResponse, + const RefPtr<FileSystemDirectoryHandle>& /* aResult */, const Name& aName, + RefPtr<FileSystemManager>& aManager) { + RefPtr<FileSystemDirectoryHandle> result = new FileSystemDirectoryHandle( + aGlobal, aManager, + FileSystemEntryMetadata(aResponse.get_EntryId(), aName, + /* directory */ true)); + + return result; +} + +RefPtr<FileSystemFileHandle> MakeResolution( + nsIGlobalObject* aGlobal, FileSystemGetHandleResponse&& aResponse, + const RefPtr<FileSystemFileHandle>& /* aResult */, const Name& aName, + RefPtr<FileSystemManager>& aManager) { + RefPtr<FileSystemFileHandle> result = new FileSystemFileHandle( + aGlobal, aManager, + FileSystemEntryMetadata(aResponse.get_EntryId(), aName, + /* directory */ false)); + return result; +} + +RefPtr<FileSystemSyncAccessHandle> MakeResolution( + nsIGlobalObject* aGlobal, FileSystemGetAccessHandleResponse&& aResponse, + const RefPtr<FileSystemSyncAccessHandle>& /* aReturns */, + const FileSystemEntryMetadata& aMetadata, + RefPtr<FileSystemManager>& aManager) { + auto& properties = aResponse.get_FileSystemAccessHandleProperties(); + + QM_TRY_UNWRAP( + RefPtr<FileSystemSyncAccessHandle> result, + FileSystemSyncAccessHandle::Create( + aGlobal, aManager, std::move(properties.streamParams()), + std::move(properties.accessHandleChildEndpoint()), + std::move(properties.accessHandleControlChildEndpoint()), aMetadata), + nullptr); + + return result; +} + +RefPtr<FileSystemWritableFileStream> MakeResolution( + nsIGlobalObject* aGlobal, + FileSystemGetWritableFileStreamResponse&& aResponse, + const RefPtr<FileSystemWritableFileStream>& /* aReturns */, + const FileSystemEntryMetadata& aMetadata, + RefPtr<FileSystemManager>& aManager) { + auto& properties = aResponse.get_FileSystemWritableFileStreamProperties(); + + auto* const actor = static_cast<FileSystemWritableFileStreamChild*>( + properties.writableFileStream().AsChild().get()); + + QM_TRY_UNWRAP(RefPtr<FileSystemWritableFileStream> result, + FileSystemWritableFileStream::Create( + aGlobal, aManager, std::move(properties.streamParams()), + actor, aMetadata), + nullptr); + + return result; +} + +RefPtr<File> MakeResolution(nsIGlobalObject* aGlobal, + FileSystemGetFileResponse&& aResponse, + const RefPtr<File>& /* aResult */, + const Name& aName, + RefPtr<FileSystemManager>& aManager) { + auto& fileProperties = aResponse.get_FileSystemFileProperties(); + + RefPtr<BlobImpl> blobImpl = IPCBlobUtils::Deserialize(fileProperties.file()); + MOZ_ASSERT(blobImpl); + RefPtr<File> result = File::Create(aGlobal, blobImpl); + return result; +} + +template <class TResponse, class... Args> +void ResolveCallback( + TResponse&& aResponse, + RefPtr<Promise> aPromise, // NOLINT(performance-unnecessary-value-param) + Args&&... args) { + MOZ_ASSERT(aPromise); + QM_TRY(OkIf(Promise::PromiseState::Pending == aPromise->State()), QM_VOID); + + if (TResponse::Tnsresult == aResponse.type()) { + HandleFailedStatus(aResponse.get_nsresult(), aPromise); + return; + } + + auto resolution = MakeResolution(aPromise->GetParentObject(), + std::forward<TResponse>(aResponse), + std::forward<Args>(args)...); + if (!resolution) { + aPromise->MaybeRejectWithUnknownError("Could not complete request"); + return; + } + + aPromise->MaybeResolve(resolution); +} + +template <> +void ResolveCallback( + FileSystemRemoveEntryResponse&& aResponse, + RefPtr<Promise> aPromise) { // NOLINT(performance-unnecessary-value-param) + MOZ_ASSERT(aPromise); + QM_TRY(OkIf(Promise::PromiseState::Pending == aPromise->State()), QM_VOID); + + if (FileSystemRemoveEntryResponse::Tvoid_t == aResponse.type()) { + aPromise->MaybeResolveWithUndefined(); + return; + } + + MOZ_ASSERT(FileSystemRemoveEntryResponse::Tnsresult == aResponse.type()); + HandleFailedStatus(aResponse.get_nsresult(), aPromise); +} + +template <> +void ResolveCallback( + FileSystemMoveEntryResponse&& aResponse, + RefPtr<Promise> aPromise, // NOLINT(performance-unnecessary-value-param) + FileSystemEntryMetadata* const& aEntry, const Name& aName) { + MOZ_ASSERT(aPromise); + QM_TRY(OkIf(Promise::PromiseState::Pending == aPromise->State()), QM_VOID); + + if (FileSystemMoveEntryResponse::TEntryId == aResponse.type()) { + if (aEntry) { + aEntry->entryId() = std::move(aResponse.get_EntryId()); + aEntry->entryName() = aName; + } + + aPromise->MaybeResolveWithUndefined(); + return; + } + MOZ_ASSERT(FileSystemMoveEntryResponse::Tnsresult == aResponse.type()); + const auto& status = aResponse.get_nsresult(); + MOZ_ASSERT(NS_FAILED(status)); + HandleFailedStatus(status, aPromise); +} + +template <> +void ResolveCallback(FileSystemResolveResponse&& aResponse, + // NOLINTNEXTLINE(performance-unnecessary-value-param) + RefPtr<Promise> aPromise) { + MOZ_ASSERT(aPromise); + QM_TRY(OkIf(Promise::PromiseState::Pending == aPromise->State()), QM_VOID); + + if (FileSystemResolveResponse::Tnsresult == aResponse.type()) { + HandleFailedStatus(aResponse.get_nsresult(), aPromise); + return; + } + + auto& maybePath = aResponse.get_MaybeFileSystemPath(); + if (maybePath.isSome()) { + aPromise->MaybeResolve(maybePath.value().path()); + return; + } + + // Spec says if there is no parent/child relationship, return null + aPromise->MaybeResolve(JS::NullHandleValue); +} + +template <class TResponse, class TReturns, class... Args, + std::enable_if_t<std::is_same<TReturns, void>::value, bool> = true> +mozilla::ipc::ResolveCallback<TResponse> SelectResolveCallback( + RefPtr<Promise> aPromise, // NOLINT(performance-unnecessary-value-param) + Args&&... args) { + using TOverload = void (*)(TResponse&&, RefPtr<Promise>, Args...); + return static_cast<std::function<void(TResponse&&)>>( + // NOLINTNEXTLINE(modernize-avoid-bind) + std::bind(static_cast<TOverload>(ResolveCallback), std::placeholders::_1, + aPromise, std::forward<Args>(args)...)); +} + +template <class TResponse, class TReturns, class... Args, + std::enable_if_t<!std::is_same<TReturns, void>::value, bool> = true> +mozilla::ipc::ResolveCallback<TResponse> SelectResolveCallback( + RefPtr<Promise> aPromise, // NOLINT(performance-unnecessary-value-param) + Args&&... args) { + using TOverload = + void (*)(TResponse&&, RefPtr<Promise>, const TReturns&, Args...); + return static_cast<std::function<void(TResponse&&)>>( + // NOLINTNEXTLINE(modernize-avoid-bind) + std::bind(static_cast<TOverload>(ResolveCallback), std::placeholders::_1, + aPromise, TReturns(), std::forward<Args>(args)...)); +} + +void RejectCallback( + RefPtr<Promise> aPromise, // NOLINT(performance-unnecessary-value-param) + mozilla::ipc::ResponseRejectReason aReason) { + IPCRejectReporter(aReason); + QM_TRY(OkIf(Promise::PromiseState::Pending == aPromise->State()), QM_VOID); + aPromise->MaybeRejectWithUndefined(); +} + +mozilla::ipc::RejectCallback GetRejectCallback( + RefPtr<Promise> aPromise) { // NOLINT(performance-unnecessary-value-param) + return static_cast<mozilla::ipc::RejectCallback>( + // NOLINTNEXTLINE(modernize-avoid-bind) + std::bind(RejectCallback, aPromise, std::placeholders::_1)); +} + +struct BeginRequestFailureCallback { + explicit BeginRequestFailureCallback(RefPtr<Promise> aPromise) + : mPromise(std::move(aPromise)) {} + + void operator()(nsresult aRv) const { + if (aRv == NS_ERROR_DOM_SECURITY_ERR) { + mPromise->MaybeRejectWithSecurityError( + "Security error when calling GetDirectory"); + return; + } + mPromise->MaybeRejectWithUnknownError("Could not create actor"); + } + + RefPtr<Promise> mPromise; +}; + +} // namespace + +void FileSystemRequestHandler::GetRootHandle( + RefPtr<FileSystemManager> + aManager, // NOLINT(performance-unnecessary-value-param) + RefPtr<Promise> aPromise, // NOLINT(performance-unnecessary-value-param) + ErrorResult& aError) { + MOZ_ASSERT(aManager); + MOZ_ASSERT(aPromise); + LOG(("GetRootHandle")); + + if (aManager->IsShutdown()) { + aError.Throw(NS_ERROR_ILLEGAL_DURING_SHUTDOWN); + return; + } + + aManager->BeginRequest( + [onResolve = SelectResolveCallback<FileSystemGetHandleResponse, + RefPtr<FileSystemDirectoryHandle>>( + aPromise, aManager), + onReject = GetRejectCallback(aPromise)](const auto& actor) mutable { + actor->SendGetRootHandle(std::move(onResolve), std::move(onReject)); + }, + BeginRequestFailureCallback(aPromise)); +} + +void FileSystemRequestHandler::GetDirectoryHandle( + RefPtr<FileSystemManager>& aManager, + const FileSystemChildMetadata& aDirectory, bool aCreate, + RefPtr<Promise> aPromise, // NOLINT(performance-unnecessary-value-param) + ErrorResult& aError) { + MOZ_ASSERT(aManager); + MOZ_ASSERT(!aDirectory.parentId().IsEmpty()); + MOZ_ASSERT(aPromise); + LOG(("GetDirectoryHandle")); + + if (aManager->IsShutdown()) { + aError.Throw(NS_ERROR_ILLEGAL_DURING_SHUTDOWN); + return; + } + + if (!IsValidName(aDirectory.childName())) { + aPromise->MaybeRejectWithTypeError("Invalid directory name"); + return; + } + + aManager->BeginRequest( + [request = FileSystemGetHandleRequest(aDirectory, aCreate), + onResolve = SelectResolveCallback<FileSystemGetHandleResponse, + RefPtr<FileSystemDirectoryHandle>>( + aPromise, aDirectory.childName(), aManager), + onReject = GetRejectCallback(aPromise)](const auto& actor) mutable { + actor->SendGetDirectoryHandle(request, std::move(onResolve), + std::move(onReject)); + }, + BeginRequestFailureCallback(aPromise)); +} + +void FileSystemRequestHandler::GetFileHandle( + RefPtr<FileSystemManager>& aManager, const FileSystemChildMetadata& aFile, + bool aCreate, + RefPtr<Promise> aPromise, // NOLINT(performance-unnecessary-value-param) + ErrorResult& aError) { + MOZ_ASSERT(aManager); + MOZ_ASSERT(!aFile.parentId().IsEmpty()); + MOZ_ASSERT(aPromise); + LOG(("GetFileHandle")); + + if (aManager->IsShutdown()) { + aError.Throw(NS_ERROR_ILLEGAL_DURING_SHUTDOWN); + return; + } + + if (!IsValidName(aFile.childName())) { + aPromise->MaybeRejectWithTypeError("Invalid filename"); + return; + } + + aManager->BeginRequest( + [request = FileSystemGetHandleRequest(aFile, aCreate), + onResolve = SelectResolveCallback<FileSystemGetHandleResponse, + RefPtr<FileSystemFileHandle>>( + aPromise, aFile.childName(), aManager), + onReject = GetRejectCallback(aPromise)](const auto& actor) mutable { + actor->SendGetFileHandle(request, std::move(onResolve), + std::move(onReject)); + }, + BeginRequestFailureCallback(aPromise)); +} + +void FileSystemRequestHandler::GetAccessHandle( + RefPtr<FileSystemManager>& aManager, const FileSystemEntryMetadata& aFile, + const RefPtr<Promise>& aPromise, ErrorResult& aError) { + MOZ_ASSERT(aManager); + MOZ_ASSERT(aPromise); + LOG(("GetAccessHandle %s", NS_ConvertUTF16toUTF8(aFile.entryName()).get())); + + if (aManager->IsShutdown()) { + aError.Throw(NS_ERROR_ILLEGAL_DURING_SHUTDOWN); + return; + } + + aManager->BeginRequest( + [request = FileSystemGetAccessHandleRequest(aFile.entryId()), + onResolve = SelectResolveCallback<FileSystemGetAccessHandleResponse, + RefPtr<FileSystemSyncAccessHandle>>( + aPromise, aFile, aManager), + onReject = GetRejectCallback(aPromise)](const auto& actor) mutable { + actor->SendGetAccessHandle(request, std::move(onResolve), + std::move(onReject)); + }, + BeginRequestFailureCallback(aPromise)); +} + +void FileSystemRequestHandler::GetWritable(RefPtr<FileSystemManager>& aManager, + const FileSystemEntryMetadata& aFile, + bool aKeepData, + const RefPtr<Promise>& aPromise, + ErrorResult& aError) { + MOZ_ASSERT(aManager); + MOZ_ASSERT(aPromise); + LOG(("GetWritable %s keep %d", NS_ConvertUTF16toUTF8(aFile.entryName()).get(), + aKeepData)); + + // XXX This should be removed once bug 1798513 is fixed. + if (!StaticPrefs::dom_fs_writable_file_stream_enabled()) { + aError.Throw(NS_ERROR_NOT_IMPLEMENTED); + return; + } + + if (aManager->IsShutdown()) { + aError.Throw(NS_ERROR_ILLEGAL_DURING_SHUTDOWN); + return; + } + + aManager->BeginRequest( + [request = FileSystemGetWritableRequest(aFile.entryId(), aKeepData), + onResolve = + SelectResolveCallback<FileSystemGetWritableFileStreamResponse, + RefPtr<FileSystemWritableFileStream>>( + aPromise, aFile, aManager), + onReject = GetRejectCallback(aPromise)](const auto& actor) mutable { + actor->SendGetWritable(request, std::move(onResolve), + std::move(onReject)); + }, + [promise = aPromise](const auto&) { + promise->MaybeRejectWithUnknownError("Could not create actor"); + }); +} + +void FileSystemRequestHandler::GetFile( + RefPtr<FileSystemManager>& aManager, const FileSystemEntryMetadata& aFile, + RefPtr<Promise> aPromise, // NOLINT(performance-unnecessary-value-param) + ErrorResult& aError) { + MOZ_ASSERT(aManager); + MOZ_ASSERT(!aFile.entryId().IsEmpty()); + MOZ_ASSERT(aPromise); + LOG(("GetFile %s", NS_ConvertUTF16toUTF8(aFile.entryName()).get())); + + if (aManager->IsShutdown()) { + aError.Throw(NS_ERROR_ILLEGAL_DURING_SHUTDOWN); + return; + } + + aManager->BeginRequest( + [request = FileSystemGetFileRequest(aFile.entryId()), + onResolve = + SelectResolveCallback<FileSystemGetFileResponse, RefPtr<File>>( + aPromise, aFile.entryName(), aManager), + onReject = GetRejectCallback(aPromise)](const auto& actor) mutable { + actor->SendGetFile(request, std::move(onResolve), std::move(onReject)); + }, + BeginRequestFailureCallback(aPromise)); +} + +void FileSystemRequestHandler::GetEntries( + RefPtr<FileSystemManager>& aManager, const EntryId& aDirectory, + PageNumber aPage, + RefPtr<Promise> aPromise, // NOLINT(performance-unnecessary-value-param) + RefPtr<FileSystemEntryMetadataArray>& aSink, ErrorResult& aError) { + MOZ_ASSERT(aManager); + MOZ_ASSERT(!aDirectory.IsEmpty()); + MOZ_ASSERT(aPromise); + LOG(("GetEntries, page %u", aPage)); + + if (aManager->IsShutdown()) { + aError.Throw(NS_ERROR_ILLEGAL_DURING_SHUTDOWN); + return; + } + + aManager->BeginRequest( + [request = FileSystemGetEntriesRequest(aDirectory, aPage), + onResolve = SelectResolveCallback<FileSystemGetEntriesResponse, bool>( + aPromise, aSink), + onReject = GetRejectCallback(aPromise)](const auto& actor) mutable { + actor->SendGetEntries(request, std::move(onResolve), + std::move(onReject)); + }, + BeginRequestFailureCallback(aPromise)); +} + +void FileSystemRequestHandler::RemoveEntry( + RefPtr<FileSystemManager>& aManager, const FileSystemChildMetadata& aEntry, + bool aRecursive, + RefPtr<Promise> aPromise, // NOLINT(performance-unnecessary-value-param) + ErrorResult& aError) { + MOZ_ASSERT(aManager); + MOZ_ASSERT(!aEntry.parentId().IsEmpty()); + MOZ_ASSERT(aPromise); + LOG(("RemoveEntry")); + + if (aManager->IsShutdown()) { + aError.Throw(NS_ERROR_ILLEGAL_DURING_SHUTDOWN); + return; + } + + if (!IsValidName(aEntry.childName())) { + aPromise->MaybeRejectWithTypeError("Invalid name"); + return; + } + + aManager->BeginRequest( + [request = FileSystemRemoveEntryRequest(aEntry, aRecursive), + onResolve = + SelectResolveCallback<FileSystemRemoveEntryResponse, void>(aPromise), + onReject = GetRejectCallback(aPromise)](const auto& actor) mutable { + actor->SendRemoveEntry(request, std::move(onResolve), + std::move(onReject)); + }, + BeginRequestFailureCallback(aPromise)); +} + +void FileSystemRequestHandler::MoveEntry( + RefPtr<FileSystemManager>& aManager, FileSystemHandle* aHandle, + FileSystemEntryMetadata* const aEntry, + const FileSystemChildMetadata& aNewEntry, + RefPtr<Promise> aPromise, // NOLINT(performance-unnecessary-value-param) + ErrorResult& aError) { + MOZ_ASSERT(aEntry); + MOZ_ASSERT(!aEntry->entryId().IsEmpty()); + MOZ_ASSERT(aPromise); + LOG(("MoveEntry")); + + if (aManager->IsShutdown()) { + aError.Throw(NS_ERROR_ILLEGAL_DURING_SHUTDOWN); + return; + } + + // reject invalid names: empty, path separators, current & parent directories + if (!IsValidName(aNewEntry.childName())) { + aPromise->MaybeRejectWithTypeError("Invalid name"); + return; + } + + aManager->BeginRequest( + [request = FileSystemMoveEntryRequest(*aEntry, aNewEntry), + onResolve = SelectResolveCallback<FileSystemMoveEntryResponse, void>( + aPromise, aEntry, aNewEntry.childName()), + onReject = GetRejectCallback(aPromise)](const auto& actor) mutable { + actor->SendMoveEntry(request, std::move(onResolve), + std::move(onReject)); + }, + BeginRequestFailureCallback(aPromise)); +} + +void FileSystemRequestHandler::RenameEntry( + RefPtr<FileSystemManager>& aManager, FileSystemHandle* aHandle, + FileSystemEntryMetadata* const aEntry, const Name& aName, + RefPtr<Promise> aPromise, // NOLINT(performance-unnecessary-value-param) + ErrorResult& aError) { + MOZ_ASSERT(aEntry); + MOZ_ASSERT(!aEntry->entryId().IsEmpty()); + MOZ_ASSERT(aPromise); + LOG(("RenameEntry")); + + if (aManager->IsShutdown()) { + aError.Throw(NS_ERROR_ILLEGAL_DURING_SHUTDOWN); + return; + } + + // reject invalid names: empty, path separators, current & parent directories + if (!IsValidName(aName)) { + aPromise->MaybeRejectWithTypeError("Invalid name"); + return; + } + + aManager->BeginRequest( + [request = FileSystemRenameEntryRequest(*aEntry, aName), + onResolve = SelectResolveCallback<FileSystemMoveEntryResponse, void>( + aPromise, aEntry, aName), + onReject = GetRejectCallback(aPromise)](const auto& actor) mutable { + actor->SendRenameEntry(request, std::move(onResolve), + std::move(onReject)); + }, + BeginRequestFailureCallback(aPromise)); +} + +void FileSystemRequestHandler::Resolve( + RefPtr<FileSystemManager>& aManager, + // NOLINTNEXTLINE(performance-unnecessary-value-param) + const FileSystemEntryPair& aEndpoints, RefPtr<Promise> aPromise, + ErrorResult& aError) { + MOZ_ASSERT(aManager); + MOZ_ASSERT(!aEndpoints.parentId().IsEmpty()); + MOZ_ASSERT(!aEndpoints.childId().IsEmpty()); + MOZ_ASSERT(aPromise); + LOG(("Resolve")); + + if (aManager->IsShutdown()) { + aError.Throw(NS_ERROR_ILLEGAL_DURING_SHUTDOWN); + return; + } + + aManager->BeginRequest( + [request = FileSystemResolveRequest(aEndpoints), + onResolve = + SelectResolveCallback<FileSystemResolveResponse, void>(aPromise), + onReject = GetRejectCallback(aPromise)](const auto& actor) mutable { + actor->SendResolve(request, std::move(onResolve), std::move(onReject)); + }, + BeginRequestFailureCallback(aPromise)); +} + +} // namespace mozilla::dom::fs diff --git a/dom/fs/child/FileSystemShutdownBlocker.cpp b/dom/fs/child/FileSystemShutdownBlocker.cpp new file mode 100644 index 0000000000..bddbf18c6b --- /dev/null +++ b/dom/fs/child/FileSystemShutdownBlocker.cpp @@ -0,0 +1,167 @@ +/* -*- 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 "fs/FileSystemShutdownBlocker.h" + +#include "MainThreadUtils.h" +#include "mozilla/Services.h" +#include "mozilla/dom/quota/QuotaCommon.h" +#include "mozilla/dom/quota/ResultExtensions.h" +#include "nsComponentManagerUtils.h" +#include "nsIAsyncShutdown.h" +#include "nsISupportsImpl.h" +#include "nsIWritablePropertyBag2.h" +#include "nsStringFwd.h" + +namespace mozilla::dom::fs { + +namespace { + +nsString CreateBlockerName() { + const int32_t blockerIdLength = 32; + nsAutoCString blockerId; + blockerId.SetLength(blockerIdLength); + NS_MakeRandomString(blockerId.BeginWriting(), blockerIdLength); + + nsString blockerName = u"OPFS_"_ns; + blockerName.Append(NS_ConvertUTF8toUTF16(blockerId)); + + return blockerName; +} + +class FileSystemWritableBlocker : public FileSystemShutdownBlocker { + public: + FileSystemWritableBlocker() : mName(CreateBlockerName()) {} + + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_NSIASYNCSHUTDOWNBLOCKER + + NS_IMETHODIMP Block() override; + + NS_IMETHODIMP Unblock() override; + + protected: + virtual ~FileSystemWritableBlocker() = default; + + Result<already_AddRefed<nsIAsyncShutdownClient>, nsresult> GetBarrier() const; + + private: + const nsString mName; +}; + +NS_IMPL_ISUPPORTS_INHERITED(FileSystemWritableBlocker, + FileSystemShutdownBlocker, nsIAsyncShutdownBlocker) + +NS_IMETHODIMP FileSystemWritableBlocker::Block() { + MOZ_ASSERT(NS_IsMainThread()); + QM_TRY_UNWRAP(nsCOMPtr<nsIAsyncShutdownClient> barrier, GetBarrier()); + + QM_TRY(MOZ_TO_RESULT(barrier->AddBlocker( + this, NS_ConvertUTF8toUTF16(nsCString(__FILE__)), __LINE__, + NS_ConvertUTF8toUTF16(nsCString(__func__))))); + + return NS_OK; +} + +NS_IMETHODIMP FileSystemWritableBlocker::Unblock() { + MOZ_ASSERT(NS_IsMainThread()); + QM_TRY_UNWRAP(nsCOMPtr<nsIAsyncShutdownClient> barrier, GetBarrier()); + + MOZ_ASSERT(NS_SUCCEEDED(barrier->RemoveBlocker(this))); + + return NS_OK; +} + +Result<already_AddRefed<nsIAsyncShutdownClient>, nsresult> +FileSystemWritableBlocker::GetBarrier() const { + nsCOMPtr<nsIAsyncShutdownService> svc = services::GetAsyncShutdownService(); + QM_TRY(OkIf(svc), Err(NS_ERROR_FAILURE)); + + nsCOMPtr<nsIAsyncShutdownClient> barrier; + QM_TRY(MOZ_TO_RESULT(svc->GetXpcomWillShutdown(getter_AddRefs(barrier)))); + + return barrier.forget(); +} + +NS_IMETHODIMP +FileSystemWritableBlocker::GetName(nsAString& aName) { + aName = mName; + + return NS_OK; +} + +NS_IMETHODIMP +FileSystemWritableBlocker::GetState(nsIPropertyBag** aBagOut) { + MOZ_ASSERT(aBagOut); + + nsCOMPtr<nsIWritablePropertyBag2> propertyBag = + do_CreateInstance("@mozilla.org/hash-property-bag;1"); + + QM_TRY(OkIf(propertyBag), NS_ERROR_OUT_OF_MEMORY) + + propertyBag.forget(aBagOut); + + return NS_OK; +} + +NS_IMETHODIMP +FileSystemWritableBlocker::BlockShutdown( + nsIAsyncShutdownClient* /* aBarrier */) { + MOZ_ASSERT(NS_IsMainThread()); + + return NS_OK; +} + +class FileSystemNullBlocker : public FileSystemShutdownBlocker { + public: + NS_IMETHODIMP Block() override { return NS_OK; } + + NS_IMETHODIMP Unblock() override { return NS_OK; } + + protected: + virtual ~FileSystemNullBlocker() = default; +}; + +} // namespace + +/* static */ +already_AddRefed<FileSystemShutdownBlocker> +FileSystemShutdownBlocker::CreateForWritable() { +// The shutdown blocker watches for xpcom-will-shutdown which is not fired +// during content process shutdown in release builds. +#ifdef DEBUG + if (NS_IsMainThread()) { + RefPtr<FileSystemShutdownBlocker> shutdownBlocker = + new FileSystemWritableBlocker(); + + return shutdownBlocker.forget(); + } +#endif + + RefPtr<FileSystemShutdownBlocker> shutdownBlocker = + new FileSystemNullBlocker(); + + return shutdownBlocker.forget(); +} + +NS_IMPL_ISUPPORTS(FileSystemShutdownBlocker, nsIAsyncShutdownBlocker) + +/* nsIAsyncShutdownBlocker methods */ +NS_IMETHODIMP +FileSystemShutdownBlocker::GetName(nsAString& /* aName */) { return NS_OK; } + +NS_IMETHODIMP +FileSystemShutdownBlocker::GetState(nsIPropertyBag** /* aBagOut */) { + return NS_OK; +} + +NS_IMETHODIMP +FileSystemShutdownBlocker::BlockShutdown( + nsIAsyncShutdownClient* /* aBarrier */) { + return NS_OK; +} + +} // namespace mozilla::dom::fs diff --git a/dom/fs/child/FileSystemThreadSafeStreamOwner.cpp b/dom/fs/child/FileSystemThreadSafeStreamOwner.cpp new file mode 100644 index 0000000000..2b2d2913d9 --- /dev/null +++ b/dom/fs/child/FileSystemThreadSafeStreamOwner.cpp @@ -0,0 +1,104 @@ +/* -*- 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 "fs/FileSystemThreadSafeStreamOwner.h" + +#include "mozilla/CheckedInt.h" +#include "mozilla/dom/FileSystemLog.h" +#include "mozilla/dom/FileSystemWritableFileStream.h" +#include "mozilla/dom/quota/QuotaCommon.h" +#include "mozilla/dom/quota/ResultExtensions.h" +#include "nsIOutputStream.h" +#include "nsIRandomAccessStream.h" + +namespace mozilla::dom::fs { + +namespace { + +nsresult TruncFile(nsCOMPtr<nsIRandomAccessStream>& aStream, int64_t aEOF) { + int64_t offset = 0; + QM_TRY(MOZ_TO_RESULT(aStream->Tell(&offset))); + + QM_TRY(MOZ_TO_RESULT(aStream->Seek(nsISeekableStream::NS_SEEK_SET, aEOF))); + + QM_TRY(MOZ_TO_RESULT(aStream->SetEOF())); + + QM_TRY(MOZ_TO_RESULT(aStream->Seek(nsISeekableStream::NS_SEEK_END, 0))); + + // Restore original offset + QM_TRY(MOZ_TO_RESULT(aStream->Seek(nsISeekableStream::NS_SEEK_SET, offset))); + + return NS_OK; +} + +} // namespace + +FileSystemThreadSafeStreamOwner::FileSystemThreadSafeStreamOwner( + FileSystemWritableFileStream* aWritableFileStream, + nsCOMPtr<nsIRandomAccessStream>&& aStream) + : +#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED + mWritableFileStream(aWritableFileStream), +#endif + mStream(std::forward<nsCOMPtr<nsIRandomAccessStream>>(aStream)), + mClosed(false) { + MOZ_ASSERT(mWritableFileStream); +} + +nsresult FileSystemThreadSafeStreamOwner::Truncate(uint64_t aSize) { + MOZ_DIAGNOSTIC_ASSERT(mWritableFileStream->IsCommandActive()); + + if (mClosed) { // Multiple closes can end up in a queue + return NS_ERROR_DOM_INVALID_STATE_ERR; + } + + int64_t where = 0; + QM_TRY(MOZ_TO_RESULT(mStream->Tell(&where))); + + // Truncate filehandle (can extend with 0's) + LOG(("%p: Truncate to %" PRIu64, this, aSize)); + QM_TRY(MOZ_TO_RESULT(TruncFile(mStream, aSize))); + + // Per non-normative text in the spec (2.5.3) we should adjust + // the cursor position to be within the new file size + if (static_cast<uint64_t>(where) > aSize) { + QM_TRY(MOZ_TO_RESULT(mStream->Seek(nsISeekableStream::NS_SEEK_END, 0))); + } + + return NS_OK; +} + +nsresult FileSystemThreadSafeStreamOwner::Seek(uint64_t aPosition) { + MOZ_DIAGNOSTIC_ASSERT(mWritableFileStream->IsCommandActive()); + + if (mClosed) { // Multiple closes can end up in a queue + return NS_ERROR_DOM_INVALID_STATE_ERR; + } + + const auto checkedPosition = CheckedInt<int64_t>(aPosition); + if (!checkedPosition.isValid()) { + return NS_ERROR_INVALID_ARG; + } + + return mStream->Seek(nsISeekableStream::NS_SEEK_SET, checkedPosition.value()); +} + +void FileSystemThreadSafeStreamOwner::Close() { + if (mClosed) { // Multiple closes can end up in a queue + return; + } + + mClosed = true; + mStream->OutputStream()->Close(); +} + +nsCOMPtr<nsIOutputStream> FileSystemThreadSafeStreamOwner::OutputStream() { + MOZ_DIAGNOSTIC_ASSERT(mWritableFileStream->IsCommandActive()); + + return mStream->OutputStream(); +} + +} // namespace mozilla::dom::fs diff --git a/dom/fs/child/FileSystemWritableFileStreamChild.cpp b/dom/fs/child/FileSystemWritableFileStreamChild.cpp new file mode 100644 index 0000000000..862e8f345e --- /dev/null +++ b/dom/fs/child/FileSystemWritableFileStreamChild.cpp @@ -0,0 +1,39 @@ +/* -*- 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 "FileSystemWritableFileStreamChild.h" + +#include "mozilla/dom/FileSystemLog.h" +#include "mozilla/dom/FileSystemWritableFileStream.h" + +namespace mozilla::dom { + +FileSystemWritableFileStreamChild::FileSystemWritableFileStreamChild() + : mStream(nullptr) { + LOG(("Created new WritableFileStreamChild %p", this)); +} + +FileSystemWritableFileStreamChild::~FileSystemWritableFileStreamChild() = + default; + +void FileSystemWritableFileStreamChild::SetStream( + FileSystemWritableFileStream* aStream) { + MOZ_ASSERT(aStream); + MOZ_ASSERT(!mStream); + + mStream = aStream; +} + +void FileSystemWritableFileStreamChild::ActorDestroy(ActorDestroyReason aWhy) { + LOG(("Destroy WritableFileStreamChild %p", this)); + + if (mStream) { + mStream->ClearActor(); + mStream = nullptr; + } +} + +} // namespace mozilla::dom diff --git a/dom/fs/child/FileSystemWritableFileStreamChild.h b/dom/fs/child/FileSystemWritableFileStreamChild.h new file mode 100644 index 0000000000..9728fc4c78 --- /dev/null +++ b/dom/fs/child/FileSystemWritableFileStreamChild.h @@ -0,0 +1,42 @@ +/* -*- 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/. */ + +#ifndef DOM_FS_CHILD_FILESYSTEMWRITABLEFILESTREAM_H_ +#define DOM_FS_CHILD_FILESYSTEMWRITABLEFILESTREAM_H_ + +#include "mozilla/dom/PFileSystemWritableFileStreamChild.h" + +namespace mozilla::dom { + +class FileSystemWritableFileStream; + +class FileSystemWritableFileStreamChild + : public PFileSystemWritableFileStreamChild { + public: + FileSystemWritableFileStreamChild(); + + NS_INLINE_DECL_REFCOUNTING(FileSystemWritableFileStreamChild, override) + + FileSystemWritableFileStream* MutableWritableFileStreamPtr() const { + MOZ_ASSERT(mStream); + return mStream; + } + + void SetStream(FileSystemWritableFileStream* aStream); + + void ActorDestroy(ActorDestroyReason aWhy) override; + + private: + virtual ~FileSystemWritableFileStreamChild(); + + // Use a weak ref so actor does not hold DOM object alive past content use. + // The weak reference is cleared in FileSystemWritableFileStream::LastRelease. + FileSystemWritableFileStream* MOZ_NON_OWNING_REF mStream; +}; + +} // namespace mozilla::dom + +#endif // DOM_FS_CHILD_FILESYSTEMWRITABLEFILESTREAM_H_ diff --git a/dom/fs/child/moz.build b/dom/fs/child/moz.build new file mode 100644 index 0000000000..e52cb6b206 --- /dev/null +++ b/dom/fs/child/moz.build @@ -0,0 +1,35 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +EXPORTS.mozilla.dom += [ + "FileSystemAccessHandleChild.h", + "FileSystemAccessHandleControlChild.h", + "FileSystemManagerChild.h", + "FileSystemWritableFileStreamChild.h", +] + +UNIFIED_SOURCES += [ + "FileSystemAccessHandleChild.cpp", + "FileSystemAccessHandleControlChild.cpp", + "FileSystemAsyncCopy.cpp", + "FileSystemBackgroundRequestHandler.cpp", + "FileSystemChildFactory.cpp", + "FileSystemDirectoryIteratorFactory.cpp", + "FileSystemManagerChild.cpp", + "FileSystemRequestHandler.cpp", + "FileSystemShutdownBlocker.cpp", + "FileSystemThreadSafeStreamOwner.cpp", + "FileSystemWritableFileStreamChild.cpp", +] + +LOCAL_INCLUDES += [ + "/dom/fs/include", + "/netwerk/base", +] + +FINAL_LIBRARY = "xul" + +include("/ipc/chromium/chromium-config.mozbuild") |