blob: fea93662321ffc9719c37959ee799f9e85772e06 [file] [log] [blame]
// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/blink/renderer/modules/filesystem/file_system_dispatcher.h"
#include <utility>
#include "base/containers/contains.h"
#include "base/task/sequenced_task_runner.h"
#include "build/build_config.h"
#include "components/services/filesystem/public/mojom/types.mojom-blink.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "third_party/blink/public/platform/browser_interface_broker_proxy.h"
#include "third_party/blink/public/platform/file_path_conversion.h"
#include "third_party/blink/public/platform/platform.h"
#include "third_party/blink/public/platform/task_type.h"
#include "third_party/blink/renderer/core/execution_context/execution_context.h"
#include "third_party/blink/renderer/core/fileapi/blob.h"
#include "third_party/blink/renderer/platform/wtf/functional.h"
#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
namespace blink {
class FileSystemDispatcher::WriteListener
: public mojom::blink::FileSystemOperationListener {
public:
WriteListener(const WriteCallback& success_callback,
StatusCallback error_callback)
: error_callback_(std::move(error_callback)),
write_callback_(success_callback) {}
void ResultsRetrieved(
Vector<filesystem::mojom::blink::DirectoryEntryPtr> entries,
bool has_more) override {
NOTREACHED();
}
void ErrorOccurred(base::File::Error error_code) override {
std::move(error_callback_).Run(error_code);
}
void DidWrite(int64_t byte_count, bool complete) override {
write_callback_.Run(byte_count, complete);
}
private:
StatusCallback error_callback_;
WriteCallback write_callback_;
};
class FileSystemDispatcher::ReadDirectoryListener
: public mojom::blink::FileSystemOperationListener {
public:
explicit ReadDirectoryListener(std::unique_ptr<EntriesCallbacks> callbacks)
: callbacks_(std::move(callbacks)) {}
void ResultsRetrieved(
Vector<filesystem::mojom::blink::DirectoryEntryPtr> entries,
bool has_more) override {
for (const auto& entry : entries) {
callbacks_->DidReadDirectoryEntry(
FilePathToWebString(entry->name.path()),
entry->type == filesystem::mojom::blink::FsFileType::DIRECTORY);
}
callbacks_->DidReadDirectoryEntries(has_more);
}
void ErrorOccurred(base::File::Error error_code) override {
callbacks_->DidFail(error_code);
}
void DidWrite(int64_t byte_count, bool complete) override { NOTREACHED(); }
private:
std::unique_ptr<EntriesCallbacks> callbacks_;
};
FileSystemDispatcher::FileSystemDispatcher(ExecutionContext& context)
: Supplement<ExecutionContext>(context),
file_system_manager_(&context),
next_operation_id_(1),
op_listeners_(&context) {}
// static
const char FileSystemDispatcher::kSupplementName[] = "FileSystemDispatcher";
// static
FileSystemDispatcher& FileSystemDispatcher::From(ExecutionContext* context) {
DCHECK(context);
FileSystemDispatcher* dispatcher =
Supplement<ExecutionContext>::From<FileSystemDispatcher>(context);
if (!dispatcher) {
dispatcher = MakeGarbageCollected<FileSystemDispatcher>(*context);
Supplement<ExecutionContext>::ProvideTo(*context, dispatcher);
}
return *dispatcher;
}
FileSystemDispatcher::~FileSystemDispatcher() = default;
mojom::blink::FileSystemManager& FileSystemDispatcher::GetFileSystemManager() {
if (!file_system_manager_.is_bound()) {
// See https://bit.ly/2S0zRAS for task types
mojo::PendingReceiver<mojom::blink::FileSystemManager> receiver =
file_system_manager_.BindNewPipeAndPassReceiver(
GetSupplementable()->GetTaskRunner(
blink::TaskType::kMiscPlatformAPI));
GetSupplementable()->GetBrowserInterfaceBroker().GetInterface(
std::move(receiver));
}
DCHECK(file_system_manager_.is_bound());
return *file_system_manager_.get();
}
void FileSystemDispatcher::OpenFileSystem(
const SecurityOrigin* origin,
mojom::blink::FileSystemType type,
std::unique_ptr<FileSystemCallbacks> callbacks) {
GetFileSystemManager().Open(
origin, type,
WTF::BindOnce(&FileSystemDispatcher::DidOpenFileSystem,
WrapWeakPersistent(this), std::move(callbacks)));
}
void FileSystemDispatcher::OpenFileSystemSync(
const SecurityOrigin* origin,
mojom::blink::FileSystemType type,
std::unique_ptr<FileSystemCallbacks> callbacks) {
String name;
KURL root_url;
base::File::Error error_code = base::File::FILE_ERROR_FAILED;
GetFileSystemManager().Open(origin, type, &name, &root_url, &error_code);
DidOpenFileSystem(std::move(callbacks), std::move(name), root_url,
error_code);
}
void FileSystemDispatcher::ResolveURL(
const KURL& filesystem_url,
std::unique_ptr<ResolveURICallbacks> callbacks) {
GetFileSystemManager().ResolveURL(
filesystem_url,
WTF::BindOnce(&FileSystemDispatcher::DidResolveURL,
WrapWeakPersistent(this), std::move(callbacks)));
}
void FileSystemDispatcher::ResolveURLSync(
const KURL& filesystem_url,
std::unique_ptr<ResolveURICallbacks> callbacks) {
mojom::blink::FileSystemInfoPtr info;
base::FilePath file_path;
bool is_directory;
base::File::Error error_code = base::File::FILE_ERROR_FAILED;
GetFileSystemManager().ResolveURL(filesystem_url, &info, &file_path,
&is_directory, &error_code);
DidResolveURL(std::move(callbacks), std::move(info), std::move(file_path),
is_directory, error_code);
}
void FileSystemDispatcher::Move(const KURL& src_path,
const KURL& dest_path,
std::unique_ptr<EntryCallbacks> callbacks) {
GetFileSystemManager().Move(
src_path, dest_path,
WTF::BindOnce(&FileSystemDispatcher::DidFinish, WrapWeakPersistent(this),
std::move(callbacks)));
}
void FileSystemDispatcher::MoveSync(const KURL& src_path,
const KURL& dest_path,
std::unique_ptr<EntryCallbacks> callbacks) {
base::File::Error error_code = base::File::FILE_ERROR_FAILED;
GetFileSystemManager().Move(src_path, dest_path, &error_code);
DidFinish(std::move(callbacks), error_code);
}
void FileSystemDispatcher::Copy(const KURL& src_path,
const KURL& dest_path,
std::unique_ptr<EntryCallbacks> callbacks) {
GetFileSystemManager().Copy(
src_path, dest_path,
WTF::BindOnce(&FileSystemDispatcher::DidFinish, WrapWeakPersistent(this),
std::move(callbacks)));
}
void FileSystemDispatcher::CopySync(const KURL& src_path,
const KURL& dest_path,
std::unique_ptr<EntryCallbacks> callbacks) {
base::File::Error error_code = base::File::FILE_ERROR_FAILED;
GetFileSystemManager().Copy(src_path, dest_path, &error_code);
DidFinish(std::move(callbacks), error_code);
}
void FileSystemDispatcher::Remove(const KURL& path,
bool recursive,
std::unique_ptr<VoidCallbacks> callbacks) {
GetFileSystemManager().Remove(
path, recursive,
WTF::BindOnce(&FileSystemDispatcher::DidRemove, WrapWeakPersistent(this),
std::move(callbacks)));
}
void FileSystemDispatcher::RemoveSync(
const KURL& path,
bool recursive,
std::unique_ptr<VoidCallbacks> callbacks) {
base::File::Error error_code = base::File::FILE_ERROR_FAILED;
GetFileSystemManager().Remove(path, recursive, &error_code);
DidRemove(std::move(callbacks), error_code);
}
void FileSystemDispatcher::ReadMetadata(
const KURL& path,
std::unique_ptr<MetadataCallbacks> callbacks) {
GetFileSystemManager().ReadMetadata(
path, WTF::BindOnce(&FileSystemDispatcher::DidReadMetadata,
WrapWeakPersistent(this), std::move(callbacks)));
}
void FileSystemDispatcher::ReadMetadataSync(
const KURL& path,
std::unique_ptr<MetadataCallbacks> callbacks) {
base::File::Info file_info;
base::File::Error error_code = base::File::FILE_ERROR_FAILED;
GetFileSystemManager().ReadMetadata(path, &file_info, &error_code);
DidReadMetadata(std::move(callbacks), std::move(file_info), error_code);
}
void FileSystemDispatcher::CreateFile(
const KURL& path,
bool exclusive,
std::unique_ptr<EntryCallbacks> callbacks) {
GetFileSystemManager().Create(
path, exclusive, /*is_directory=*/false, /*is_recursive=*/false,
WTF::BindOnce(&FileSystemDispatcher::DidFinish, WrapWeakPersistent(this),
std::move(callbacks)));
}
void FileSystemDispatcher::CreateFileSync(
const KURL& path,
bool exclusive,
std::unique_ptr<EntryCallbacks> callbacks) {
base::File::Error error_code = base::File::FILE_ERROR_FAILED;
GetFileSystemManager().Create(path, exclusive, /*is_directory=*/false,
/*is_recursive=*/false, &error_code);
DidFinish(std::move(callbacks), error_code);
}
void FileSystemDispatcher::CreateDirectory(
const KURL& path,
bool exclusive,
bool recursive,
std::unique_ptr<EntryCallbacks> callbacks) {
GetFileSystemManager().Create(
path, exclusive, /*is_directory=*/true, recursive,
WTF::BindOnce(&FileSystemDispatcher::DidFinish, WrapWeakPersistent(this),
std::move(callbacks)));
}
void FileSystemDispatcher::CreateDirectorySync(
const KURL& path,
bool exclusive,
bool recursive,
std::unique_ptr<EntryCallbacks> callbacks) {
base::File::Error error_code = base::File::FILE_ERROR_FAILED;
GetFileSystemManager().Create(path, exclusive, /*is_directory=*/true,
recursive, &error_code);
DidFinish(std::move(callbacks), error_code);
}
void FileSystemDispatcher::Exists(const KURL& path,
bool is_directory,
std::unique_ptr<EntryCallbacks> callbacks) {
GetFileSystemManager().Exists(
path, is_directory,
WTF::BindOnce(&FileSystemDispatcher::DidFinish, WrapWeakPersistent(this),
std::move(callbacks)));
}
void FileSystemDispatcher::ExistsSync(
const KURL& path,
bool is_directory,
std::unique_ptr<EntryCallbacks> callbacks) {
base::File::Error error_code = base::File::FILE_ERROR_FAILED;
GetFileSystemManager().Exists(path, is_directory, &error_code);
DidFinish(std::move(callbacks), error_code);
}
void FileSystemDispatcher::ReadDirectory(
const KURL& path,
std::unique_ptr<EntriesCallbacks> callbacks) {
mojo::PendingRemote<mojom::blink::FileSystemOperationListener> listener;
mojo::PendingReceiver<mojom::blink::FileSystemOperationListener> receiver =
listener.InitWithNewPipeAndPassReceiver();
op_listeners_.Add(
std::make_unique<ReadDirectoryListener>(std::move(callbacks)),
std::move(receiver),
// See https://bit.ly/2S0zRAS for task types
GetSupplementable()->GetTaskRunner(blink::TaskType::kMiscPlatformAPI));
GetFileSystemManager().ReadDirectory(path, std::move(listener));
}
void FileSystemDispatcher::ReadDirectorySync(
const KURL& path,
std::unique_ptr<EntriesCallbacks> callbacks) {
Vector<filesystem::mojom::blink::DirectoryEntryPtr> entries;
base::File::Error result = base::File::FILE_ERROR_FAILED;
GetFileSystemManager().ReadDirectorySync(path, &entries, &result);
if (result == base::File::FILE_OK) {
DidReadDirectory(std::move(callbacks), std::move(entries),
std::move(result));
}
}
void FileSystemDispatcher::InitializeFileWriter(
const KURL& path,
std::unique_ptr<FileWriterCallbacks> callbacks) {
GetFileSystemManager().ReadMetadata(
path,
WTF::BindOnce(&FileSystemDispatcher::InitializeFileWriterCallback,
WrapWeakPersistent(this), path, std::move(callbacks)));
}
void FileSystemDispatcher::InitializeFileWriterSync(
const KURL& path,
std::unique_ptr<FileWriterCallbacks> callbacks) {
base::File::Info file_info;
base::File::Error error_code = base::File::FILE_ERROR_FAILED;
GetFileSystemManager().ReadMetadata(path, &file_info, &error_code);
InitializeFileWriterCallback(path, std::move(callbacks), file_info,
error_code);
}
void FileSystemDispatcher::Truncate(const KURL& path,
int64_t offset,
int* request_id_out,
StatusCallback callback) {
HeapMojoRemote<mojom::blink::FileSystemCancellableOperation> op_remote(
GetSupplementable());
// See https://bit.ly/2S0zRAS for task types
mojo::PendingReceiver<mojom::blink::FileSystemCancellableOperation>
op_receiver = op_remote.BindNewPipeAndPassReceiver(
GetSupplementable()->GetTaskRunner(
blink::TaskType::kMiscPlatformAPI));
int operation_id = next_operation_id_++;
op_remote.set_disconnect_handler(
WTF::BindOnce(&FileSystemDispatcher::RemoveOperationRemote,
WrapWeakPersistent(this), operation_id));
cancellable_operations_.insert(operation_id,
WrapDisallowNew(std::move(op_remote)));
GetFileSystemManager().Truncate(
path, offset, std::move(op_receiver),
WTF::BindOnce(&FileSystemDispatcher::DidTruncate,
WrapWeakPersistent(this), operation_id,
std::move(callback)));
if (request_id_out)
*request_id_out = operation_id;
}
void FileSystemDispatcher::TruncateSync(const KURL& path,
int64_t offset,
StatusCallback callback) {
base::File::Error error_code = base::File::FILE_ERROR_FAILED;
GetFileSystemManager().TruncateSync(path, offset, &error_code);
std::move(callback).Run(error_code);
}
void FileSystemDispatcher::Write(const KURL& path,
const Blob& blob,
int64_t offset,
int* request_id_out,
const WriteCallback& success_callback,
StatusCallback error_callback) {
HeapMojoRemote<mojom::blink::FileSystemCancellableOperation> op_remote(
GetSupplementable());
// See https://bit.ly/2S0zRAS for task types
scoped_refptr<base::SequencedTaskRunner> task_runner =
GetSupplementable()->GetTaskRunner(blink::TaskType::kMiscPlatformAPI);
mojo::PendingReceiver<mojom::blink::FileSystemCancellableOperation>
op_receiver = op_remote.BindNewPipeAndPassReceiver(task_runner);
int operation_id = next_operation_id_++;
op_remote.set_disconnect_handler(
WTF::BindOnce(&FileSystemDispatcher::RemoveOperationRemote,
WrapWeakPersistent(this), operation_id));
cancellable_operations_.insert(operation_id,
WrapDisallowNew(std::move(op_remote)));
mojo::PendingRemote<mojom::blink::FileSystemOperationListener> listener;
mojo::PendingReceiver<mojom::blink::FileSystemOperationListener> receiver =
listener.InitWithNewPipeAndPassReceiver();
op_listeners_.Add(std::make_unique<WriteListener>(
WTF::BindRepeating(&FileSystemDispatcher::DidWrite,
WrapWeakPersistent(this),
success_callback, operation_id),
WTF::BindOnce(&FileSystemDispatcher::WriteErrorCallback,
WrapWeakPersistent(this),
std::move(error_callback), operation_id)),
std::move(receiver), task_runner);
GetFileSystemManager().Write(path, blob.AsMojoBlob(), offset,
std::move(op_receiver), std::move(listener));
if (request_id_out)
*request_id_out = operation_id;
}
void FileSystemDispatcher::WriteSync(const KURL& path,
const Blob& blob,
int64_t offset,
const WriteCallback& success_callback,
StatusCallback error_callback) {
int64_t byte_count;
base::File::Error error_code = base::File::FILE_ERROR_FAILED;
GetFileSystemManager().WriteSync(path, blob.AsMojoBlob(), offset, &byte_count,
&error_code);
if (error_code == base::File::FILE_OK)
std::move(success_callback).Run(byte_count, /*complete=*/true);
else
std::move(error_callback).Run(error_code);
}
void FileSystemDispatcher::Cancel(int request_id_to_cancel,
StatusCallback callback) {
if (!base::Contains(cancellable_operations_, request_id_to_cancel)) {
std::move(callback).Run(base::File::FILE_ERROR_INVALID_OPERATION);
return;
}
auto& remote =
cancellable_operations_.find(request_id_to_cancel)->value->Value();
if (!remote.is_bound()) {
RemoveOperationRemote(request_id_to_cancel);
std::move(callback).Run(base::File::FILE_ERROR_INVALID_OPERATION);
return;
}
remote->Cancel(WTF::BindOnce(&FileSystemDispatcher::DidCancel,
WrapWeakPersistent(this), std::move(callback),
request_id_to_cancel));
}
void FileSystemDispatcher::CreateSnapshotFile(
const KURL& file_path,
std::unique_ptr<SnapshotFileCallbackBase> callbacks) {
GetFileSystemManager().CreateSnapshotFile(
file_path, WTF::BindOnce(&FileSystemDispatcher::DidCreateSnapshotFile,
WrapWeakPersistent(this), std::move(callbacks)));
}
void FileSystemDispatcher::CreateSnapshotFileSync(
const KURL& file_path,
std::unique_ptr<SnapshotFileCallbackBase> callbacks) {
base::File::Info file_info;
base::FilePath platform_path;
base::File::Error error_code = base::File::FILE_ERROR_FAILED;
mojo::PendingRemote<mojom::blink::ReceivedSnapshotListener> listener;
GetFileSystemManager().CreateSnapshotFile(
file_path, &file_info, &platform_path, &error_code, &listener);
DidCreateSnapshotFile(std::move(callbacks), std::move(file_info),
std::move(platform_path), error_code,
std::move(listener));
}
void FileSystemDispatcher::Trace(Visitor* visitor) const {
visitor->Trace(file_system_manager_);
visitor->Trace(cancellable_operations_);
visitor->Trace(op_listeners_);
Supplement<ExecutionContext>::Trace(visitor);
}
void FileSystemDispatcher::DidOpenFileSystem(
std::unique_ptr<FileSystemCallbacks> callbacks,
const String& name,
const KURL& root,
base::File::Error error_code) {
if (error_code == base::File::Error::FILE_OK) {
callbacks->DidOpenFileSystem(name, root);
} else {
callbacks->DidFail(error_code);
}
}
void FileSystemDispatcher::DidResolveURL(
std::unique_ptr<ResolveURICallbacks> callbacks,
mojom::blink::FileSystemInfoPtr info,
const base::FilePath& file_path,
bool is_directory,
base::File::Error error_code) {
if (error_code == base::File::Error::FILE_OK) {
DCHECK(info->root_url.IsValid());
callbacks->DidResolveURL(info->name, info->root_url, info->mount_type,
FilePathToWebString(file_path), is_directory);
} else {
callbacks->DidFail(error_code);
}
}
void FileSystemDispatcher::DidRemove(std::unique_ptr<VoidCallbacks> callbacks,
base::File::Error error_code) {
if (error_code == base::File::Error::FILE_OK)
callbacks->DidSucceed();
else
callbacks->DidFail(error_code);
}
void FileSystemDispatcher::DidFinish(std::unique_ptr<EntryCallbacks> callbacks,
base::File::Error error_code) {
if (error_code == base::File::Error::FILE_OK)
callbacks->DidSucceed();
else
callbacks->DidFail(error_code);
}
void FileSystemDispatcher::DidReadMetadata(
std::unique_ptr<MetadataCallbacks> callbacks,
const base::File::Info& file_info,
base::File::Error error_code) {
if (error_code == base::File::Error::FILE_OK) {
callbacks->DidReadMetadata(FileMetadata::From(file_info));
} else {
callbacks->DidFail(error_code);
}
}
void FileSystemDispatcher::DidReadDirectory(
std::unique_ptr<EntriesCallbacks> callbacks,
Vector<filesystem::mojom::blink::DirectoryEntryPtr> entries,
base::File::Error error_code) {
if (error_code == base::File::Error::FILE_OK) {
for (const auto& entry : entries) {
callbacks->DidReadDirectoryEntry(
FilePathToWebString(entry->name.path()),
entry->type == filesystem::mojom::blink::FsFileType::DIRECTORY);
}
callbacks->DidReadDirectoryEntries(false);
} else {
callbacks->DidFail(error_code);
}
}
void FileSystemDispatcher::InitializeFileWriterCallback(
const KURL& path,
std::unique_ptr<FileWriterCallbacks> callbacks,
const base::File::Info& file_info,
base::File::Error error_code) {
if (error_code == base::File::Error::FILE_OK) {
if (file_info.is_directory || file_info.size < 0) {
callbacks->DidFail(base::File::FILE_ERROR_FAILED);
return;
}
callbacks->DidCreateFileWriter(path, file_info.size);
} else {
callbacks->DidFail(error_code);
}
}
void FileSystemDispatcher::DidTruncate(int operation_id,
StatusCallback callback,
base::File::Error error_code) {
if (error_code != base::File::FILE_ERROR_ABORT)
RemoveOperationRemote(operation_id);
std::move(callback).Run(error_code);
}
void FileSystemDispatcher::DidWrite(const WriteCallback& callback,
int operation_id,
int64_t bytes,
bool complete) {
callback.Run(bytes, complete);
if (complete)
RemoveOperationRemote(operation_id);
}
void FileSystemDispatcher::WriteErrorCallback(StatusCallback callback,
int operation_id,
base::File::Error error) {
std::move(callback).Run(error);
if (error != base::File::FILE_ERROR_ABORT)
RemoveOperationRemote(operation_id);
}
void FileSystemDispatcher::DidCancel(StatusCallback callback,
int cancelled_operation_id,
base::File::Error error_code) {
if (error_code == base::File::FILE_OK)
RemoveOperationRemote(cancelled_operation_id);
std::move(callback).Run(error_code);
}
void FileSystemDispatcher::DidCreateSnapshotFile(
std::unique_ptr<SnapshotFileCallbackBase> callbacks,
const base::File::Info& file_info,
const base::FilePath& platform_path,
base::File::Error error_code,
mojo::PendingRemote<mojom::blink::ReceivedSnapshotListener> listener) {
if (error_code == base::File::FILE_OK) {
FileMetadata file_metadata = FileMetadata::From(file_info);
file_metadata.platform_path = FilePathToWebString(platform_path);
callbacks->DidCreateSnapshotFile(file_metadata);
if (listener) {
mojo::Remote<mojom::blink::ReceivedSnapshotListener>(std::move(listener))
->DidReceiveSnapshotFile();
}
} else {
callbacks->DidFail(error_code);
}
}
void FileSystemDispatcher::RemoveOperationRemote(int operation_id) {
auto it = cancellable_operations_.find(operation_id);
if (it == cancellable_operations_.end())
return;
cancellable_operations_.erase(it);
}
} // namespace blink