summaryrefslogtreecommitdiffstats
path: root/dom/fs/child
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 01:47:29 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 01:47:29 +0000
commit0ebf5bdf043a27fd3dfb7f92e0cb63d88954c44d (patch)
treea31f07c9bcca9d56ce61e9a1ffd30ef350d513aa /dom/fs/child
parentInitial commit. (diff)
downloadfirefox-esr-0ebf5bdf043a27fd3dfb7f92e0cb63d88954c44d.tar.xz
firefox-esr-0ebf5bdf043a27fd3dfb7f92e0cb63d88954c44d.zip
Adding upstream version 115.8.0esr.upstream/115.8.0esr
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'dom/fs/child')
-rw-r--r--dom/fs/child/FileSystemAccessHandleChild.cpp34
-rw-r--r--dom/fs/child/FileSystemAccessHandleChild.h41
-rw-r--r--dom/fs/child/FileSystemAccessHandleControlChild.cpp37
-rw-r--r--dom/fs/child/FileSystemAccessHandleControlChild.h47
-rw-r--r--dom/fs/child/FileSystemAsyncCopy.cpp66
-rw-r--r--dom/fs/child/FileSystemBackgroundRequestHandler.cpp142
-rw-r--r--dom/fs/child/FileSystemBackgroundRequestHandler.h73
-rw-r--r--dom/fs/child/FileSystemChildFactory.cpp19
-rw-r--r--dom/fs/child/FileSystemDirectoryIteratorFactory.cpp237
-rw-r--r--dom/fs/child/FileSystemDirectoryIteratorFactory.h24
-rw-r--r--dom/fs/child/FileSystemEntryMetadataArray.h26
-rw-r--r--dom/fs/child/FileSystemManagerChild.cpp131
-rw-r--r--dom/fs/child/FileSystemManagerChild.h62
-rw-r--r--dom/fs/child/FileSystemRequestHandler.cpp631
-rw-r--r--dom/fs/child/FileSystemShutdownBlocker.cpp167
-rw-r--r--dom/fs/child/FileSystemThreadSafeStreamOwner.cpp104
-rw-r--r--dom/fs/child/FileSystemWritableFileStreamChild.cpp39
-rw-r--r--dom/fs/child/FileSystemWritableFileStreamChild.h42
-rw-r--r--dom/fs/child/moz.build35
19 files changed, 1957 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..26b605f5b6
--- /dev/null
+++ b/dom/fs/child/FileSystemDirectoryIteratorFactory.cpp
@@ -0,0 +1,237 @@
+/* -*- 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);
+ MOZ_ASSERT(newPage->Length() <= PageSize);
+
+ const size_t startPos = mCurrentPageIsLastPage ? 0u : PageSize;
+ if (mData.Length() < 2 * PageSize) {
+ mData.InsertElementsAt(startPos, newPage->Elements(),
+ newPage->Length());
+ } else {
+ mData.ReplaceElementsAt(startPos, newPage->Length(),
+ newPage->Elements(), newPage->Length());
+ }
+ MOZ_ASSERT(mData.Length() <= 2 * PageSize);
+ mWithinPageEnd = newPage->Length();
+
+ 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 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
+
+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..7a86ac6628
--- /dev/null
+++ b/dom/fs/child/FileSystemManagerChild.cpp
@@ -0,0 +1,131 @@
+/* -*- 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->IsClosed()) {
+ 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 && !handle->IsClosed()) {
+ aPromises.AppendElement(handle->BeginClose());
+ }
+ }
+}
+
+} // 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..ffb444b979
--- /dev/null
+++ b/dom/fs/child/FileSystemRequestHandler.cpp
@@ -0,0 +1,631 @@
+/* -*- 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)
+ 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)...));
+}
+
+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/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")