Bug 1768050: Test request handler. r=jesup

Depends on D147273

Differential Revision: https://phabricator.services.mozilla.com/D149983
This commit is contained in:
Randell Jesup 2022-07-06 18:00:04 +00:00
parent 3a934f66ac
commit 96bbb1edea
19 changed files with 582 additions and 114 deletions

View File

@ -68,6 +68,7 @@ class FileSystemHandle : public nsISupports, public nsWrapperCache {
virtual ~FileSystemHandle() = default;
nsCOMPtr<nsIGlobalObject> mGlobal;
RefPtr<FileSystemActorHolder> mActor;
const fs::FileSystemEntryMetadata mMetadata;

View File

@ -0,0 +1,103 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "fs/FileSystemChildFactory.h"
#include "mozilla/dom/POriginPrivateFileSystemChild.h"
#include "mozilla/dom/OriginPrivateFileSystemChild.h"
#include "mozilla/RefPtr.h"
#include "nsISupportsImpl.h"
namespace mozilla::dom::fs {
namespace {
using ResolveGetHandle =
mozilla::ipc::ResolveCallback<fs::FileSystemGetHandleResponse>;
using ResolveGetFile =
mozilla::ipc::ResolveCallback<fs::FileSystemGetFileResponse>;
using ResolveResolve =
mozilla::ipc::ResolveCallback<fs::FileSystemResolveResponse>;
using ResolveGetEntries =
mozilla::ipc::ResolveCallback<fs::FileSystemGetEntriesResponse>;
using ResolveRemoveEntry =
mozilla::ipc::ResolveCallback<fs::FileSystemRemoveEntryResponse>;
using DoReject = mozilla::ipc::RejectCallback;
class OriginPrivateFileSystemChildImpl final
: public OriginPrivateFileSystemChild {
public:
class TOriginPrivateFileSystem final : public POriginPrivateFileSystemChild {
NS_INLINE_DECL_REFCOUNTING(TOriginPrivateFileSystem);
protected:
~TOriginPrivateFileSystem() = default;
};
OriginPrivateFileSystemChildImpl() : mImpl(new TOriginPrivateFileSystem()) {}
NS_INLINE_DECL_REFCOUNTING(OriginPrivateFileSystemChildImpl, override)
void SendGetDirectoryHandle(const fs::FileSystemGetHandleRequest& aRequest,
ResolveGetHandle&& aResolve,
DoReject&& aReject) override {
mImpl->SendGetDirectoryHandle(aRequest,
std::forward<ResolveGetHandle>(aResolve),
std::forward<DoReject>(aReject));
}
void SendGetFileHandle(const fs::FileSystemGetHandleRequest& aRequest,
ResolveGetHandle&& aResolve,
DoReject&& aReject) override {
mImpl->SendGetFileHandle(aRequest, std::forward<ResolveGetHandle>(aResolve),
std::forward<DoReject>(aReject));
}
void SendGetFile(const fs::FileSystemGetFileRequest& aRequest,
ResolveGetFile&& aResolve, DoReject&& aReject) override {
mImpl->SendGetFile(aRequest, std::forward<ResolveGetFile>(aResolve),
std::forward<DoReject>(aReject));
}
void SendResolve(const fs::FileSystemResolveRequest& aRequest,
ResolveResolve&& aResolve, DoReject&& aReject) override {
mImpl->SendResolve(aRequest, std::forward<ResolveResolve>(aResolve),
std::forward<DoReject>(aReject));
}
void SendGetEntries(const fs::FileSystemGetEntriesRequest& aRequest,
ResolveGetEntries&& aResolve,
DoReject&& aReject) override {
mImpl->SendGetEntries(aRequest, std::forward<ResolveGetEntries>(aResolve),
std::forward<DoReject>(aReject));
}
void SendRemoveEntry(const fs::FileSystemRemoveEntryRequest& aRequest,
ResolveRemoveEntry&& aResolve,
DoReject&& aReject) override {
mImpl->SendRemoveEntry(aRequest, std::forward<ResolveRemoveEntry>(aResolve),
std::forward<DoReject>(aReject));
}
virtual bool IsCloseable() const override { return mImpl->IsOnCxxStack(); }
void Close() override { mImpl->Close(); }
POriginPrivateFileSystemChild* AsBindable() override { return mImpl.get(); };
protected:
~OriginPrivateFileSystemChildImpl() = default;
const RefPtr<TOriginPrivateFileSystem> mImpl;
}; // class OriginPrivateFileSystemChildImpl
} // namespace
already_AddRefed<OriginPrivateFileSystemChild> FileSystemChildFactory::Create()
const {
return MakeAndAddRef<OriginPrivateFileSystemChildImpl>();
}
} // namespace mozilla::dom::fs

View File

@ -15,9 +15,9 @@
#include "mozilla/dom/FileSystemDirectoryHandle.h"
#include "mozilla/dom/Promise.h"
#include "mozilla/dom/WorkerPrivate.h"
#include "mozilla/ipc/Endpoint.h"
#include "mozilla/ipc/BackgroundChild.h"
#include "mozilla/ipc/BackgroundUtils.h"
#include "mozilla/ipc/Endpoint.h"
#include "mozilla/ipc/PBackgroundChild.h"
namespace mozilla::dom::fs {
@ -192,6 +192,11 @@ void ResolveCallback(
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());
const auto& status = aResponse.get_nsresult();
if (NS_ERROR_FILE_ACCESS_DENIED == status) {
@ -279,7 +284,7 @@ void IPCRejectReporter(mozilla::ipc::ResponseRejectReason aReason) {
}
}
void RejectHandler(
void RejectCallback(
RefPtr<Promise> aPromise, // NOLINT(performance-unnecessary-value-param)
mozilla::ipc::ResponseRejectReason aReason) {
IPCRejectReporter(aReason);
@ -291,7 +296,7 @@ mozilla::ipc::RejectCallback GetRejectCallback(
RefPtr<Promise> aPromise) { // NOLINT(performance-unnecessary-value-param)
return static_cast<mozilla::ipc::RejectCallback>(
// NOLINTNEXTLINE(modernize-avoid-bind)
std::bind(RejectHandler, aPromise, std::placeholders::_1));
std::bind(RejectCallback, aPromise, std::placeholders::_1));
}
} // namespace
@ -309,8 +314,8 @@ void FileSystemRequestHandler::GetRoot(
POriginPrivateFileSystem::CreateEndpoints(&parentEp, &childEp));
RefPtr<FileSystemActorHolder> actor =
MakeAndAddRef<FileSystemActorHolder>(new OriginPrivateFileSystemChild());
if (!childEp.Bind(actor->Actor())) {
MakeAndAddRef<FileSystemActorHolder>(mChildFactory->Create().take());
if (!childEp.Bind(actor->Actor()->AsBindable())) {
aPromise->MaybeRejectWithUndefined();
return;
}
@ -348,7 +353,9 @@ void FileSystemRequestHandler::GetDirectoryHandle(
auto&& onReject = GetRejectCallback(aPromise);
QM_TRY(OkIf(aActor), QM_VOID);
QM_TRY(OkIf(aActor), QM_VOID, [aPromise](const auto&) {
aPromise->MaybeRejectWithUnknownError("Invalid actor");
});
aActor->Actor()->SendGetDirectoryHandle(request, std::move(onResolve),
std::move(onReject));
}
@ -368,7 +375,9 @@ void FileSystemRequestHandler::GetFileHandle(
auto&& onReject = GetRejectCallback(aPromise);
QM_TRY(OkIf(aActor), QM_VOID);
QM_TRY(OkIf(aActor), QM_VOID, [aPromise](const auto&) {
aPromise->MaybeRejectWithUnknownError("Invalid actor");
});
aActor->Actor()->SendGetFileHandle(request, std::move(onResolve),
std::move(onReject));
}
@ -387,7 +396,9 @@ void FileSystemRequestHandler::GetFile(
auto&& onReject = GetRejectCallback(aPromise);
QM_TRY(OkIf(aActor), QM_VOID);
QM_TRY(OkIf(aActor), QM_VOID, [aPromise](const auto&) {
aPromise->MaybeRejectWithUnknownError("Invalid actor");
});
aActor->Actor()->SendGetFile(request, std::move(onResolve),
std::move(onReject));
}
@ -414,7 +425,9 @@ void FileSystemRequestHandler::GetEntries(
auto&& onReject = GetRejectCallback(aPromise);
QM_TRY(OkIf(aActor), QM_VOID);
QM_TRY(OkIf(aActor), QM_VOID, [aPromise](const auto&) {
aPromise->MaybeRejectWithUnknownError("Invalid actor");
});
aActor->Actor()->SendGetEntries(request, std::move(onResolve),
std::move(onReject));
}
@ -433,7 +446,9 @@ void FileSystemRequestHandler::RemoveEntry(
auto&& onReject = GetRejectCallback(aPromise);
QM_TRY(OkIf(aActor), QM_VOID);
QM_TRY(OkIf(aActor), QM_VOID, [aPromise](const auto&) {
aPromise->MaybeRejectWithUnknownError("Invalid actor");
});
aActor->Actor()->SendRemoveEntry(request, std::move(onResolve),
std::move(onReject));
}

View File

@ -8,12 +8,52 @@
#define DOM_FS_CHILD_ORIGINPRIVATEFILESYSTEMCHILD_H_
#include "mozilla/dom/POriginPrivateFileSystemChild.h"
#include "nsISupports.h"
#include "mozilla/UniquePtr.h"
#include "nsISupportsImpl.h"
namespace mozilla::dom {
class OriginPrivateFileSystemChild : public POriginPrivateFileSystemChild {
NS_INLINE_DECL_REFCOUNTING(OriginPrivateFileSystemChild);
class OriginPrivateFileSystemChild {
public:
NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING
virtual void SendGetDirectoryHandle(
const fs::FileSystemGetHandleRequest& request,
mozilla::ipc::ResolveCallback<fs::FileSystemGetHandleResponse>&& aResolve,
mozilla::ipc::RejectCallback&& aReject) = 0;
virtual void SendGetFileHandle(
const fs::FileSystemGetHandleRequest& request,
mozilla::ipc::ResolveCallback<fs::FileSystemGetHandleResponse>&& aResolve,
mozilla::ipc::RejectCallback&& aReject) = 0;
virtual void SendGetFile(
const fs::FileSystemGetFileRequest& request,
mozilla::ipc::ResolveCallback<fs::FileSystemGetFileResponse>&& aResolve,
mozilla::ipc::RejectCallback&& aReject) = 0;
virtual void SendResolve(
const fs::FileSystemResolveRequest& request,
mozilla::ipc::ResolveCallback<fs::FileSystemResolveResponse>&& aResolve,
mozilla::ipc::RejectCallback&& aReject) = 0;
virtual void SendGetEntries(
const fs::FileSystemGetEntriesRequest& request,
mozilla::ipc::ResolveCallback<fs::FileSystemGetEntriesResponse>&&
aResolve,
mozilla::ipc::RejectCallback&& aReject) = 0;
virtual void SendRemoveEntry(
const fs::FileSystemRemoveEntryRequest& request,
mozilla::ipc::ResolveCallback<fs::FileSystemRemoveEntryResponse>&&
aResolve,
mozilla::ipc::RejectCallback&& aReject) = 0;
virtual bool IsCloseable() const = 0;
virtual void Close() = 0;
virtual POriginPrivateFileSystemChild* AsBindable() = 0;
protected:
virtual ~OriginPrivateFileSystemChild() = default;

View File

@ -10,6 +10,7 @@ EXPORTS.mozilla.dom += [
]
UNIFIED_SOURCES += [
"FileSystemChildFactory.cpp",
"FileSystemRequestHandler.cpp",
]

View File

@ -0,0 +1,33 @@
/* -*- 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_FILESYSTEMCHILDFACTORY_H_
#define DOM_FS_FILESYSTEMCHILDFACTORY_H_
#include "mozilla/AlreadyAddRefed.h"
class nsIGlobalObject;
namespace mozilla {
class ErrorResult;
namespace dom {
class OriginPrivateFileSystemChild;
} // namespace dom
} // namespace mozilla
namespace mozilla::dom::fs {
class FileSystemChildFactory {
public:
virtual already_AddRefed<OriginPrivateFileSystemChild> Create() const;
virtual ~FileSystemChildFactory() = default;
};
} // namespace mozilla::dom::fs
#endif // DOM_FS_FILESYSTEMCHILDFACTORY_H_

View File

@ -7,16 +7,16 @@
#ifndef DOM_FS_CHILD_FILESYSTEMREQUESTHANDLER_H_
#define DOM_FS_CHILD_FILESYSTEMREQUESTHANDLER_H_
#include "nsStringFwd.h"
#include "mozilla/dom/FileSystemActorHolder.h"
#include "fs/FileSystemChildFactory.h"
#include "mozilla/dom/FileSystemTypes.h"
#include "mozilla/dom/FileSystemHandle.h"
#include "nsStringFwd.h"
template <class T>
class RefPtr;
namespace mozilla::dom {
class FileSystemHandle;
class Promise;
class OriginPrivateFileSystemChild;
@ -31,6 +31,12 @@ class ArrayAppendable {};
class FileSystemRequestHandler {
public:
explicit FileSystemRequestHandler(FileSystemChildFactory* aChildFactory)
: mChildFactory(aChildFactory) {}
FileSystemRequestHandler()
: FileSystemRequestHandler(new FileSystemChildFactory()) {}
virtual void GetRoot(RefPtr<Promise> aPromise);
virtual void GetDirectoryHandle(RefPtr<FileSystemActorHolder>& aActor,
@ -54,6 +60,10 @@ class FileSystemRequestHandler {
bool aRecursive, RefPtr<Promise> aPromise);
virtual ~FileSystemRequestHandler() = default;
protected:
const UniquePtr<FileSystemChildFactory> mChildFactory;
}; // class FileSystemRequestHandler
} // namespace mozilla::dom::fs

View File

@ -9,11 +9,11 @@
#include "nsNetCID.h"
#include "mozilla/dom/FileSystemTypes.h"
#include "mozilla/dom/quota/QuotaManager.h"
#include "mozilla/ipc/Endpoint.h"
#include "mozilla/ipc/FileDescriptorUtils.h"
#include "mozilla/Maybe.h"
#include "mozilla/MozPromise.h"
#include "mozilla/dom/quota/QuotaManager.h"
#include "mozilla/StaticPrefs_dom.h"
#include "nsServiceManagerUtils.h"
@ -53,12 +53,14 @@ using RootPromise = MozPromise<fs::FileSystemGetRootResponse, nsresult, false>;
mozilla::ipc::IPCResult BackgroundFileSystemParent::RecvGetRoot(
Endpoint<POriginPrivateFileSystemParent>&& aParentEp,
GetRootResolver&& aResolver) {
if (!StaticPrefs::dom_fs_enabled()) {
return IPC_FAIL(this, "OPFS is disabled");
}
if (!aParentEp.IsValid()) {
return IPC_FAIL(this, "Invalid endpoint");
}
QM_TRY(
OkIf(StaticPrefs::dom_fs_enabled()), IPC_OK(), [aResolver](const auto&) {
aResolver(fs::FileSystemGetRootResponse(NS_ERROR_DOM_NOT_ALLOWED_ERR));
});
QM_TRY(OkIf(aParentEp.IsValid()), IPC_OK(), [aResolver](const auto&) {
aResolver(fs::FileSystemGetRootResponse(NS_ERROR_INVALID_ARG));
});
nsAutoCString origin =
quota::QuotaManager::GetOriginFromValidatedPrincipalInfo(mPrincipalInfo);
@ -66,12 +68,11 @@ mozilla::ipc::IPCResult BackgroundFileSystemParent::RecvGetRoot(
// This opens the quota manager, which has to be done on PBackground
auto res =
fs::data::FileSystemDataManager::CreateFileSystemDataManager(origin);
if (NS_WARN_IF(res.isErr())) {
MOZ_ASSERT(false, "Can't create FileSystemDataManager");
aResolver(fs::FileSystemGetRootResponse(NS_ERROR_FAILURE));
QM_TRY(OkIf(res.isErr()), IPC_OK(), [aResolver](const auto&) {
aResolver(fs::FileSystemGetRootResponse(NS_ERROR_UNEXPECTED));
});
return IPC_OK();
}
UniquePtr<fs::data::FileSystemDataManager> data(res.unwrap());
nsCOMPtr<nsIThread> pbackground = NS_GetCurrentThread();
nsCOMPtr<nsIEventTarget> target =
@ -87,8 +88,8 @@ mozilla::ipc::IPCResult BackgroundFileSystemParent::RecvGetRoot(
// new channel, but that's an extra IPC instead.
InvokeAsync(
taskqueue, __func__,
[origin, parentEp = std::move(aParentEp), aResolver, data = res.unwrap(),
taskqueue, pbackground]() mutable {
[origin, parentEp = std::move(aParentEp), aResolver,
data = std::move(data), taskqueue, pbackground]() mutable {
RefPtr<OriginPrivateFileSystemParent> parent =
new OriginPrivateFileSystemParent(taskqueue);
if (!parentEp.Bind(parent)) {

View File

@ -9,8 +9,8 @@
#include "mozilla/ipc/BackgroundUtils.h"
#include "mozilla/dom/PBackgroundFileSystemParent.h"
#include "mozilla/ipc/PBackgroundSharedTypes.h"
#include "mozilla/dom/POriginPrivateFileSystemParent.h"
#include "mozilla/ipc/PBackgroundSharedTypes.h"
#include "mozilla/TaskQueue.h"
#include "nsISupports.h"
@ -23,7 +23,7 @@ class BackgroundFileSystemParent : public PBackgroundFileSystemParent {
: mPrincipalInfo(aPrincipalInfo) {}
mozilla::ipc::IPCResult RecvGetRoot(
Endpoint<POriginPrivateFileSystemParent>&& aParentEp,
mozilla::ipc::Endpoint<POriginPrivateFileSystemParent>&& aParentEp,
GetRootResolver&& aResolver);
NS_INLINE_DECL_REFCOUNTING(BackgroundFileSystemParent)

View File

@ -5,11 +5,11 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/dom/OriginPrivateFileSystemChild.h"
#include "mozilla/ipc/ActorHolder.h"
#include "mozilla/ipc/ToplevelActorHolder.h"
namespace mozilla::dom {
using FileSystemActorHolder =
mozilla::ipc::TopLevelActorHolder<OriginPrivateFileSystemChild>;
mozilla::ipc::ToplevelActorHolder<OriginPrivateFileSystemChild>;
} // namespace mozilla::dom

View File

@ -7,8 +7,6 @@ include protocol POriginPrivateFileSystem;
using mozilla::dom::fs::EntryId from "mozilla/dom/FileSystemTypes.h";
using struct mozilla::void_t from "mozilla/ipc/IPCCore.h";
namespace mozilla {
namespace dom {
namespace fs {
@ -30,7 +28,6 @@ async protocol PBackgroundFileSystem
/**
* This will set up a POriginPrivateFileSystem IPC connection
*/
async GetRoot(Endpoint<POriginPrivateFileSystemParent> aParentEP) returns(FileSystemGetRootResponse handle);
child:

View File

@ -26,7 +26,8 @@ struct FileSystemEntryMetadata
};
/**
* Identifies a file or a directory with its parent identifier and user provided name.
* Identifies a file or a directory with its parent identifier and
* user provided name.
*/
struct FileSystemChildMetadata
{
@ -207,7 +208,8 @@ async protocol POriginPrivateFileSystem
*
* @returns error or entry handle
*/
async GetDirectoryHandle(FileSystemGetHandleRequest request) returns(FileSystemGetHandleResponse handle);
async GetDirectoryHandle(FileSystemGetHandleRequest request)
returns(FileSystemGetHandleResponse handle);
/**
* Initiates an asynchronous request for the handle to
@ -225,7 +227,8 @@ async protocol POriginPrivateFileSystem
*
* @returns error or entry handle
*/
async GetFileHandle(FileSystemGetHandleRequest request) returns(FileSystemGetHandleResponse handle);
async GetFileHandle(FileSystemGetHandleRequest request)
returns(FileSystemGetHandleResponse handle);
/**
* Initiates an asynchronous request for a read-only object representing the
@ -242,17 +245,20 @@ async protocol POriginPrivateFileSystem
*
* @returns error or file object
*/
async GetFile(FileSystemGetFileRequest request) returns(FileSystemGetFileResponse response);
async GetFile(FileSystemGetFileRequest request)
returns(FileSystemGetFileResponse response);
/**
* TODO: documentation
*/
async GetAccessHandle(FileSystemGetFileRequest request) returns(FileSystemGetAccessHandleResponse fileData);
async GetAccessHandle(FileSystemGetFileRequest request)
returns(FileSystemGetAccessHandleResponse fileData);
/**
* TODO: documentation
*/
async GetWritable(FileSystemGetFileRequest request) returns(FileSystemGetAccessHandleResponse fileData);
async GetWritable(FileSystemGetFileRequest request)
returns(FileSystemGetAccessHandleResponse fileData);
/**
* Initiates an asynchronous request for the file system path
@ -262,32 +268,35 @@ async protocol POriginPrivateFileSystem
*
* @returns error or file system path
*/
async Resolve(FileSystemResolveRequest request) returns(FileSystemResolveResponse response);
async Resolve(FileSystemResolveRequest request)
returns(FileSystemResolveResponse response);
/**
* Initiates an asynchronous request for an iterator to the child entries under
* the current directory handle.
* Initiates an asynchronous request for an iterator to the child entries
* under the calling 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.
* 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);
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.
* Initiates an asynchronous request to delete a directory or file with a
* given name under the calling 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.
* 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.
*
@ -295,7 +304,9 @@ async protocol POriginPrivateFileSystem
*
* @returns error information
*/
async RemoveEntry(FileSystemRemoveEntryRequest request) returns(FileSystemRemoveEntryResponse response);
async RemoveEntry(FileSystemRemoveEntryRequest request)
returns(FileSystemRemoveEntryResponse response);
/**
* TODO: documentation
* So we can implement exclusive access
@ -303,10 +314,12 @@ async protocol POriginPrivateFileSystem
async CloseFile(FileSystemGetFileRequest request);
/**
* Request for quota needed to finish a write, beyond the amount preallocated at creation of the AccessHandle.
* While officially async, this is used in a sync manner from write() by spinning an event loop.
* Request for quota needed to finish a write, beyond the amount preallocated
* at creation of the AccessHandle. While officially async, this is used in a
* sync manner from write() by spinning an event loop.
*/
async NeedQuota(FileSystemQuotaRequest aRequest) returns (uint64_t aQuotaGranted);
async NeedQuota(FileSystemQuotaRequest aRequest)
returns (uint64_t aQuotaGranted);
child:

View File

@ -11,12 +11,15 @@
#include "gmock/gmock.h"
#include "TestHelpers.h"
#include "fs/FileSystemRequestHandler.h"
#include "fs/FileSystemChildFactory.h"
#include "mozilla/dom/BindingDeclarations.h"
#include "mozilla/dom/BindingUtils.h"
#include "mozilla/dom/DOMException.h"
#include "mozilla/dom/DOMExceptionBinding.h"
#include "mozilla/dom/OriginPrivateFileSystemChild.h"
#include "mozilla/dom/Promise.h"
#include "mozilla/dom/PromiseNativeHandler.h"
#include "mozilla/dom/ScriptSettings.h"
@ -24,6 +27,7 @@
#include "mozilla/ScopeExit.h"
#include "mozilla/UniquePtr.h"
#include "nsIGlobalObject.h"
#include "nsISupports.h"
#include "nsISupportsImpl.h"
#include "nsITimer.h"
@ -39,29 +43,36 @@ nsIGlobalObject* GetGlobal();
class MockFileSystemRequestHandler : public FileSystemRequestHandler {
public:
MOCK_METHOD1(GetRoot, void(RefPtr<Promise> aPromise));
MOCK_METHOD(void, GetRoot, (RefPtr<Promise> aPromise), (override));
MOCK_METHOD4(GetDirectoryHandle,
void(RefPtr<FileSystemActorHolder>& aActor,
const FileSystemChildMetadata& aDirectory, bool aCreate,
RefPtr<Promise> aPromise));
MOCK_METHOD(void, GetDirectoryHandle,
(RefPtr<FileSystemActorHolder> & aActor,
const FileSystemChildMetadata& aDirectory, bool aCreate,
RefPtr<Promise> aPromise),
(override));
MOCK_METHOD4(GetFileHandle, void(RefPtr<FileSystemActorHolder>& aActor,
const FileSystemChildMetadata& aFile,
bool aCreate, RefPtr<Promise> aPromise));
MOCK_METHOD(void, GetFileHandle,
(RefPtr<FileSystemActorHolder> & aActor,
const FileSystemChildMetadata& aFile, bool aCreate,
RefPtr<Promise> aPromise),
(override));
MOCK_METHOD3(GetFile, void(RefPtr<FileSystemActorHolder>& aActor,
const FileSystemEntryMetadata& aFile,
RefPtr<Promise> aPromise));
MOCK_METHOD(void, GetFile,
(RefPtr<FileSystemActorHolder> & aActor,
const FileSystemEntryMetadata& aFile, RefPtr<Promise> aPromise),
(override));
MOCK_METHOD5(GetEntries,
void(RefPtr<FileSystemActorHolder>& aActor,
const EntryId& aDirectory, PageNumber aPage,
RefPtr<Promise> aPromise, ArrayAppendable& aSink));
MOCK_METHOD(void, GetEntries,
(RefPtr<FileSystemActorHolder> & aActor,
const EntryId& aDirectory, PageNumber aPage,
RefPtr<Promise> aPromise, ArrayAppendable& aSink),
(override));
MOCK_METHOD4(RemoveEntry, void(RefPtr<FileSystemActorHolder>& aActor,
const FileSystemChildMetadata& aEntry,
bool aRecursive, RefPtr<Promise> aPromise));
MOCK_METHOD(void, RemoveEntry,
(RefPtr<FileSystemActorHolder> & aActor,
const FileSystemChildMetadata& aEntry, bool aRecursive,
RefPtr<Promise> aPromise),
(override));
};
class WaitablePromiseListener {
@ -83,7 +94,11 @@ template <class SuccessHandler, class ErrorHandler,
class TestPromiseListener : public PromiseNativeHandler,
public WaitablePromiseListener {
public:
TestPromiseListener() : mIsDone(std::make_shared<bool>(false)), mTimer() {
TestPromiseListener()
: mIsDone(std::make_shared<bool>(false)),
mTimer(),
mOnSuccess(),
mOnError() {
ClearDone();
}
@ -102,17 +117,23 @@ class TestPromiseListener : public PromiseNativeHandler,
void ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue,
ErrorResult& aRv) override {
mozilla::ScopeExit flagAsDone([isDone = mIsDone] { *isDone = true; });
mozilla::ScopeExit flagAsDone([isDone = mIsDone, timer = mTimer] {
timer->Cancel();
*isDone = true;
});
SuccessHandler{}();
mOnSuccess();
}
void RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue,
ErrorResult& aRv) override {
mozilla::ScopeExit flagAsDone([isDone = mIsDone] { *isDone = true; });
mozilla::ScopeExit flagAsDone([isDone = mIsDone, timer = mTimer] {
timer->Cancel();
*isDone = true;
});
if (aValue.isInt32()) {
ErrorHandler{}(static_cast<nsresult>(aValue.toInt32()));
mOnError(static_cast<nsresult>(aValue.toInt32()));
return;
}
@ -122,7 +143,7 @@ class TestPromiseListener : public PromiseNativeHandler,
RefPtr<Exception> exception;
UNWRAP_OBJECT(Exception, exceptionObject, exception);
if (exception) {
ErrorHandler{}(static_cast<nsresult>(exception->Result()));
mOnError(static_cast<nsresult>(exception->Result()));
return;
}
}
@ -136,6 +157,7 @@ class TestPromiseListener : public PromiseNativeHandler,
}
auto timerCallback = [isDone = mIsDone](nsITimer* aTimer) {
*isDone = true;
FAIL() << "Timed out!";
};
const char* timerName = "fs::TestPromiseListener::ClearDone";
auto res = NS_NewTimerWithCallback(timerCallback, MilliSeconds,
@ -149,12 +171,106 @@ class TestPromiseListener : public PromiseNativeHandler,
PromiseNativeHandler* AsHandler() override { return this; }
SuccessHandler& GetSuccessHandler() { return mOnSuccess; }
SuccessHandler& GetErrorHandler() { return mOnError; }
protected:
virtual ~TestPromiseListener() = default;
std::shared_ptr<bool> mIsDone; // We pass this to a callback
nsCOMPtr<nsITimer> mTimer;
SuccessHandler mOnSuccess;
ErrorHandler mOnError;
};
class TestOriginPrivateFileSystemChild : public OriginPrivateFileSystemChild {
public:
NS_INLINE_DECL_REFCOUNTING(TestOriginPrivateFileSystemChild, override)
void ManualRelease() {
if (mRefCnt > 0u) {
--mRefCnt;
}
}
MOCK_METHOD(
void, SendGetDirectoryHandle,
(const FileSystemGetHandleRequest& request,
mozilla::ipc::ResolveCallback<FileSystemGetHandleResponse>&& aResolve,
mozilla::ipc::RejectCallback&& aReject),
(override));
MOCK_METHOD(
void, SendGetFileHandle,
(const FileSystemGetHandleRequest& request,
mozilla::ipc::ResolveCallback<FileSystemGetHandleResponse>&& aResolve,
mozilla::ipc::RejectCallback&& aReject),
(override));
MOCK_METHOD(
void, SendGetFile,
(const FileSystemGetFileRequest& request,
mozilla::ipc::ResolveCallback<FileSystemGetFileResponse>&& aResolve,
mozilla::ipc::RejectCallback&& aReject),
(override));
MOCK_METHOD(
void, SendResolve,
(const FileSystemResolveRequest& request,
mozilla::ipc::ResolveCallback<FileSystemResolveResponse>&& aResolve,
mozilla::ipc::RejectCallback&& aReject),
(override));
MOCK_METHOD(
void, SendGetEntries,
(const FileSystemGetEntriesRequest& request,
mozilla::ipc::ResolveCallback<FileSystemGetEntriesResponse>&& aResolve,
mozilla::ipc::RejectCallback&& aReject),
(override));
MOCK_METHOD(
void, SendRemoveEntry,
(const FileSystemRemoveEntryRequest& request,
mozilla::ipc::ResolveCallback<FileSystemRemoveEntryResponse>&& aResolve,
mozilla::ipc::RejectCallback&& aReject),
(override));
MOCK_METHOD(bool, IsCloseable, (), (const, override));
MOCK_METHOD(void, Close, (), (override));
MOCK_METHOD(POriginPrivateFileSystemChild*, AsBindable, (), (override));
protected:
virtual ~TestOriginPrivateFileSystemChild() = default;
};
class TestFileSystemChildFactory final : public FileSystemChildFactory {
public:
explicit TestFileSystemChildFactory(TestOriginPrivateFileSystemChild* aChild)
: mChild(aChild) {}
already_AddRefed<OriginPrivateFileSystemChild> Create() const override {
return RefPtr<TestOriginPrivateFileSystemChild>(mChild).forget();
}
~TestFileSystemChildFactory() = default;
private:
TestOriginPrivateFileSystemChild* mChild;
};
struct MockExpectMe {
MOCK_METHOD0(InvokeMe, void());
template <class... Args>
void operator()(Args...) {
InvokeMe();
}
};
template <nsresult Expected>
@ -163,7 +279,10 @@ struct NSErrorMatcher {
};
struct FailOnCall {
void operator()() { FAIL(); }
template <class... Args>
void operator()(Args...) {
FAIL();
}
};
} // namespace mozilla::dom::fs::test
@ -175,4 +294,7 @@ MOCK_PROMISE_LISTENER(
ExpectNotImplemented, mozilla::dom::fs::test::FailOnCall,
mozilla::dom::fs::test::NSErrorMatcher<NS_ERROR_NOT_IMPLEMENTED>);
MOCK_PROMISE_LISTENER(ExpectResolveCalled, mozilla::dom::fs::test::MockExpectMe,
mozilla::dom::fs::test::FailOnCall);
#endif // DOM_FS_TEST_GTEST_FILESYSTEMMOCKS_H_

View File

@ -8,6 +8,7 @@
#include "FileSystemMocks.h"
#include "mozilla/dom/FileSystemActorHolder.h"
#include "mozilla/dom/FileSystemDirectoryHandle.h"
#include "mozilla/dom/FileSystemDirectoryHandleBinding.h"
#include "mozilla/dom/FileSystemDirectoryIterator.h"
@ -26,7 +27,8 @@ class TestFileSystemDirectoryHandle : public ::testing::Test {
mRequestHandler = MakeUnique<MockFileSystemRequestHandler>();
mMetadata = FileSystemEntryMetadata("dir"_ns, u"Directory"_ns);
mName = u"testDir"_ns;
mActor = MakeAndAddRef<FileSystemActorHolder>(nullptr);
mActor = MakeAndAddRef<FileSystemActorHolder>(
MakeUnique<FileSystemChildFactory>()->Create().take());
}
nsIGlobalObject* mGlobal = GetGlobal();

View File

@ -8,6 +8,9 @@
#include "FileSystemMocks.h"
#include "fs/FileSystemChildFactory.h"
#include "mozilla/dom/FileSystemActorHolder.h"
#include "mozilla/dom/FileSystemFileHandle.h"
#include "mozilla/dom/FileSystemFileHandleBinding.h"
#include "mozilla/dom/FileSystemHandle.h"
@ -24,7 +27,8 @@ class TestFileSystemFileHandle : public ::testing::Test {
void SetUp() override {
mRequestHandler = MakeUnique<MockFileSystemRequestHandler>();
mMetadata = FileSystemEntryMetadata("file"_ns, u"File"_ns);
mActor = MakeAndAddRef<FileSystemActorHolder>(nullptr);
mActor = MakeAndAddRef<FileSystemActorHolder>(
MakeUnique<FileSystemChildFactory>()->Create().take());
}
nsIGlobalObject* mGlobal = GetGlobal();

View File

@ -8,6 +8,10 @@
#include "FileSystemMocks.h"
#include "fs/FileSystemChildFactory.h"
#include "mozilla/dom/FileSystemActorHolder.h"
#include "mozilla/dom/FileSystemHandle.h"
#include "mozilla/dom/FileSystemDirectoryHandle.h"
#include "mozilla/dom/FileSystemFileHandle.h"
#include "mozilla/dom/FileSystemHandle.h"
@ -21,7 +25,8 @@ class TestFileSystemHandle : public ::testing::Test {
void SetUp() override {
mDirMetadata = FileSystemEntryMetadata("dir"_ns, u"Directory"_ns);
mFileMetadata = FileSystemEntryMetadata("file"_ns, u"File"_ns);
mActor = MakeAndAddRef<FileSystemActorHolder>(nullptr);
mActor = MakeAndAddRef<FileSystemActorHolder>(
MakeUnique<FileSystemChildFactory>()->Create().take());
}
nsIGlobalObject* mGlobal = GetGlobal();

View File

@ -8,23 +8,32 @@
#include "FileSystemMocks.h"
#include "fs/FileSystemRequestHandler.h"
#include "mozilla/dom/IPCBlob.h"
#include "mozilla/dom/OriginPrivateFileSystemChild.h"
#include "mozilla/dom/POriginPrivateFileSystem.h"
#include "mozilla/ipc/FileDescriptorUtils.h"
#include "mozilla/ipc/IPCCore.h"
#include "mozilla/SpinEventLoopUntil.h"
#include "mozilla/UniquePtr.h"
using ::testing::_;
using ::testing::ByRef;
using ::testing::Invoke;
using ::testing::Return;
namespace mozilla::dom::fs::test {
class TestFileSystemRequestHandler : public ::testing::Test {
protected:
void SetUp() override {
mListener = MakeAndAddRef<ExpectNotImplemented>();
mRequestHandler = MakeUnique<FileSystemRequestHandler>();
mListener = MakeAndAddRef<ExpectResolveCalled>();
mChild = FileSystemChildMetadata("parent"_ns, u"ChildName"_ns);
mEntry = FileSystemEntryMetadata("myid"_ns, u"EntryName"_ns);
mName = u"testDir"_ns;
mActor = MakeAndAddRef<FileSystemActorHolder>(nullptr);
mOPFSChild = MakeAndAddRef<TestOriginPrivateFileSystemChild>();
mActor = MakeAndAddRef<FileSystemActorHolder>(mOPFSChild.get());
}
already_AddRefed<Promise> GetDefaultPromise() {
@ -36,57 +45,169 @@ class TestFileSystemRequestHandler : public ::testing::Test {
return result.forget();
}
UniquePtr<FileSystemRequestHandler> GetFileSystemRequestHandler() {
return MakeUnique<FileSystemRequestHandler>(
new TestFileSystemChildFactory(mOPFSChild));
}
nsIGlobalObject* mGlobal = GetGlobal();
RefPtr<ExpectNotImplemented> mListener;
UniquePtr<FileSystemRequestHandler> mRequestHandler;
RefPtr<ExpectResolveCalled> mListener;
FileSystemChildMetadata mChild;
FileSystemEntryMetadata mEntry;
nsString mName;
RefPtr<TestOriginPrivateFileSystemChild> mOPFSChild;
RefPtr<FileSystemActorHolder> mActor;
};
class TestOPFSChild : public POriginPrivateFileSystemChild {
public:
NS_INLINE_DECL_REFCOUNTING(TestOPFSChild)
MOCK_METHOD(void, Close, ());
protected:
~TestOPFSChild() {
mozilla::ipc::MessageChannel* channel = GetIPCChannel();
channel->Close();
}
};
TEST_F(TestFileSystemRequestHandler, isGetRootSuccessful) {
EXPECT_CALL(*mOPFSChild, AsBindable())
.WillOnce(Invoke([]() -> POriginPrivateFileSystemChild* {
return new TestOPFSChild();
}));
// 1) In the parent process, at the end of get root
// 2) In the content process, when the handle is destroyed
EXPECT_CALL(*mOPFSChild, IsCloseable()).WillRepeatedly(Return(true));
EXPECT_CALL(*mOPFSChild, Close()).Times(2);
RefPtr<Promise> promise = GetDefaultPromise();
mRequestHandler->GetRoot(promise);
auto testable = GetFileSystemRequestHandler();
testable->GetRoot(promise);
// Promise should be rejected
SpinEventLoopUntil("Promise is fulfilled or timeout"_ns,
[this]() { return mListener->IsDone(); });
mOPFSChild->ManualRelease(); // Otherwise leak
}
TEST_F(TestFileSystemRequestHandler, isGetDirectoryHandleSuccessful) {
auto fakeResponse = [](const auto& /* aRequest */, auto&& aResolve,
auto&& /* aReject */) {
EntryId expected = "expected"_ns;
FileSystemGetHandleResponse response(expected);
aResolve(std::move(response));
};
EXPECT_CALL(mListener->GetSuccessHandler(), InvokeMe());
EXPECT_CALL(*mOPFSChild, SendGetDirectoryHandle(_, _, _))
.WillOnce(Invoke(fakeResponse));
EXPECT_CALL(*mOPFSChild, IsCloseable()).WillOnce(Return(true));
EXPECT_CALL(*mOPFSChild, Close()).Times(1);
RefPtr<Promise> promise = GetDefaultPromise();
mRequestHandler->GetDirectoryHandle(mActor, mChild,
/* create */ true, promise);
auto testable = GetFileSystemRequestHandler();
testable->GetDirectoryHandle(mActor, mChild,
/* create */ true, promise);
SpinEventLoopUntil("Promise is fulfilled or timeout"_ns,
[this]() { return mListener->IsDone(); });
}
TEST_F(TestFileSystemRequestHandler, isGetFileHandleSuccessful) {
auto fakeResponse = [](const auto& /* aRequest */, auto&& aResolve,
auto&& /* aReject */) {
EntryId expected = "expected"_ns;
FileSystemGetHandleResponse response(expected);
aResolve(std::move(response));
};
EXPECT_CALL(mListener->GetSuccessHandler(), InvokeMe());
EXPECT_CALL(*mOPFSChild, SendGetFileHandle(_, _, _))
.WillOnce(Invoke(fakeResponse));
EXPECT_CALL(*mOPFSChild, IsCloseable()).WillOnce(Return(true));
EXPECT_CALL(*mOPFSChild, Close()).Times(1);
RefPtr<Promise> promise = GetDefaultPromise();
mRequestHandler->GetFileHandle(mActor, mChild, /* create */ true, promise);
auto testable = GetFileSystemRequestHandler();
testable->GetFileHandle(mActor, mChild, /* create */ true, promise);
SpinEventLoopUntil("Promise is fulfilled or timeout"_ns,
[this]() { return mListener->IsDone(); });
}
TEST_F(TestFileSystemRequestHandler, isGetFileSuccessful) {
auto fakeResponse = [](const auto& /* aRequest */, auto&& aResolve,
auto&& /* aReject */) {
TimeStamp last_modified_ms = 0;
mozilla::dom::IPCBlob file;
ContentType type = u"txt"_ns;
nsTArray<Name> path;
path.AppendElement(u"root"_ns);
path.AppendElement(u"Trash"_ns);
FileSystemFileProperties properties(last_modified_ms, file, type, path);
FileSystemGetFileResponse response(properties);
aResolve(std::move(response));
};
EXPECT_CALL(mListener->GetSuccessHandler(), InvokeMe());
EXPECT_CALL(*mOPFSChild, SendGetFile(_, _, _)).WillOnce(Invoke(fakeResponse));
EXPECT_CALL(*mOPFSChild, IsCloseable()).WillOnce(Return(true));
EXPECT_CALL(*mOPFSChild, Close()).Times(1);
RefPtr<Promise> promise = GetDefaultPromise();
mRequestHandler->GetFile(mActor, mEntry, promise);
auto testable = GetFileSystemRequestHandler();
testable->GetFile(mActor, mEntry, promise);
SpinEventLoopUntil("Promise is fulfilled or timeout"_ns,
[this]() { return mListener->IsDone(); });
}
TEST_F(TestFileSystemRequestHandler, isGetEntriesSuccessful) {
RefPtr<Promise> promise = GetDefaultPromise();
auto fakeResponse = [](const auto& /* aRequest */, auto&& aResolve,
auto&& /* aReject */) {
nsTArray<FileSystemEntryMetadata> files;
nsTArray<FileSystemEntryMetadata> directories;
FileSystemDirectoryListing listing(files, directories);
FileSystemGetEntriesResponse response(listing);
aResolve(std::move(response));
};
RefPtr<ExpectNotImplemented> listener = MakeAndAddRef<ExpectNotImplemented>();
IgnoredErrorResult rv;
listener->ClearDone();
RefPtr<Promise> promise = Promise::Create(mGlobal, rv);
promise->AppendNativeHandler(listener);
EXPECT_CALL(*mOPFSChild, SendGetEntries(_, _, _))
.WillOnce(Invoke(fakeResponse));
EXPECT_CALL(*mOPFSChild, IsCloseable()).WillOnce(Return(true));
EXPECT_CALL(*mOPFSChild, Close()).Times(1);
auto testable = GetFileSystemRequestHandler();
ArrayAppendable sink;
mRequestHandler->GetEntries(mActor, mEntry.entryId(), /* page */ 0, promise,
sink);
testable->GetEntries(mActor, mEntry.entryId(), /* page */ 0, promise, sink);
SpinEventLoopUntil("Promise is fulfilled or timeout"_ns,
[this]() { return mListener->IsDone(); });
[listener]() { return listener->IsDone(); });
}
TEST_F(TestFileSystemRequestHandler, isRemoveEntrySuccessful) {
auto fakeResponse = [](const auto& /* aRequest */, auto&& aResolve,
auto&& /* aReject */) {
FileSystemRemoveEntryResponse response(mozilla::void_t{});
aResolve(std::move(response));
};
EXPECT_CALL(mListener->GetSuccessHandler(), InvokeMe());
EXPECT_CALL(*mOPFSChild, SendRemoveEntry(_, _, _))
.WillOnce(Invoke(fakeResponse));
EXPECT_CALL(*mOPFSChild, IsCloseable()).WillOnce(Return(true));
EXPECT_CALL(*mOPFSChild, Close()).Times(1);
auto testable = GetFileSystemRequestHandler();
RefPtr<Promise> promise = GetDefaultPromise();
mRequestHandler->RemoveEntry(mActor, mChild, /* recursive */ true, promise);
testable->RemoveEntry(mActor, mChild, /* recursive */ true, promise);
SpinEventLoopUntil("Promise is fulfilled or timeout"_ns,
[this]() { return mListener->IsDone(); });
}

View File

@ -4,8 +4,8 @@
* 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 MOZILLA_IPC_ACTORHOLDER_H
#define MOZILLA_IPC_ACTORHOLDER_H
#ifndef MOZILLA_IPC_TOPLEVELACTORHOLDER_H
#define MOZILLA_IPC_TOPLEVELACTORHOLDER_H
#include "nsISupports.h"
@ -21,17 +21,17 @@ namespace mozilla::ipc {
// You can avoid calling Close() on an un-connected Actor (for example if
// Bind() fails) by calling RemoveActor();
template <typename T>
class TopLevelActorHolder final {
class ToplevelActorHolder final {
public:
NS_INLINE_DECL_REFCOUNTING_ONEVENTTARGET(TopLevelActorHolder)
NS_INLINE_DECL_REFCOUNTING_ONEVENTTARGET(ToplevelActorHolder)
explicit TopLevelActorHolder(T* aActor) : mActor(aActor) {}
explicit ToplevelActorHolder(T* aActor) : mActor(aActor) {}
constexpr T* Actor() const { return mActor; }
inline void RemoveActor() { mActor = nullptr; }
private:
inline ~TopLevelActorHolder() {
inline ~ToplevelActorHolder() {
if (mActor) {
mActor->Close();
}
@ -42,4 +42,4 @@ class TopLevelActorHolder final {
} // namespace mozilla::ipc
#endif // MOZILLA_IPC_ACTORHOLDER_H
#endif // MOZILLA_IPC_TOPLEVELACTORHOLDER_H

View File

@ -10,7 +10,6 @@ EXPORTS += [
]
EXPORTS.mozilla.ipc += [
"ActorHolder.h",
"AsyncBlockers.h",
"BackgroundChild.h",
"BackgroundParent.h",
@ -60,6 +59,7 @@ EXPORTS.mozilla.ipc += [
"ShmemMessageUtils.h",
"TaintingIPCUtils.h",
"TaskFactory.h",
"ToplevelActorHolder.h",
"TransportSecurityInfoUtils.h",
"URIUtils.h",
"UtilityAudioDecoderChild.h",