diff options
Diffstat (limited to 'dom/fs/child')
-rw-r--r-- | dom/fs/child/FileSystemAccessHandleChild.cpp | 34 | ||||
-rw-r--r-- | dom/fs/child/FileSystemAccessHandleChild.h | 41 | ||||
-rw-r--r-- | dom/fs/child/FileSystemBackgroundRequestHandler.cpp | 142 | ||||
-rw-r--r-- | dom/fs/child/FileSystemBackgroundRequestHandler.h | 73 | ||||
-rw-r--r-- | dom/fs/child/FileSystemChildFactory.cpp | 19 | ||||
-rw-r--r-- | dom/fs/child/FileSystemDirectoryIteratorFactory.cpp | 238 | ||||
-rw-r--r-- | dom/fs/child/FileSystemDirectoryIteratorFactory.h | 24 | ||||
-rw-r--r-- | dom/fs/child/FileSystemEntryMetadataArray.h | 26 | ||||
-rw-r--r-- | dom/fs/child/FileSystemManagerChild.cpp | 103 | ||||
-rw-r--r-- | dom/fs/child/FileSystemManagerChild.h | 58 | ||||
-rw-r--r-- | dom/fs/child/FileSystemRequestHandler.cpp | 648 | ||||
-rw-r--r-- | dom/fs/child/FileSystemWritableFileStreamChild.cpp | 39 | ||||
-rw-r--r-- | dom/fs/child/FileSystemWritableFileStreamChild.h | 42 | ||||
-rw-r--r-- | dom/fs/child/moz.build | 29 |
14 files changed, 1516 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/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..5bb3e13255 --- /dev/null +++ b/dom/fs/child/FileSystemDirectoryIteratorFactory.cpp @@ -0,0 +1,238 @@ +/* -*- 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.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()), + mData(), + 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(); + } + + ~DoubleBufferQueueImpl() = default; + + protected: + 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>(); + + RefPtr<DomPromiseListener> listener = new DomPromiseListener( + [global = nsCOMPtr<nsIGlobalObject>(aGlobal), + manager = RefPtr<FileSystemManager>(aManager), newPage, aResult, + this](JSContext* aCx, JS::Handle<JS::Value> aValue) mutable { + MOZ_ASSERT(0u == mWithinPageIndex); + + // XXX Do we need this extra copy ? + nsTArray<DataType> batch; + for (const auto& it : *newPage) { + batch.AppendElement(it); + } + + const size_t batchSize = std::min(PageSize, newPage->Length()); + mData.InsertElementsAt( + PageSize * static_cast<size_t>(!mCurrentPageIsLastPage), + batch.Elements(), batchSize); + mWithinPageEnd += batchSize; + + Maybe<DataType> value; + if (0 != newPage->Length()) { + nextInternal(value); + } + + ResolveValue(global, manager, value, aResult); + }, + [aResult](nsresult aRv) { aResult->MaybeReject(aRv); }); + promise->AppendNativeHandler(listener); + + 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 auto previous = + static_cast<size_t>(!mCurrentPageIsLastPage) * PageSize + + mWithinPageIndex; + 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 + +UniquePtr<mozilla::dom::FileSystemDirectoryIterator::Impl> +FileSystemDirectoryIteratorFactory::Create( + const FileSystemEntryMetadata& aMetadata, + IterableIteratorBase::IteratorType aType) { + if (IterableIteratorBase::Entries == aType) { + return MakeUnique<UnderlyingQueue<IterableIteratorBase::Entries>>( + aMetadata); + } + + if (IterableIteratorBase::Values == aType) { + return MakeUnique<UnderlyingQueue<IterableIteratorBase::Values>>(aMetadata); + } + + return MakeUnique<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..278f0a9ef0 --- /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 UniquePtr<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..045f50cf7f --- /dev/null +++ b/dom/fs/child/FileSystemManagerChild.cpp @@ -0,0 +1,103 @@ +/* -*- 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; +} + +#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; +} +#endif + +void FileSystemManagerChild::CloseAllWritableFileStreams() { + for (const auto& item : ManagedPFileSystemWritableFileStreamChild()) { + auto* child = static_cast<FileSystemWritableFileStreamChild*>(item); + + child->MutableWritableFileStreamPtr()->Close(); + } +} + +void FileSystemManagerChild::Shutdown() { + if (!CanSend()) { + return; + } + + Close(); +} + +already_AddRefed<PFileSystemAccessHandleChild> +FileSystemManagerChild::AllocPFileSystemAccessHandleChild() { + return MakeAndAddRef<FileSystemAccessHandleChild>(); +} + +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()); + } + } + + CloseAllWritableFileStreams(); + + 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; + } +} + +} // namespace mozilla::dom diff --git a/dom/fs/child/FileSystemManagerChild.h b/dom/fs/child/FileSystemManagerChild.h new file mode 100644 index 0000000000..7000dfab67 --- /dev/null +++ b/dom/fs/child/FileSystemManagerChild.h @@ -0,0 +1,58 @@ +/* -*- 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/PFileSystemManagerChild.h" +#include "nsISupportsImpl.h" + +namespace mozilla::dom { + +class FileSystemBackgroundRequestHandler; + +class FileSystemManagerChild : public PFileSystemManagerChild { + public: + NS_INLINE_DECL_REFCOUNTING_WITH_DESTROY(FileSystemManagerChild, Destroy(), + override) + + void SetBackgroundRequestHandler( + FileSystemBackgroundRequestHandler* aBackgroundRequestHandler); + +#ifdef DEBUG + virtual bool AllSyncAccessHandlesClosed() const; +#endif + + virtual void CloseAllWritableFileStreams(); + + virtual void Shutdown(); + + already_AddRefed<PFileSystemAccessHandleChild> + AllocPFileSystemAccessHandleChild(); + + 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; +}; + +} // 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..87c06e409a --- /dev/null +++ b/dom/fs/child/FileSystemRequestHandler.cpp @@ -0,0 +1,648 @@ +/* -*- 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/quota/QuotaCommon.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_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(); + + auto* const actor = + static_cast<FileSystemAccessHandleChild*>(properties.accessHandleChild()); + + QM_TRY_UNWRAP(RefPtr<FileSystemSyncAccessHandle> result, + FileSystemSyncAccessHandle::Create( + aGlobal, aManager, actor, + std::move(properties.streamParams()), aMetadata), + nullptr); + + return result; +} + +RefPtr<FileSystemWritableFileStream> MakeResolution( + nsIGlobalObject* aGlobal, + FileSystemGetWritableFileStreamResponse&& aResponse, + const RefPtr<FileSystemWritableFileStream>& /* aReturns */, + const FileSystemEntryMetadata& aMetadata, + RefPtr<FileSystemManager>& aManager) { + const auto& properties = + aResponse.get_FileSystemWritableFileStreamProperties(); + + auto* const actor = static_cast<FileSystemWritableFileStreamChild*>( + properties.writableFileStreamChild()); + + RefPtr<FileSystemWritableFileStream> result = + FileSystemWritableFileStream::Create( + aGlobal, aManager, actor, properties.fileDescriptor(), aMetadata); + + 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) + MOZ_ASSERT(aPromise); + QM_TRY(OkIf(Promise::PromiseState::Pending == aPromise->State()), QM_VOID); + + MOZ_ASSERT(FileSystemMoveEntryResponse::Tnsresult == aResponse.type()); + const auto& status = aResponse.get_nsresult(); + if (NS_OK == status) { + aPromise->MaybeResolveWithUndefined(); + return; + } + 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)...)); +} + +// TODO: Find a better way to deal with these errors +void IPCRejectReporter(mozilla::ipc::ResponseRejectReason aReason) { + switch (aReason) { + case mozilla::ipc::ResponseRejectReason::ActorDestroyed: + // This is ok + break; + case mozilla::ipc::ResponseRejectReason::HandlerRejected: + QM_TRY(OkIf(false), QM_VOID); + break; + case mozilla::ipc::ResponseRejectReason::ChannelClosed: + QM_TRY(OkIf(false), QM_VOID); + break; + case mozilla::ipc::ResponseRejectReason::ResolverDestroyed: + QM_TRY(OkIf(false), QM_VOID); + break; + case mozilla::ipc::ResponseRejectReason::SendError: + QM_TRY(OkIf(false), QM_VOID); + break; + default: + QM_TRY(OkIf(false), QM_VOID); + break; + } +} + +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, + const FileSystemEntryMetadata& aEntry, + const FileSystemChildMetadata& aNewEntry, + RefPtr<Promise> aPromise, // NOLINT(performance-unnecessary-value-param) + ErrorResult& aError) { + 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), + 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, + const FileSystemEntryMetadata& aEntry, const Name& aName, + RefPtr<Promise> aPromise, // NOLINT(performance-unnecessary-value-param) + ErrorResult& aError) { + 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), + 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/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..7cff598101 --- /dev/null +++ b/dom/fs/child/moz.build @@ -0,0 +1,29 @@ +# -*- 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", + "FileSystemManagerChild.h", + "FileSystemWritableFileStreamChild.h", +] + +UNIFIED_SOURCES += [ + "FileSystemAccessHandleChild.cpp", + "FileSystemBackgroundRequestHandler.cpp", + "FileSystemChildFactory.cpp", + "FileSystemDirectoryIteratorFactory.cpp", + "FileSystemManagerChild.cpp", + "FileSystemRequestHandler.cpp", + "FileSystemWritableFileStreamChild.cpp", +] + +LOCAL_INCLUDES += [ + "/dom/fs/include", +] + +FINAL_LIBRARY = "xul" + +include("/ipc/chromium/chromium-config.mozbuild") |