diff --git a/dom/fs/api/FileSystemDirectoryHandle.cpp b/dom/fs/api/FileSystemDirectoryHandle.cpp index 27ee07e28c42..8020d1d89005 100644 --- a/dom/fs/api/FileSystemDirectoryHandle.cpp +++ b/dom/fs/api/FileSystemDirectoryHandle.cpp @@ -5,6 +5,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "FileSystemDirectoryHandle.h" +#include "fs/FileSystemRequestHandler.h" #include "mozilla/ErrorResult.h" #include "mozilla/dom/FileSystemDirectoryHandleBinding.h" @@ -14,6 +15,16 @@ namespace mozilla::dom { +FileSystemDirectoryHandle::FileSystemDirectoryHandle( + nsIGlobalObject* aGlobal, const fs::FileSystemEntryMetadata& aMetadata, + fs::FileSystemRequestHandler* aRequestHandler) + : FileSystemHandle(aGlobal, aMetadata, aRequestHandler) {} + +FileSystemDirectoryHandle::FileSystemDirectoryHandle( + nsIGlobalObject* aGlobal, const fs::FileSystemEntryMetadata& aMetadata) + : FileSystemDirectoryHandle(aGlobal, aMetadata, + new fs::FileSystemRequestHandler()) {} + NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED_0(FileSystemDirectoryHandle, FileSystemHandle) NS_IMPL_CYCLE_COLLECTION_INHERITED(FileSystemDirectoryHandle, FileSystemHandle) @@ -27,7 +38,7 @@ JSObject* FileSystemDirectoryHandle::WrapObject( // WebIDL Interface -FileSystemHandleKind FileSystemDirectoryHandle::Kind() { +FileSystemHandleKind FileSystemDirectoryHandle::Kind() const { return FileSystemHandleKind::Directory; } diff --git a/dom/fs/api/FileSystemDirectoryHandle.h b/dom/fs/api/FileSystemDirectoryHandle.h index c124b9900bfb..35bd96ea128a 100644 --- a/dom/fs/api/FileSystemDirectoryHandle.h +++ b/dom/fs/api/FileSystemDirectoryHandle.h @@ -22,6 +22,13 @@ struct FileSystemRemoveOptions; class FileSystemDirectoryHandle final : public FileSystemHandle { public: + FileSystemDirectoryHandle(nsIGlobalObject* aGlobal, + const fs::FileSystemEntryMetadata& aMetadata, + fs::FileSystemRequestHandler* aRequestHandler); + + FileSystemDirectoryHandle(nsIGlobalObject* aGlobal, + const fs::FileSystemEntryMetadata& aMetadata); + NS_DECL_ISUPPORTS_INHERITED NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(FileSystemDirectoryHandle, FileSystemHandle) @@ -31,7 +38,7 @@ class FileSystemDirectoryHandle final : public FileSystemHandle { JS::Handle aGivenProto) override; // WebIDL Interface - FileSystemHandleKind Kind() override; + FileSystemHandleKind Kind() const override; [[nodiscard]] already_AddRefed Entries(); diff --git a/dom/fs/api/FileSystemFileHandle.cpp b/dom/fs/api/FileSystemFileHandle.cpp index bbb05376d575..d3c71c0c1a3d 100644 --- a/dom/fs/api/FileSystemFileHandle.cpp +++ b/dom/fs/api/FileSystemFileHandle.cpp @@ -5,6 +5,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "FileSystemFileHandle.h" +#include "fs/FileSystemRequestHandler.h" #include "mozilla/ErrorResult.h" #include "mozilla/dom/FileSystemFileHandleBinding.h" @@ -17,6 +18,16 @@ NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED_0(FileSystemFileHandle, FileSystemHandle) NS_IMPL_CYCLE_COLLECTION_INHERITED(FileSystemFileHandle, FileSystemHandle) +FileSystemFileHandle::FileSystemFileHandle( + nsIGlobalObject* aGlobal, const fs::FileSystemEntryMetadata& aMetadata, + fs::FileSystemRequestHandler* aRequestHandler) + : FileSystemHandle(aGlobal, aMetadata, aRequestHandler) {} + +FileSystemFileHandle::FileSystemFileHandle( + nsIGlobalObject* aGlobal, const fs::FileSystemEntryMetadata& aMetadata) + : FileSystemFileHandle(aGlobal, aMetadata, + new fs::FileSystemRequestHandler()) {} + // WebIDL Boilerplate JSObject* FileSystemFileHandle::WrapObject(JSContext* aCx, @@ -26,7 +37,7 @@ JSObject* FileSystemFileHandle::WrapObject(JSContext* aCx, // WebIDL Interface -FileSystemHandleKind FileSystemFileHandle::Kind() { +FileSystemHandleKind FileSystemFileHandle::Kind() const { return FileSystemHandleKind::File; } diff --git a/dom/fs/api/FileSystemFileHandle.h b/dom/fs/api/FileSystemFileHandle.h index bc42af3caefd..90e51f7f81c3 100644 --- a/dom/fs/api/FileSystemFileHandle.h +++ b/dom/fs/api/FileSystemFileHandle.h @@ -19,6 +19,13 @@ struct FileSystemCreateWritableOptions; class FileSystemFileHandle final : public FileSystemHandle { public: + FileSystemFileHandle(nsIGlobalObject* aGlobal, + const fs::FileSystemEntryMetadata& aMetadata, + fs::FileSystemRequestHandler* aRequestHandler); + + FileSystemFileHandle(nsIGlobalObject* aGlobal, + const fs::FileSystemEntryMetadata& aMetadata); + NS_DECL_ISUPPORTS_INHERITED NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(FileSystemFileHandle, FileSystemHandle) @@ -28,7 +35,7 @@ class FileSystemFileHandle final : public FileSystemHandle { JS::Handle aGivenProto) override; // WebIDL interface - FileSystemHandleKind Kind() override; + FileSystemHandleKind Kind() const override; already_AddRefed GetFile(ErrorResult& aError); diff --git a/dom/fs/api/FileSystemHandle.cpp b/dom/fs/api/FileSystemHandle.cpp index 8804e351e6a2..b472f379a983 100644 --- a/dom/fs/api/FileSystemHandle.cpp +++ b/dom/fs/api/FileSystemHandle.cpp @@ -5,6 +5,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "FileSystemHandle.h" +#include "fs/FileSystemRequestHandler.h" #include "mozilla/ErrorResult.h" #include "mozilla/dom/FileSystemHandleBinding.h" @@ -20,6 +21,13 @@ NS_IMPL_CYCLE_COLLECTING_ADDREF(FileSystemHandle); NS_IMPL_CYCLE_COLLECTING_RELEASE(FileSystemHandle); NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(FileSystemHandle, mGlobal); +FileSystemHandle::FileSystemHandle( + nsIGlobalObject* aGlobal, const fs::FileSystemEntryMetadata& aMetadata, + fs::FileSystemRequestHandler* aRequestHandler) + : mGlobal(aGlobal), + mMetadata(aMetadata), + mRequestHandler(aRequestHandler) {} + // WebIDL Boilerplate nsIGlobalObject* FileSystemHandle::GetParentObject() const { return mGlobal; } @@ -31,10 +39,12 @@ JSObject* FileSystemHandle::WrapObject(JSContext* aCx, // WebIDL Interface -void FileSystemHandle::GetName(DOMString& aResult) { aResult.SetNull(); } +void FileSystemHandle::GetName(nsAString& aResult) { + aResult = mMetadata.entryName(); +} already_AddRefed FileSystemHandle::IsSameEntry( - FileSystemHandle& aOther, ErrorResult& aError) { + FileSystemHandle& aOther, ErrorResult& aError) const { RefPtr promise = Promise::Create(GetParentObject(), aError); if (aError.Failed()) { return nullptr; diff --git a/dom/fs/api/FileSystemHandle.h b/dom/fs/api/FileSystemHandle.h index e2cb323fc11b..e2f41ba74772 100644 --- a/dom/fs/api/FileSystemHandle.h +++ b/dom/fs/api/FileSystemHandle.h @@ -7,6 +7,7 @@ #ifndef DOM_FS_FILESYSTEMHANDLE_H_ #define DOM_FS_FILESYSTEMHANDLE_H_ +#include "mozilla/dom/PBackgroundFileSystem.h" #include "nsCOMPtr.h" #include "nsISupports.h" #include "nsWrapperCache.h" @@ -23,8 +24,16 @@ class DOMString; enum class FileSystemHandleKind : uint8_t; class Promise; +namespace fs { +class FileSystemRequestHandler; +} // namespace fs + class FileSystemHandle : public nsISupports, public nsWrapperCache { public: + FileSystemHandle(nsIGlobalObject* aGlobal, + const fs::FileSystemEntryMetadata& aMetadata, + fs::FileSystemRequestHandler* aRequestHandler); + NS_DECL_CYCLE_COLLECTING_ISUPPORTS NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(FileSystemHandle) @@ -35,17 +44,21 @@ class FileSystemHandle : public nsISupports, public nsWrapperCache { JS::Handle aGivenProto) override; // WebIDL Interface - virtual FileSystemHandleKind Kind() = 0; + virtual FileSystemHandleKind Kind() const = 0; - void GetName(DOMString& aResult); + void GetName(nsAString& aResult); already_AddRefed IsSameEntry(FileSystemHandle& aOther, - ErrorResult& aError); + ErrorResult& aError) const; protected: virtual ~FileSystemHandle() = default; nsCOMPtr mGlobal; + + const fs::FileSystemEntryMetadata mMetadata; + + const UniquePtr mRequestHandler; }; } // namespace dom diff --git a/dom/fs/api/moz.build b/dom/fs/api/moz.build index 643136a903ff..3296a00c23df 100644 --- a/dom/fs/api/moz.build +++ b/dom/fs/api/moz.build @@ -22,4 +22,10 @@ UNIFIED_SOURCES += [ "FileSystemWritableFileStream.cpp", ] +LOCAL_INCLUDES += [ + "/dom/fs/include", +] + FINAL_LIBRARY = "xul" + +include("/ipc/chromium/chromium-config.mozbuild") diff --git a/dom/fs/child/BackgroundFileSystemChild.h b/dom/fs/child/BackgroundFileSystemChild.h new file mode 100644 index 000000000000..63aa545d79e8 --- /dev/null +++ b/dom/fs/child/BackgroundFileSystemChild.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_BACKGROUNDFILESYSTEMCHILD_H_ +#define DOM_FS_CHILD_BACKGROUNDFILESYSTEMCHILD_H_ + +#include "mozilla/dom/PBackgroundFileSystemChild.h" +#include "nsISupports.h" + +namespace mozilla::dom { + +class BackgroundFileSystemChild : public PBackgroundFileSystemChild { + NS_INLINE_DECL_REFCOUNTING(BackgroundFileSystemChild); + + protected: + virtual ~BackgroundFileSystemChild() = default; +}; + +} // namespace mozilla::dom + +#endif // DOM_FS_CHILD_BACKGROUNDFILESYSTEMCHILD_H_ diff --git a/dom/fs/child/FileSystemRequestHandler.cpp b/dom/fs/child/FileSystemRequestHandler.cpp new file mode 100644 index 000000000000..c2814d2f0b4f --- /dev/null +++ b/dom/fs/child/FileSystemRequestHandler.cpp @@ -0,0 +1,372 @@ +/* -*- 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 "fs/FileSystemConstants.h" + +#include "mozilla/dom/BackgroundFileSystemChild.h" +#include "mozilla/dom/File.h" +#include "mozilla/dom/FileSystemFileHandle.h" +#include "mozilla/dom/FileSystemDirectoryHandle.h" +#include "mozilla/dom/Promise.h" +#include "mozilla/ipc/BackgroundChild.h" +#include "mozilla/ipc/PBackgroundChild.h" + +using namespace mozilla::ipc; + +namespace mozilla::dom::fs { + +namespace { + +// Not static: BackgroundFileSystemChild must be owned by calling thread +RefPtr GetRootProvider() { + mozilla::dom::BackgroundFileSystemChild* inputPtr = + new mozilla::dom::BackgroundFileSystemChild(); + + mozilla::ipc::PBackgroundChild* bgAccessor = + mozilla::ipc::BackgroundChild::GetOrCreateForCurrentThread(); + + // TODO: Would be nice to convert this to QM_TRY some time later. + if (NS_WARN_IF(!bgAccessor)) { + MOZ_ASSERT(false); + return nullptr; + } + + RefPtr getterPtr = + static_cast( + bgAccessor->SendPBackgroundFileSystemConstructor(inputPtr)); + MOZ_ASSERT(getterPtr); + + return getterPtr; +} + +// TODO: This is just a dummy implementation +RefPtr MakeGetFileResult(const nsString& aName, const nsString& aType, + int64_t aLastModifiedMilliSeconds, + nsTArray&& aPath, + mozilla::ipc::FileDescriptor&& aFile, + nsIGlobalObject* aGlobal) { + // TODO: Replace with a real implementation + RefPtr result = File::CreateMemoryFileWithCustomLastModified( + aGlobal, static_cast(new uint8_t[1]), sizeof(uint8_t), aName, + aType, aLastModifiedMilliSeconds); + + return result; +} + +void GetDirectoryContentsResponseHandler( + RefPtr& /* aActor */, + FileSystemDirectoryListing&& aResponse, nsIGlobalObject* aGlobal, + ArrayAppendable& /* aSink */) { + // TODO: Add page size to FileSystemConstants, preallocate and handle overflow + nsTArray> batch; + + for (const auto& it : aResponse.files()) { + RefPtr handle = new FileSystemFileHandle(aGlobal, it); + batch.AppendElement(handle); + } + + for (const auto& it : aResponse.directories()) { + RefPtr handle = + new FileSystemDirectoryHandle(aGlobal, it); + batch.AppendElement(handle); + } +} + +RefPtr MakeResolution( + nsIGlobalObject* aGlobal, FileSystemGetHandleResponse&& aResponse, + const RefPtr& /* aResolution */, + const Name& aName, RefPtr& aActor) { + RefPtr result = new FileSystemDirectoryHandle( + aGlobal, FileSystemEntryMetadata(aResponse.get_EntryId(), aName)); + return result; +} + +RefPtr MakeResolution( + nsIGlobalObject* aGlobal, FileSystemGetHandleResponse&& aResponse, + const RefPtr& /* aResolution */, const Name& aName, + RefPtr& aActor) { + RefPtr result = new FileSystemFileHandle( + aGlobal, FileSystemEntryMetadata(aResponse.get_EntryId(), aName)); + return result; +} + +RefPtr MakeResolution(nsIGlobalObject* aGlobal, + FileSystemGetFileResponse&& aResponse, + const RefPtr& /* aResolution */, + const Name& aName, + RefPtr& aActor) { + auto& fileProperties = aResponse.get_FileSystemFileProperties(); + return MakeGetFileResult(aName, fileProperties.type(), + fileProperties.last_modified_ms(), + std::move(fileProperties.path()), + std::move(fileProperties.file()), aGlobal); +} + +template +void ResolveCallback( + TResponse&& aResponse, + RefPtr 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()) { + aPromise->MaybeReject(aResponse.get_nsresult()); + return; + } + + aPromise->MaybeResolve(MakeResolution(aPromise->GetParentObject(), + std::forward(aResponse), + std::forward(args)...)); +} + +template <> +void ResolveCallback( + FileSystemRemoveEntryResponse&& aResponse, + RefPtr aPromise) { // NOLINT(performance-unnecessary-value-param) + MOZ_ASSERT(aPromise); + QM_TRY(OkIf(Promise::PromiseState::Pending == aPromise->State()), QM_VOID); + + MOZ_ASSERT(FileSystemRemoveEntryResponse::Tnsresult == aResponse.type()); + const auto& status = aResponse.get_nsresult(); + if (NS_ERROR_FILE_ACCESS_DENIED == status) { + aPromise->MaybeRejectWithNotAllowedError("Permission denied"); + } else if (NS_ERROR_DOM_FILESYSTEM_NO_MODIFICATION_ALLOWED_ERR == status) { + aPromise->MaybeRejectWithInvalidModificationError("Disallowed by system"); + } else if (NS_FAILED(status)) { + aPromise->MaybeRejectWithUnknownError("Unknown failure"); + } else { + aPromise->MaybeResolveWithUndefined(); + } +} + +// NOLINTBEGIN(readability-inconsistent-declaration-parameter-name) +template <> +void ResolveCallback(FileSystemGetEntriesResponse&& aResponse, + // NOLINTNEXTLINE(performance-unnecessary-value-param) + RefPtr aPromise, ArrayAppendable& aSink, + RefPtr& aActor) { + // NOLINTEND(readability-inconsistent-declaration-parameter-name) + MOZ_ASSERT(aPromise); + QM_TRY(OkIf(Promise::PromiseState::Pending == aPromise->State()), QM_VOID); + + if (FileSystemGetEntriesResponse::Tnsresult == aResponse.type()) { + aPromise->MaybeReject(aResponse.get_nsresult()); + return; + } + + GetDirectoryContentsResponseHandler( + aActor, + std::forward( + aResponse.get_FileSystemDirectoryListing()), + aPromise->GetParentObject(), aSink); + + // TODO: Remove this when sink is ready + aPromise->MaybeReject(NS_ERROR_NOT_IMPLEMENTED); +} + +template ::value, bool> = true> +mozilla::ipc::ResolveCallback SelectResolveCallback( + RefPtr aPromise, // NOLINT(performance-unnecessary-value-param) + Args&&... args) { + using TOverload = void (*)(TResponse&&, RefPtr, Args...); + return static_cast>( + // NOLINTNEXTLINE(modernize-avoid-bind) + std::bind(static_cast(ResolveCallback), std::placeholders::_1, + aPromise, std::forward(args)...)); +} + +template ::value, bool> = true> +mozilla::ipc::ResolveCallback SelectResolveCallback( + RefPtr aPromise, // NOLINT(performance-unnecessary-value-param) + Args&&... args) { + using TOverload = + void (*)(TResponse&&, RefPtr, const TReturns&, Args...); + return static_cast>( + // NOLINTNEXTLINE(modernize-avoid-bind) + std::bind(static_cast(ResolveCallback), std::placeholders::_1, + aPromise, TReturns(), std::forward(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 RejectHandler( + RefPtr 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 aPromise) { // NOLINT(performance-unnecessary-value-param) + return static_cast( + // NOLINTNEXTLINE(modernize-avoid-bind) + std::bind(RejectHandler, aPromise, std::placeholders::_1)); +} + +} // namespace + +void FileSystemRequestHandler::GetRoot( + const Origin& aOrigin, + RefPtr aPromise) { // NOLINT(performance-unnecessary-value-param) + MOZ_ASSERT(!aOrigin.IsEmpty()); + MOZ_ASSERT(aPromise); + + RefPtr dummyActor = + MakeAndAddRef(); + + Name name = kRootName; + auto&& onResolve = SelectResolveCallback>( + aPromise, name, dummyActor); + + auto&& onReject = GetRejectCallback(aPromise); + + // XXX do something (register with global?) so that we can Close() the actor + // before the event queue starts to shut down. That will cancel all + // outstanding async requests (returns lambdas with errors). + RefPtr rootProvider = + GetRootProvider(); + if (!rootProvider) { + aPromise->MaybeRejectWithUnknownError("Could not access the file system"); + return; + } + rootProvider->SendGetRoot(aOrigin, std::move(onResolve), std::move(onReject)); +} + +void FileSystemRequestHandler::GetDirectoryHandle( + RefPtr& aActor, + const FileSystemChildMetadata& aDirectory, bool aCreate, + RefPtr aPromise) { // NOLINT(performance-unnecessary-value-param) + MOZ_ASSERT(!aDirectory.parentId().IsEmpty()); + MOZ_ASSERT(aPromise); + + FileSystemGetHandleRequest request(aDirectory, aCreate); + + auto&& onResolve = SelectResolveCallback>( + aPromise, aDirectory.childName(), aActor); + + auto&& onReject = GetRejectCallback(aPromise); + + auto actor = GetRootProvider(); + QM_TRY(OkIf(actor), QM_VOID); + actor->SendGetDirectoryHandle(request, std::move(onResolve), + std::move(onReject)); +} + +void FileSystemRequestHandler::GetFileHandle( + RefPtr& aActor, const FileSystemChildMetadata& aFile, + bool aCreate, + RefPtr aPromise) { // NOLINT(performance-unnecessary-value-param) + MOZ_ASSERT(!aFile.parentId().IsEmpty()); + MOZ_ASSERT(aPromise); + + FileSystemGetHandleRequest request(aFile, aCreate); + + auto&& onResolve = SelectResolveCallback>( + aPromise, aFile.childName(), aActor); + + auto&& onReject = GetRejectCallback(aPromise); + + auto actor = GetRootProvider(); + QM_TRY(OkIf(actor), QM_VOID); + actor->SendGetFileHandle(request, std::move(onResolve), std::move(onReject)); +} + +void FileSystemRequestHandler::GetFile( + RefPtr& aActor, const FileSystemEntryMetadata& aFile, + RefPtr aPromise) { // NOLINT(performance-unnecessary-value-param) + MOZ_ASSERT(!aFile.entryId().IsEmpty()); + MOZ_ASSERT(aPromise); + + FileSystemGetFileRequest request(aFile.entryId()); + + auto&& onResolve = + SelectResolveCallback>( + aPromise, aFile.entryName(), aActor); + + auto&& onReject = GetRejectCallback(aPromise); + + auto actor = GetRootProvider(); + QM_TRY(OkIf(actor), QM_VOID); + actor->SendGetFile(request, std::move(onResolve), std::move(onReject)); +} + +void FileSystemRequestHandler::GetEntries( + RefPtr& aActor, const EntryId& aDirectory, + PageNumber aPage, + RefPtr aPromise, // NOLINT(performance-unnecessary-value-param) + ArrayAppendable& aSink) { + MOZ_ASSERT(!aDirectory.IsEmpty()); + MOZ_ASSERT(aPromise); + + FileSystemGetEntriesRequest request(aDirectory, aPage); + + using TOverload = void (*)(FileSystemGetEntriesResponse&&, RefPtr, + ArrayAppendable&, RefPtr&); + + // We are not allowed to pass a promise to an external function in a lambda + auto&& onResolve = + static_cast>( + // NOLINTNEXTLINE(modernize-avoid-bind) + std::bind(static_cast(ResolveCallback), + std::placeholders::_1, aPromise, std::ref(aSink), aActor)); + + auto&& onReject = GetRejectCallback(aPromise); + + auto actor = GetRootProvider(); + QM_TRY(OkIf(actor), QM_VOID); + actor->SendGetEntries(request, std::move(onResolve), std::move(onReject)); +} + +void FileSystemRequestHandler::RemoveEntry( + RefPtr& /* aActor */, + const FileSystemChildMetadata& aEntry, bool aRecursive, + RefPtr aPromise) { // NOLINT(performance-unnecessary-value-param) + MOZ_ASSERT(!aEntry.parentId().IsEmpty()); + MOZ_ASSERT(aPromise); + + FileSystemRemoveEntryRequest request(aEntry, aRecursive); + + auto&& onResolve = + SelectResolveCallback(aPromise); + + auto&& onReject = GetRejectCallback(aPromise); + + auto actor = GetRootProvider(); + QM_TRY(OkIf(actor), QM_VOID); + actor->SendRemoveEntry(request, std::move(onResolve), std::move(onReject)); +} + +} // namespace mozilla::dom::fs diff --git a/dom/fs/child/moz.build b/dom/fs/child/moz.build new file mode 100644 index 000000000000..5be549513609 --- /dev/null +++ b/dom/fs/child/moz.build @@ -0,0 +1,21 @@ +# -*- 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 += [ + "BackgroundFileSystemChild.h", +] + +UNIFIED_SOURCES += [ + "FileSystemRequestHandler.cpp", +] + +LOCAL_INCLUDES += [ + "/dom/fs/include", +] + +FINAL_LIBRARY = "xul" + +include("/ipc/chromium/chromium-config.mozbuild") diff --git a/dom/fs/include/fs/FileSystemConstants.h b/dom/fs/include/fs/FileSystemConstants.h new file mode 100644 index 000000000000..fc9e1840c469 --- /dev/null +++ b/dom/fs/include/fs/FileSystemConstants.h @@ -0,0 +1,18 @@ +/* -*- 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_FILESYSTEMCONSTANTS_H_ +#define DOM_FS_FILESYSTEMCONSTANTS_H_ + +#include "nsLiteralString.h" + +namespace mozilla::dom::fs { + +constexpr nsLiteralString kRootName = u"root"_ns; + +} // namespace mozilla::dom::fs + +#endif // DOM_FS_FILESYSTEMCONSTANTS_H_ diff --git a/dom/fs/include/fs/FileSystemRequestHandler.h b/dom/fs/include/fs/FileSystemRequestHandler.h new file mode 100644 index 000000000000..400f07c154aa --- /dev/null +++ b/dom/fs/include/fs/FileSystemRequestHandler.h @@ -0,0 +1,67 @@ +/* -*- 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_FILESYSTEMREQUESTHANDLER_H_ +#define DOM_FS_CHILD_FILESYSTEMREQUESTHANDLER_H_ + +#include "nsStringFwd.h" +#include "mozilla/dom/FileSystemTypes.h" +#include "mozilla/dom/FileSystemHandle.h" + +template +class RefPtr; + +namespace mozilla::dom { + +// TODO: Replace this dummy class with real implementation +class FileSystemActorHolder { + NS_INLINE_DECL_REFCOUNTING(FileSystemActorHolder) + protected: + virtual ~FileSystemActorHolder() = default; +}; + +class FileSystemHandle; +class Promise; +class OriginPrivateFileSystemChild; +} // namespace mozilla::dom + +namespace mozilla::dom::fs { + +class FileSystemChildMetadata; +class FileSystemEntryMetadata; + +class ArrayAppendable {}; + +class FileSystemRequestHandler { + public: + virtual void GetRoot(const Origin& aOrigin, RefPtr aPromise); + + virtual void GetDirectoryHandle(RefPtr& aActor, + const FileSystemChildMetadata& aDirectory, + bool aCreate, RefPtr aPromise); + + virtual void GetFileHandle(RefPtr& aActor, + const FileSystemChildMetadata& aFile, bool aCreate, + RefPtr aPromise); + + virtual void GetFile(RefPtr& aActor, + const FileSystemEntryMetadata& aFile, + RefPtr aPromise); + + virtual void GetEntries(RefPtr& aActor, + const EntryId& aDirectory, PageNumber aPage, + RefPtr aPromise, ArrayAppendable& aSink); + + virtual void RemoveEntry(RefPtr& aActor, + const FileSystemChildMetadata& aEntry, + bool aRecursive, RefPtr aPromise); + + virtual ~FileSystemRequestHandler() = default; +}; // class FileSystemRequestHandler + +} // namespace mozilla::dom::fs + +#endif // DOM_FS_CHILD_FILESYSTEMREQUESTHANDLER_H_ diff --git a/dom/fs/moz.build b/dom/fs/moz.build index 495b944535a3..1a1ba49ab704 100644 --- a/dom/fs/moz.build +++ b/dom/fs/moz.build @@ -4,6 +4,11 @@ # 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/. -DIRS += ["api"] +DIRS += [ + "api", + "child", + "parent", + "shared", +] TEST_DIRS += ["test"] diff --git a/dom/fs/parent/BackgroundFileSystemParent.cpp b/dom/fs/parent/BackgroundFileSystemParent.cpp new file mode 100644 index 000000000000..4ae11072749b --- /dev/null +++ b/dom/fs/parent/BackgroundFileSystemParent.cpp @@ -0,0 +1,71 @@ +/* -*- 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 "BackgroundFileSystemParent.h" +#include "mozilla/dom/FileSystemTypes.h" + +using IPCResult = mozilla::ipc::IPCResult; + +namespace mozilla::dom { + +IPCResult BackgroundFileSystemParent::RecvGetRoot(const fs::Origin& aOrigin, + GetRootResolver&& aResolver) { + FileSystemGetHandleResponse response(NS_ERROR_NOT_IMPLEMENTED); + aResolver(response); + + return IPC_OK(); +} + +IPCResult BackgroundFileSystemParent::RecvGetDirectoryHandle( + FileSystemGetHandleRequest&& /* aRequest */, + GetDirectoryHandleResolver&& aResolver) { + FileSystemGetHandleResponse response(NS_ERROR_NOT_IMPLEMENTED); + aResolver(response); + + return IPC_OK(); +} + +IPCResult BackgroundFileSystemParent::RecvGetFileHandle( + FileSystemGetHandleRequest&& aRequest, GetFileHandleResolver&& aResolver) { + FileSystemGetHandleResponse response(NS_ERROR_NOT_IMPLEMENTED); + aResolver(response); + + return IPC_OK(); +} + +IPCResult BackgroundFileSystemParent::RecvGetFile( + FileSystemGetFileRequest&& aRequest, GetFileResolver&& aResolver) { + FileSystemGetFileResponse response(NS_ERROR_NOT_IMPLEMENTED); + aResolver(response); + + return IPC_OK(); +} + +IPCResult BackgroundFileSystemParent::RecvResolve( + FileSystemResolveRequest&& aRequest, ResolveResolver&& aResolver) { + FileSystemResolveResponse response(NS_ERROR_NOT_IMPLEMENTED); + aResolver(response); + + return IPC_OK(); +} + +IPCResult BackgroundFileSystemParent::RecvGetEntries( + FileSystemGetEntriesRequest&& aRequest, GetEntriesResolver&& aResolver) { + FileSystemGetEntriesResponse response(NS_ERROR_NOT_IMPLEMENTED); + aResolver(response); + + return IPC_OK(); +} + +IPCResult BackgroundFileSystemParent::RecvRemoveEntry( + FileSystemRemoveEntryRequest&& aRequest, RemoveEntryResolver&& aResolver) { + FileSystemRemoveEntryResponse response(NS_ERROR_NOT_IMPLEMENTED); + aResolver(response); + + return IPC_OK(); +} + +} // namespace mozilla::dom diff --git a/dom/fs/parent/BackgroundFileSystemParent.h b/dom/fs/parent/BackgroundFileSystemParent.h new file mode 100644 index 000000000000..015585f27a7a --- /dev/null +++ b/dom/fs/parent/BackgroundFileSystemParent.h @@ -0,0 +1,48 @@ +/* -*- 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_PARENT_BACKGROUNDFILESYSTEMPARENT_H_ +#define DOM_FS_PARENT_BACKGROUNDFILESYSTEMPARENT_H_ + +#include "mozilla/dom/PBackgroundFileSystemParent.h" +#include "mozilla/ipc/ProtocolUtils.h" +#include "nsISupports.h" + +namespace mozilla::dom { + +class BackgroundFileSystemParent : public PBackgroundFileSystemParent { + public: + mozilla::ipc::IPCResult RecvGetRoot(const fs::Origin& aOrigin, + GetRootResolver&& aResolver); + + mozilla::ipc::IPCResult RecvGetDirectoryHandle( + FileSystemGetHandleRequest&& aRequest, + GetDirectoryHandleResolver&& aResolver); + + mozilla::ipc::IPCResult RecvGetFileHandle( + FileSystemGetHandleRequest&& aRequest, GetFileHandleResolver&& aResolver); + + mozilla::ipc::IPCResult RecvGetFile(FileSystemGetFileRequest&& aRequest, + GetFileResolver&& aResolver); + + mozilla::ipc::IPCResult RecvResolve(FileSystemResolveRequest&& aRequest, + ResolveResolver&& aResolver); + + mozilla::ipc::IPCResult RecvGetEntries(FileSystemGetEntriesRequest&& aRequest, + GetEntriesResolver&& aResolver); + + mozilla::ipc::IPCResult RecvRemoveEntry( + FileSystemRemoveEntryRequest&& aRequest, RemoveEntryResolver&& aResolver); + + NS_INLINE_DECL_REFCOUNTING(BackgroundFileSystemParent) + + protected: + virtual ~BackgroundFileSystemParent() = default; +}; + +} // namespace mozilla::dom + +#endif // DOM_FS_PARENT_BACKGROUNDFILESYSTEMPARENT_H_ diff --git a/dom/fs/parent/moz.build b/dom/fs/parent/moz.build new file mode 100644 index 000000000000..6f4f2942bc1a --- /dev/null +++ b/dom/fs/parent/moz.build @@ -0,0 +1,17 @@ +# -*- 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 += [ + "BackgroundFileSystemParent.h", +] + +UNIFIED_SOURCES += [ + "BackgroundFileSystemParent.cpp", +] + +FINAL_LIBRARY = "xul" + +include("/ipc/chromium/chromium-config.mozbuild") diff --git a/dom/fs/shared/FileSystemTypes.h b/dom/fs/shared/FileSystemTypes.h new file mode 100644 index 000000000000..5517dd23584b --- /dev/null +++ b/dom/fs/shared/FileSystemTypes.h @@ -0,0 +1,27 @@ +/* -*- 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_FILESYSTEMTYPES_H_ +#define DOM_FS_FILESYSTEMTYPES_H_ + +#include "nsStringFwd.h" + +template +class nsTArray; + +namespace mozilla::dom::fs { + +using ContentType = nsString; +using EntryId = nsCString; +using Name = nsString; +using Origin = nsCString; +using PageNumber = uint32_t; +using Path = nsTArray; +using TimeStamp = int64_t; + +} // namespace mozilla::dom::fs + +#endif // DOM_FS_FILESYSTEMTYPES_H_ diff --git a/dom/fs/shared/PBackgroundFileSystem.ipdl b/dom/fs/shared/PBackgroundFileSystem.ipdl new file mode 100644 index 000000000000..a879578b4a9b --- /dev/null +++ b/dom/fs/shared/PBackgroundFileSystem.ipdl @@ -0,0 +1,296 @@ +/* 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 protocol PBackground; + +using mozilla::dom::fs::ContentType from "mozilla/dom/FileSystemTypes.h"; +using mozilla::dom::fs::EntryId from "mozilla/dom/FileSystemTypes.h"; +using mozilla::dom::fs::Name from "mozilla/dom/FileSystemTypes.h"; +using mozilla::dom::fs::Origin from "mozilla/dom/FileSystemTypes.h"; +using mozilla::dom::fs::PageNumber from "mozilla/dom/FileSystemTypes.h"; +using mozilla::dom::fs::TimeStamp from "mozilla/dom/FileSystemTypes.h"; +using struct mozilla::void_t from "mozilla/ipc/IPCCore.h"; + +namespace mozilla { +namespace dom { +namespace fs { + +/** + * Identifies a file or a directory and contains its user provided name. + */ +struct FileSystemEntryMetadata +{ + EntryId entryId; + Name entryName; +}; + +/** + * Identifies a file or a directory with its parent identifier and user provided name. + */ +struct FileSystemChildMetadata +{ + EntryId parentId; + Name childName; +}; + +/** + * Identifies a file with its parent directory and name, and + * indicates whether the file may be created if it is missing. + */ +struct FileSystemGetHandleRequest +{ + FileSystemChildMetadata handle; + bool create; +}; + +/** + * Contains a file or directory or an error. + */ +union FileSystemGetHandleResponse +{ + nsresult; + EntryId; +}; + +/** + * Contains an identifier for a parent directory and a page number + * which is used to fetch the next set of entries when the directory + * contains so many items that communicating all of them in one message + * is an impractical. + */ +struct FileSystemGetEntriesRequest +{ + EntryId parentId; + PageNumber page; +}; + +/** + * Contains a set of directories and files + * under the same parent directory. + */ +struct FileSystemDirectoryListing +{ + FileSystemEntryMetadata[] directories; + FileSystemEntryMetadata[] files; +}; + +/** + * Contains a set of entries or an error. + */ +union FileSystemGetEntriesResponse +{ + nsresult; + FileSystemDirectoryListing; +}; + +/** + * Contains entry handle information. + */ +struct FileSystemGetFileRequest +{ + EntryId entryId; +}; + +/** + * Contains the properties of a file and a file descriptor. + * The properties may differ from the properties of the + * underlying object of the file descriptor. + */ +struct FileSystemFileProperties +{ + TimeStamp last_modified_ms; + FileDescriptor file; + ContentType type; + Name[] path; +}; + +/** + * Contains file properties or an error. + */ +union FileSystemGetFileResponse +{ + nsresult; + FileSystemFileProperties; +}; + +/** + * Represents a pair of file system entries which + * are not necessarily connected by a path. + */ +struct FileSystemEntryPair +{ + EntryId parentId; + EntryId childId; +}; + +/** + * Contains a pair of file system entries. + */ +struct FileSystemResolveRequest +{ + FileSystemEntryPair endpoints; +}; + +/** + * Contains a file system path. + */ +struct FileSystemPath +{ + Name[] path; +}; + +/** + * Contains a potentially empty path or an error. + */ +union FileSystemResolveResponse +{ + nsresult; + FileSystemPath?; +}; + +/** + * Identifies a file with its parent directory and name, and + * indicates whether all the children of a directory may be removed. + */ +struct FileSystemRemoveEntryRequest +{ + FileSystemChildMetadata handle; + bool recursive; +}; + +/** + * Contains an error or nothing. + */ +union FileSystemRemoveEntryResponse +{ + nsresult; + void_t; +}; + +} // namespace fs + +async protocol PBackgroundFileSystem +{ + manager PBackground; + + parent: + + /** + * Initiates an asynchronous request for the directory handle of + * the file system's root level. On first call, may conduct initialization + * activities. + * + * @param[in] origin of the file system + * + * @returns error or entry handle + */ + async GetRoot(Origin origin) returns(FileSystemGetHandleResponse handle); + + /** + * Initiates an asynchronous request for the handle of + * a subdirectory with a given name under the current directory. + * + * Invalid names are rejected with an appropriate error. + * + * If the subdirectory exists, a handle to it is always returned. + * + * If no child of any kind with the given name exists and + * the create-flag of the input is set, the subdirectory will be created, + * otherwise an appropriate error is returned. + * + * @param[in] handle request containing a create flag + * + * @returns error or entry handle + */ + async GetDirectoryHandle(FileSystemGetHandleRequest request) returns(FileSystemGetHandleResponse handle); + + /** + * Initiates an asynchronous request for the handle to + * a file with a given name under the current directory. + * + * Invalid names are rejected with an appropriate error. + * + * If the file exists, a handle to it is always returned. + * + * If no child of any kind with the given name exists and + * the create-flag of the input is set, the file will be created, + * otherwise an appropriate error is returned. + * + * @param[in] handle request containing a create flag + * + * @returns error or entry handle + */ + async GetFileHandle(FileSystemGetHandleRequest request) returns(FileSystemGetHandleResponse handle); + + /** + * Initiates an asynchronous request for a read-only object representing the + * file corresponding to the current file handle. + * + * The returned object provides read-only access. + * + * If the underlying file object is modified through a mutable interface, + * the returned value is considered stale. Concurrent changes are not + * guaranteed to be visible or invisible. Using a stale object + * returns appropriate errors when the results are unpredictable. + * + * @param[in] request for a file object + * + * @returns error or file object + */ + async GetFile(FileSystemGetFileRequest request) returns(FileSystemGetFileResponse response); + + /** + * Initiates an asynchronous request for the file system path + * associated with a file system entry. + * + * @param[in] request identifying a file object + * + * @returns error or file system path + */ + async Resolve(FileSystemResolveRequest request) returns(FileSystemResolveResponse response); + + /** + * Initiates an asynchronous request for an iterator to the child entries under + * the current directory handle. + * + * If the directory item names or the directory structure is modified while the iterator + * is in use, the iterator remains safe to use but no guarantees are made regarding + * the visibility of the concurrent changes. It is possible that a file which is added after + * the iteration has begun will not be returned, or that among the values there + * are invalid file handles whose underlying objects have been removed + * after the iteration started. + * + * @param[in] request for a iterator + * + * @returns error or iterator + */ + async GetEntries(FileSystemGetEntriesRequest request) returns(FileSystemGetEntriesResponse entries); + + /** + * Initiates an asynchronous request to delete a directory or file with a given name + * under the current directory handle. + * + * If recursive flag of the request is not set, a request to remove a non-empty + * directory returns an appropriate error, otherwise all the child files and + * directories are made to vanish. + * + * The recursive flag has no impact on files. + * + * @param[in] request containing a recursive flag + * + * @returns error information + */ + async RemoveEntry(FileSystemRemoveEntryRequest request) returns(FileSystemRemoveEntryResponse response); + + child: + + /** + * Mandatory IPC managed lifecycle request. + */ + async __delete__(); +}; + +} // namespace dom +} // namespace mozilla + diff --git a/dom/fs/shared/moz.build b/dom/fs/shared/moz.build new file mode 100644 index 000000000000..c36ea2df0b35 --- /dev/null +++ b/dom/fs/shared/moz.build @@ -0,0 +1,17 @@ +# -*- 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 += [ + "FileSystemTypes.h", +] + +FINAL_LIBRARY = "xul" + +IPDL_SOURCES += [ + "PBackgroundFileSystem.ipdl", +] + +include("/ipc/chromium/chromium-config.mozbuild") diff --git a/dom/fs/test/gtest/FileSystemMocks.cpp b/dom/fs/test/gtest/FileSystemMocks.cpp new file mode 100644 index 000000000000..078711dafc3d --- /dev/null +++ b/dom/fs/test/gtest/FileSystemMocks.cpp @@ -0,0 +1,29 @@ +/* -*- 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 "FileSystemMocks.h" + +#include "jsapi.h" +#include "nsISupports.h" +#include "js/RootingAPI.h" + +namespace mozilla::dom::fs::test { + +nsIGlobalObject* GetGlobal() { + AutoJSAPI jsapi; + DebugOnly ok = jsapi.Init(xpc::PrivilegedJunkScope()); + MOZ_ASSERT(ok); + + JSContext* cx = jsapi.cx(); + mozilla::dom::GlobalObject globalObject(cx, JS::CurrentGlobalOrNull(cx)); + nsCOMPtr global = + do_QueryInterface(globalObject.GetAsSupports()); + MOZ_ASSERT(global); + + return global.get(); +} + +} // namespace mozilla::dom::fs::test diff --git a/dom/fs/test/gtest/FileSystemMocks.h b/dom/fs/test/gtest/FileSystemMocks.h new file mode 100644 index 000000000000..93349aed5814 --- /dev/null +++ b/dom/fs/test/gtest/FileSystemMocks.h @@ -0,0 +1,178 @@ +/* -*- 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_TEST_GTEST_FILESYSTEMMOCKS_H_ +#define DOM_FS_TEST_GTEST_FILESYSTEMMOCKS_H_ + +#include "gtest/gtest.h" +#include "gmock/gmock.h" + +#include "TestHelpers.h" +#include "fs/FileSystemRequestHandler.h" + +#include "mozilla/dom/BindingDeclarations.h" +#include "mozilla/dom/BindingUtils.h" +#include "mozilla/dom/DOMException.h" +#include "mozilla/dom/DOMExceptionBinding.h" +#include "mozilla/dom/Promise.h" +#include "mozilla/dom/PromiseNativeHandler.h" +#include "mozilla/dom/ScriptSettings.h" +#include "mozilla/ErrorResult.h" +#include "mozilla/ScopeExit.h" +#include "mozilla/UniquePtr.h" +#include "nsIGlobalObject.h" +#include "nsISupportsImpl.h" +#include "nsITimer.h" + +#include "jsapi.h" +#include "js/Promise.h" +#include "js/RootingAPI.h" + +#include // We don't have a mozilla shared pointer for pod types + +namespace mozilla::dom::fs::test { + +nsIGlobalObject* GetGlobal(); + +class MockFileSystemRequestHandler : public FileSystemRequestHandler { + public: + MOCK_METHOD2(GetRoot, void(const Origin& aOrigin, RefPtr aPromise)); + + MOCK_METHOD4(GetDirectoryHandle, + void(RefPtr& aActor, + const FileSystemChildMetadata& aDirectory, bool aCreate, + RefPtr aPromise)); + + MOCK_METHOD4(GetFileHandle, void(RefPtr& aActor, + const FileSystemChildMetadata& aFile, + bool aCreate, RefPtr aPromise)); + + MOCK_METHOD3(GetFile, void(RefPtr& aActor, + const FileSystemEntryMetadata& aFile, + RefPtr aPromise)); + + MOCK_METHOD5(GetEntries, + void(RefPtr& aActor, + const EntryId& aDirectory, PageNumber aPage, + RefPtr aPromise, ArrayAppendable& aSink)); + + MOCK_METHOD4(RemoveEntry, void(RefPtr& aActor, + const FileSystemChildMetadata& aEntry, + bool aRecursive, RefPtr aPromise)); +}; + +class WaitablePromiseListener { + public: + NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING + + virtual void ClearDone() = 0; + + virtual bool IsDone() const = 0; + + virtual PromiseNativeHandler* AsHandler() = 0; + + protected: + virtual ~WaitablePromiseListener() = default; +}; + +template +class TestPromiseListener : public PromiseNativeHandler, + public WaitablePromiseListener { + public: + TestPromiseListener() : mIsDone(std::make_shared(false)), mTimer() { + ClearDone(); + } + + // nsISupports implementation + + NS_IMETHODIMP QueryInterface(REFNSIID aIID, void** aInstancePtr) override { + nsresult rv = NS_ERROR_UNEXPECTED; + NS_INTERFACE_TABLE0(TestPromiseListener) + + return rv; + } + + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(TestPromiseListener, override) + + // PromiseNativeHandler implementation + + void ResolvedCallback(JSContext* aCx, JS::Handle aValue, + ErrorResult& aRv) override { + mozilla::ScopeExit flagAsDone([isDone = mIsDone] { *isDone = true; }); + + SuccessHandler{}(); + } + + void RejectedCallback(JSContext* aCx, JS::Handle aValue, + ErrorResult& aRv) override { + mozilla::ScopeExit flagAsDone([isDone = mIsDone] { *isDone = true; }); + + if (aValue.isInt32()) { + ErrorHandler{}(static_cast(aValue.toInt32())); + return; + } + + ASSERT_TRUE(aValue.isObject()); + JS::Rooted exceptionObject(aCx, &aValue.toObject()); + + RefPtr exception; + UNWRAP_OBJECT(Exception, exceptionObject, exception); + if (exception) { + ErrorHandler{}(static_cast(exception->Result())); + return; + } + } + + // WaitablePromiseListener implementation + + void ClearDone() override { + *mIsDone = false; + if (mTimer) { + mTimer->Cancel(); + } + auto timerCallback = [isDone = mIsDone](nsITimer* aTimer) { + *isDone = true; + }; + const char* timerName = "fs::TestPromiseListener::ClearDone"; + auto res = NS_NewTimerWithCallback(timerCallback, MilliSeconds, + nsITimer::TYPE_ONE_SHOT, timerName); + if (res.isOk()) { + mTimer = res.unwrap(); + } + } + + bool IsDone() const override { return *mIsDone; } + + PromiseNativeHandler* AsHandler() override { return this; } + + protected: + virtual ~TestPromiseListener() = default; + + std::shared_ptr mIsDone; // We pass this to a callback + + nsCOMPtr mTimer; +}; + +template +struct NSErrorMatcher { + void operator()(nsresult aErr) { ASSERT_NSEQ(Expected, aErr); } +}; + +struct FailOnCall { + void operator()() { FAIL(); } +}; + +} // namespace mozilla::dom::fs::test + +#define MOCK_PROMISE_LISTENER(name, ...) \ + using name = mozilla::dom::fs::test::TestPromiseListener<__VA_ARGS__>; + +MOCK_PROMISE_LISTENER( + ExpectNotImplemented, mozilla::dom::fs::test::FailOnCall, + mozilla::dom::fs::test::NSErrorMatcher); + +#endif // DOM_FS_TEST_GTEST_FILESYSTEMMOCKS_H_ diff --git a/dom/fs/test/gtest/TestHelpers.h b/dom/fs/test/gtest/TestHelpers.h new file mode 100644 index 000000000000..2954d9757ec1 --- /dev/null +++ b/dom/fs/test/gtest/TestHelpers.h @@ -0,0 +1,18 @@ +/* -*- 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_TEST_GTEST_TESTHELPERS_H_ +#define DOM_FS_TEST_GTEST_TESTHELPERS_H_ + +#include "gtest/gtest.h" + +#include "ErrorList.h" +#include "mozilla/ErrorNames.h" + +#define ASSERT_NSEQ(lhs, rhs) \ + ASSERT_STREQ(GetStaticErrorName((lhs)), GetStaticErrorName((rhs))) + +#endif // DOM_FS_TEST_GTEST_TESTHELPERS_H_ diff --git a/dom/fs/test/gtest/api/TestFileSystemDirectoryHandle.cpp b/dom/fs/test/gtest/api/TestFileSystemDirectoryHandle.cpp new file mode 100644 index 000000000000..41320123ec38 --- /dev/null +++ b/dom/fs/test/gtest/api/TestFileSystemDirectoryHandle.cpp @@ -0,0 +1,196 @@ +/* -*- 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 "gtest/gtest.h" + +#include "FileSystemMocks.h" + +#include "mozilla/dom/FileSystemDirectoryHandle.h" +#include "mozilla/dom/FileSystemDirectoryHandleBinding.h" +#include "mozilla/dom/FileSystemDirectoryIterator.h" +#include "mozilla/dom/FileSystemHandle.h" +#include "mozilla/dom/FileSystemHandleBinding.h" + +#include "mozilla/UniquePtr.h" +#include "nsIGlobalObject.h" + +namespace mozilla::dom::fs::test { + +class TestFileSystemDirectoryHandle : public ::testing::Test { + protected: + void SetUp() override { + mRequestHandler = MakeUnique(); + mMetadata = FileSystemEntryMetadata("dir"_ns, u"Directory"_ns); + mName = u"testDir"_ns; + } + + nsIGlobalObject* mGlobal = GetGlobal(); + UniquePtr mRequestHandler; + FileSystemEntryMetadata mMetadata; + nsString mName; +}; + +TEST_F(TestFileSystemDirectoryHandle, constructDirectoryHandleRefPointer) { + RefPtr dirHandle = + MakeAndAddRef(mGlobal, mMetadata); + + ASSERT_TRUE(dirHandle); +} + +TEST_F(TestFileSystemDirectoryHandle, areEntriesReturned) { + RefPtr dirHandle = + MakeAndAddRef(mGlobal, mMetadata, + mRequestHandler.release()); + + ASSERT_TRUE(dirHandle); + + RefPtr entries = dirHandle->Entries(); + ASSERT_TRUE(entries); +} + +TEST_F(TestFileSystemDirectoryHandle, areKeysReturned) { + RefPtr dirHandle = + MakeAndAddRef(mGlobal, mMetadata, + mRequestHandler.release()); + + ASSERT_TRUE(dirHandle); + + RefPtr keys = dirHandle->Keys(); + ASSERT_TRUE(keys); +} + +TEST_F(TestFileSystemDirectoryHandle, areValuesReturned) { + RefPtr dirHandle = + MakeAndAddRef(mGlobal, mMetadata, + mRequestHandler.release()); + + ASSERT_TRUE(dirHandle); + + RefPtr values = dirHandle->Values(); + ASSERT_TRUE(values); +} + +TEST_F(TestFileSystemDirectoryHandle, isHandleKindDirectory) { + RefPtr dirHandle = + MakeAndAddRef(mGlobal, mMetadata, + mRequestHandler.release()); + + ASSERT_TRUE(dirHandle); + + ASSERT_EQ(FileSystemHandleKind::Directory, dirHandle->Kind()); +} + +TEST_F(TestFileSystemDirectoryHandle, isFileHandleReturned) { + RefPtr dirHandle = + MakeAndAddRef(mGlobal, mMetadata, + mRequestHandler.release()); + + ASSERT_TRUE(dirHandle); + + FileSystemGetFileOptions options; + IgnoredErrorResult rv; + RefPtr promise = dirHandle->GetFileHandle(mName, options, rv); + + ASSERT_TRUE(rv.ErrorCodeIs(NS_OK)); +} + +TEST_F(TestFileSystemDirectoryHandle, doesGetFileHandleFailOnNullGlobal) { + mGlobal = nullptr; + RefPtr dirHandle = + MakeAndAddRef(mGlobal, mMetadata); + + ASSERT_TRUE(dirHandle); + + FileSystemGetFileOptions options; + IgnoredErrorResult rv; + RefPtr promise = dirHandle->GetFileHandle(mName, options, rv); + + ASSERT_TRUE(rv.ErrorCodeIs(NS_ERROR_UNEXPECTED)); +} + +TEST_F(TestFileSystemDirectoryHandle, isDirectoryHandleReturned) { + RefPtr dirHandle = + MakeAndAddRef(mGlobal, mMetadata, + mRequestHandler.release()); + + ASSERT_TRUE(dirHandle); + + FileSystemGetDirectoryOptions options; + IgnoredErrorResult rv; + RefPtr promise = dirHandle->GetDirectoryHandle(mName, options, rv); + + ASSERT_TRUE(rv.ErrorCodeIs(NS_OK)); +} + +TEST_F(TestFileSystemDirectoryHandle, doesGetDirectoryHandleFailOnNullGlobal) { + mGlobal = nullptr; + RefPtr dirHandle = + MakeAndAddRef(mGlobal, mMetadata); + + ASSERT_TRUE(dirHandle); + + FileSystemGetDirectoryOptions options; + IgnoredErrorResult rv; + RefPtr promise = dirHandle->GetDirectoryHandle(mName, options, rv); + + ASSERT_TRUE(rv.ErrorCodeIs(NS_ERROR_UNEXPECTED)); +} + +TEST_F(TestFileSystemDirectoryHandle, isRemoveEntrySuccessful) { + RefPtr dirHandle = + MakeAndAddRef(mGlobal, mMetadata, + mRequestHandler.release()); + + ASSERT_TRUE(dirHandle); + + FileSystemRemoveOptions options; + IgnoredErrorResult rv; + RefPtr promise = dirHandle->RemoveEntry(mName, options, rv); + + ASSERT_TRUE(rv.ErrorCodeIs(NS_OK)); +} + +TEST_F(TestFileSystemDirectoryHandle, doesRemoveEntryFailOnNullGlobal) { + mGlobal = nullptr; + RefPtr dirHandle = + MakeAndAddRef(mGlobal, mMetadata); + + ASSERT_TRUE(dirHandle); + + FileSystemRemoveOptions options; + IgnoredErrorResult rv; + RefPtr promise = dirHandle->RemoveEntry(mName, options, rv); + + ASSERT_TRUE(rv.ErrorCodeIs(NS_ERROR_UNEXPECTED)); +} + +TEST_F(TestFileSystemDirectoryHandle, isResolveSuccessful) { + RefPtr dirHandle = + MakeAndAddRef(mGlobal, mMetadata, + mRequestHandler.release()); + + ASSERT_TRUE(dirHandle); + + IgnoredErrorResult rv; + RefPtr promise = dirHandle->Resolve(*dirHandle, rv); + + ASSERT_TRUE(rv.ErrorCodeIs(NS_OK)); +} + +TEST_F(TestFileSystemDirectoryHandle, doesResolveFailOnNullGlobal) { + mGlobal = nullptr; + RefPtr dirHandle = + MakeAndAddRef(mGlobal, mMetadata); + + ASSERT_TRUE(dirHandle); + + IgnoredErrorResult rv; + RefPtr promise = dirHandle->Resolve(*dirHandle, rv); + + ASSERT_TRUE(rv.ErrorCodeIs(NS_ERROR_UNEXPECTED)); +} + +} // namespace mozilla::dom::fs::test diff --git a/dom/fs/test/gtest/api/TestFileSystemFileHandle.cpp b/dom/fs/test/gtest/api/TestFileSystemFileHandle.cpp new file mode 100644 index 000000000000..2cdeb5b29120 --- /dev/null +++ b/dom/fs/test/gtest/api/TestFileSystemFileHandle.cpp @@ -0,0 +1,127 @@ +/* -*- 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 "gtest/gtest.h" + +#include "FileSystemMocks.h" + +#include "mozilla/dom/FileSystemFileHandle.h" +#include "mozilla/dom/FileSystemFileHandleBinding.h" +#include "mozilla/dom/FileSystemHandle.h" +#include "mozilla/dom/FileSystemHandleBinding.h" +#include "mozilla/dom/Promise.h" + +#include "mozilla/UniquePtr.h" +#include "nsIGlobalObject.h" + +namespace mozilla::dom::fs::test { + +class TestFileSystemFileHandle : public ::testing::Test { + protected: + void SetUp() override { + mRequestHandler = MakeUnique(); + mMetadata = FileSystemEntryMetadata("file"_ns, u"File"_ns); + } + + nsIGlobalObject* mGlobal = GetGlobal(); + UniquePtr mRequestHandler; + FileSystemEntryMetadata mMetadata; +}; + +TEST_F(TestFileSystemFileHandle, constructFileHandleRefPointer) { + RefPtr fileHandle = MakeAndAddRef( + mGlobal, mMetadata, mRequestHandler.release()); + + ASSERT_TRUE(fileHandle); +} + +TEST_F(TestFileSystemFileHandle, isHandleKindFile) { + RefPtr fileHandle = MakeAndAddRef( + mGlobal, mMetadata, mRequestHandler.release()); + + ASSERT_TRUE(fileHandle); + + ASSERT_EQ(FileSystemHandleKind::File, fileHandle->Kind()); +} + +TEST_F(TestFileSystemFileHandle, isFileReturned) { + RefPtr fileHandle = MakeAndAddRef( + mGlobal, mMetadata, mRequestHandler.release()); + + ASSERT_TRUE(fileHandle); + + IgnoredErrorResult rv; + RefPtr promise = fileHandle->GetFile(rv); + + ASSERT_TRUE(rv.ErrorCodeIs(NS_OK)); +} + +TEST_F(TestFileSystemFileHandle, doesGetFileFailOnNullGlobal) { + mGlobal = nullptr; + RefPtr fileHandle = MakeAndAddRef( + mGlobal, mMetadata, mRequestHandler.release()); + + ASSERT_TRUE(fileHandle); + + IgnoredErrorResult rv; + RefPtr promise = fileHandle->GetFile(rv); + + ASSERT_TRUE(rv.ErrorCodeIs(NS_ERROR_UNEXPECTED)); +} + +TEST_F(TestFileSystemFileHandle, isWritableReturned) { + RefPtr fileHandle = MakeAndAddRef( + mGlobal, mMetadata, mRequestHandler.release()); + + ASSERT_TRUE(fileHandle); + + FileSystemCreateWritableOptions options; + IgnoredErrorResult rv; + RefPtr promise = fileHandle->CreateWritable(options, rv); + + ASSERT_TRUE(rv.ErrorCodeIs(NS_OK)); +} + +TEST_F(TestFileSystemFileHandle, doesCreateWritableFailOnNullGlobal) { + mGlobal = nullptr; + RefPtr fileHandle = MakeAndAddRef( + mGlobal, mMetadata, mRequestHandler.release()); + + ASSERT_TRUE(fileHandle); + + FileSystemCreateWritableOptions options; + IgnoredErrorResult rv; + RefPtr promise = fileHandle->CreateWritable(options, rv); + + ASSERT_TRUE(rv.ErrorCodeIs(NS_ERROR_UNEXPECTED)); +} + +TEST_F(TestFileSystemFileHandle, isSyncAccessHandleReturned) { + RefPtr fileHandle = MakeAndAddRef( + mGlobal, mMetadata, mRequestHandler.release()); + + ASSERT_TRUE(fileHandle); + + IgnoredErrorResult rv; + RefPtr promise = fileHandle->CreateSyncAccessHandle(rv); + + ASSERT_TRUE(rv.ErrorCodeIs(NS_OK)); +} + +TEST_F(TestFileSystemFileHandle, doesCreateSyncAccessHandleFailOnNullGlobal) { + mGlobal = nullptr; + RefPtr fileHandle = MakeAndAddRef( + mGlobal, mMetadata, mRequestHandler.release()); + + ASSERT_TRUE(fileHandle); + + IgnoredErrorResult rv; + RefPtr promise = fileHandle->CreateSyncAccessHandle(rv); + + ASSERT_TRUE(rv.ErrorCodeIs(NS_ERROR_UNEXPECTED)); +} + +} // namespace mozilla::dom::fs::test diff --git a/dom/fs/test/gtest/api/TestFileSystemHandle.cpp b/dom/fs/test/gtest/api/TestFileSystemHandle.cpp new file mode 100644 index 000000000000..8804e740c83a --- /dev/null +++ b/dom/fs/test/gtest/api/TestFileSystemHandle.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 "gtest/gtest.h" + +#include "FileSystemMocks.h" + +#include "mozilla/dom/FileSystemDirectoryHandle.h" +#include "mozilla/dom/FileSystemFileHandle.h" +#include "mozilla/dom/FileSystemHandle.h" +#include "mozilla/dom/FileSystemHandleBinding.h" +#include "nsIGlobalObject.h" + +namespace mozilla::dom::fs::test { + +class TestFileSystemHandle : public ::testing::Test { + protected: + void SetUp() override { + mDirMetadata = FileSystemEntryMetadata("dir"_ns, u"Directory"_ns); + mFileMetadata = FileSystemEntryMetadata("file"_ns, u"File"_ns); + } + + nsIGlobalObject* mGlobal = GetGlobal(); + FileSystemEntryMetadata mDirMetadata; + FileSystemEntryMetadata mFileMetadata; +}; + +TEST_F(TestFileSystemHandle, createAndDestroyHandles) { + RefPtr dirHandle = + new FileSystemDirectoryHandle(mGlobal, mDirMetadata); + RefPtr fileHandle = + new FileSystemFileHandle(mGlobal, mFileMetadata); + + EXPECT_TRUE(dirHandle); + EXPECT_TRUE(fileHandle); +} + +TEST_F(TestFileSystemHandle, areFileNamesAsExpected) { + RefPtr dirHandle = + new FileSystemDirectoryHandle(mGlobal, mDirMetadata); + RefPtr fileHandle = + new FileSystemFileHandle(mGlobal, mFileMetadata); + + auto GetEntryName = [](const RefPtr& aHandle) { + DOMString domName; + aHandle->GetName(domName); + nsString result; + domName.ToString(result); + return result; + }; + + const nsString& dirName = GetEntryName(dirHandle); + EXPECT_TRUE(mDirMetadata.entryName().Equals(dirName)); + + const nsString& fileName = GetEntryName(fileHandle); + EXPECT_TRUE(mFileMetadata.entryName().Equals(fileName)); +} + +TEST_F(TestFileSystemHandle, isParentObjectReturned) { + ASSERT_TRUE(mGlobal); + RefPtr dirHandle = + new FileSystemDirectoryHandle(mGlobal, mDirMetadata); + + ASSERT_EQ(mGlobal, dirHandle->GetParentObject()); +} + +TEST_F(TestFileSystemHandle, areHandleKindsAsExpected) { + RefPtr dirHandle = + new FileSystemDirectoryHandle(mGlobal, mDirMetadata); + RefPtr fileHandle = + new FileSystemFileHandle(mGlobal, mFileMetadata); + + EXPECT_EQ(FileSystemHandleKind::Directory, dirHandle->Kind()); + EXPECT_EQ(FileSystemHandleKind::File, fileHandle->Kind()); +} + +TEST_F(TestFileSystemHandle, isDifferentEntry) { + RefPtr dirHandle = + new FileSystemDirectoryHandle(mGlobal, mDirMetadata); + RefPtr fileHandle = + new FileSystemFileHandle(mGlobal, mFileMetadata); + + IgnoredErrorResult rv; + RefPtr promise = dirHandle->IsSameEntry(*fileHandle, rv); + ASSERT_TRUE(rv.ErrorCodeIs(NS_OK)); + ASSERT_TRUE(promise); + ASSERT_EQ(Promise::PromiseState::Rejected, promise->State()); +} + +TEST_F(TestFileSystemHandle, isSameEntry) { + RefPtr fileHandle = + new FileSystemFileHandle(mGlobal, mFileMetadata); + + IgnoredErrorResult rv; + RefPtr promise = fileHandle->IsSameEntry(*fileHandle, rv); + ASSERT_TRUE(rv.ErrorCodeIs(NS_OK)); + ASSERT_TRUE(promise); + ASSERT_EQ(Promise::PromiseState::Rejected, promise->State()); +} + +} // namespace mozilla::dom::fs::test diff --git a/dom/fs/test/gtest/api/moz.build b/dom/fs/test/gtest/api/moz.build new file mode 100644 index 000000000000..eb8416a3ba59 --- /dev/null +++ b/dom/fs/test/gtest/api/moz.build @@ -0,0 +1,21 @@ +# -*- 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/. + +UNIFIED_SOURCES = [ + "TestFileSystemDirectoryHandle.cpp", + "TestFileSystemFileHandle.cpp", + "TestFileSystemHandle.cpp", +] + +include("/ipc/chromium/chromium-config.mozbuild") + +FINAL_LIBRARY = "xul-gtest" + +LOCAL_INCLUDES += [ + "/dom/fs/api", + "/dom/fs/include", + "/dom/fs/test/gtest", +] diff --git a/dom/fs/test/gtest/child/TestFileSystemRequestHandler.cpp b/dom/fs/test/gtest/child/TestFileSystemRequestHandler.cpp new file mode 100644 index 000000000000..d0787acec008 --- /dev/null +++ b/dom/fs/test/gtest/child/TestFileSystemRequestHandler.cpp @@ -0,0 +1,94 @@ +/* -*- 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 "gtest/gtest.h" + +#include "FileSystemMocks.h" +#include "fs/FileSystemRequestHandler.h" +#include "mozilla/SpinEventLoopUntil.h" +#include "mozilla/UniquePtr.h" + +namespace mozilla::dom::fs::test { + +class TestFileSystemRequestHandler : public ::testing::Test { + protected: + void SetUp() override { + mListener = MakeAndAddRef(); + + mRequestHandler = MakeUnique(); + + mChild = FileSystemChildMetadata("parent"_ns, u"ChildName"_ns); + mEntry = FileSystemEntryMetadata("myid"_ns, u"EntryName"_ns); + mName = u"testDir"_ns; + mActor = MakeAndAddRef(); + } + + already_AddRefed GetDefaultPromise() { + IgnoredErrorResult rv; + RefPtr result = Promise::Create(mGlobal, rv); + mListener->ClearDone(); + result->AppendNativeHandler(mListener->AsHandler()); + + return result.forget(); + } + + nsIGlobalObject* mGlobal = GetGlobal(); + RefPtr mListener; + UniquePtr mRequestHandler; + + FileSystemChildMetadata mChild; + FileSystemEntryMetadata mEntry; + nsString mName; + RefPtr mActor; +}; + +TEST_F(TestFileSystemRequestHandler, isGetRootSuccessful) { + const Origin origin = "http://example.com"_ns; + RefPtr promise = GetDefaultPromise(); + mRequestHandler->GetRoot(origin, promise); + SpinEventLoopUntil("Promise is fulfilled or timeout"_ns, + [this]() { return mListener->IsDone(); }); +} + +TEST_F(TestFileSystemRequestHandler, isGetDirectoryHandleSuccessful) { + RefPtr promise = GetDefaultPromise(); + mRequestHandler->GetDirectoryHandle(mActor, mChild, + /* create */ true, promise); + SpinEventLoopUntil("Promise is fulfilled or timeout"_ns, + [this]() { return mListener->IsDone(); }); +} + +TEST_F(TestFileSystemRequestHandler, isGetFileHandleSuccessful) { + RefPtr promise = GetDefaultPromise(); + mRequestHandler->GetFileHandle(mActor, mChild, /* create */ true, promise); + SpinEventLoopUntil("Promise is fulfilled or timeout"_ns, + [this]() { return mListener->IsDone(); }); +} + +TEST_F(TestFileSystemRequestHandler, isGetFileSuccessful) { + RefPtr promise = GetDefaultPromise(); + mRequestHandler->GetFile(mActor, mEntry, promise); + SpinEventLoopUntil("Promise is fulfilled or timeout"_ns, + [this]() { return mListener->IsDone(); }); +} + +TEST_F(TestFileSystemRequestHandler, isGetEntriesSuccessful) { + RefPtr promise = GetDefaultPromise(); + ArrayAppendable sink; + mRequestHandler->GetEntries(mActor, mEntry.entryId(), /* page */ 0, promise, + sink); + SpinEventLoopUntil("Promise is fulfilled or timeout"_ns, + [this]() { return mListener->IsDone(); }); +} + +TEST_F(TestFileSystemRequestHandler, isRemoveEntrySuccessful) { + RefPtr promise = GetDefaultPromise(); + mRequestHandler->RemoveEntry(mActor, mChild, /* recursive */ true, promise); + SpinEventLoopUntil("Promise is fulfilled or timeout"_ns, + [this]() { return mListener->IsDone(); }); +} + +} // namespace mozilla::dom::fs::test diff --git a/dom/fs/test/gtest/child/moz.build b/dom/fs/test/gtest/child/moz.build new file mode 100644 index 000000000000..68858b1cce6e --- /dev/null +++ b/dom/fs/test/gtest/child/moz.build @@ -0,0 +1,19 @@ +# -*- 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/. + +UNIFIED_SOURCES = [ + "TestFileSystemRequestHandler.cpp", +] + +include("/ipc/chromium/chromium-config.mozbuild") + +FINAL_LIBRARY = "xul-gtest" + +LOCAL_INCLUDES += [ + "/dom/fs/child", + "/dom/fs/include", + "/dom/fs/test/gtest", +] diff --git a/dom/fs/test/gtest/moz.build b/dom/fs/test/gtest/moz.build new file mode 100644 index 000000000000..49960c8cc5db --- /dev/null +++ b/dom/fs/test/gtest/moz.build @@ -0,0 +1,22 @@ +# -*- 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/. + +TEST_DIRS += [ + "api", + "child", +] + +UNIFIED_SOURCES = [ + "FileSystemMocks.cpp", +] + +include("/ipc/chromium/chromium-config.mozbuild") + +FINAL_LIBRARY = "xul-gtest" + +LOCAL_INCLUDES += [ + "/dom/fs/include", +] diff --git a/dom/fs/test/moz.build b/dom/fs/test/moz.build index 8154c78eb78f..b31669735c70 100644 --- a/dom/fs/test/moz.build +++ b/dom/fs/test/moz.build @@ -26,3 +26,7 @@ TEST_HARNESS_FILES.xpcshell.dom.fs.test.common += [ "common/nsresult.js", "common/test_basics.js", ] + +TEST_DIRS += [ + "gtest", +] diff --git a/ipc/glue/BackgroundParentImpl.cpp b/ipc/glue/BackgroundParentImpl.cpp index 6409481278a3..cc6662eb37e9 100644 --- a/ipc/glue/BackgroundParentImpl.cpp +++ b/ipc/glue/BackgroundParentImpl.cpp @@ -14,6 +14,7 @@ #include "mozilla/RDDProcessManager.h" #include "mozilla/ipc/UtilityProcessManager.h" #include "mozilla/RefPtr.h" +#include "mozilla/dom/BackgroundFileSystemParent.h" #include "mozilla/dom/BackgroundSessionStorageServiceParent.h" #include "mozilla/dom/ClientManagerActors.h" #include "mozilla/dom/ContentParent.h" @@ -183,6 +184,14 @@ auto BackgroundParentImpl::AllocPBackgroundIDBFactoryParent( return AllocPBackgroundIDBFactoryParent(aLoggingInfo); } +auto BackgroundParentImpl::AllocPBackgroundFileSystemParent() + -> already_AddRefed { + AssertIsInMainProcess(); + AssertIsOnBackgroundThread(); + + return MakeAndAddRef(); +} + mozilla::ipc::IPCResult BackgroundParentImpl::RecvPBackgroundIDBFactoryConstructor( PBackgroundIDBFactoryParent* aActor, const LoggingInfo& aLoggingInfo) { diff --git a/ipc/glue/BackgroundParentImpl.h b/ipc/glue/BackgroundParentImpl.h index 59dfe03a1afd..5498987bbeab 100644 --- a/ipc/glue/BackgroundParentImpl.h +++ b/ipc/glue/BackgroundParentImpl.h @@ -28,6 +28,9 @@ class BackgroundParentImpl : public PBackgroundParent { bool DeallocPBackgroundTestParent(PBackgroundTestParent* aActor) override; + already_AddRefed + AllocPBackgroundFileSystemParent() override; + already_AddRefed AllocPBackgroundIDBFactoryParent(const LoggingInfo& aLoggingInfo) override; diff --git a/ipc/glue/PBackground.ipdl b/ipc/glue/PBackground.ipdl index 7c1c702618d3..5dfab41b7ee7 100644 --- a/ipc/glue/PBackground.ipdl +++ b/ipc/glue/PBackground.ipdl @@ -3,6 +3,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ include protocol PBackgroundDataBridge; +include protocol PBackgroundFileSystem; include protocol PBackgroundIDBFactory; include protocol PBackgroundIndexedDBUtils; include protocol PBackgroundSDBConnection; @@ -81,6 +82,7 @@ namespace ipc { sync protocol PBackground { manages PBackgroundDataBridge; + manages PBackgroundFileSystem; manages PBackgroundIDBFactory; manages PBackgroundIndexedDBUtils; manages PBackgroundSDBConnection; @@ -132,6 +134,8 @@ parent: // Only called at startup during mochitests to check the basic infrastructure. async PBackgroundTest(nsCString testArg); + async PBackgroundFileSystem(); + async PBackgroundDataBridge(uint64_t channelID); async PBackgroundIDBFactory(LoggingInfo loggingInfo);