blob: 80d27f085008aa1a8163d7adf85734e5ace536fd [file] [log] [blame]
// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/chromeos/extensions/file_system_provider/file_system_provider_api.h"
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "base/memory/ptr_util.h"
#include "base/trace_event/trace_event.h"
#include "base/values.h"
#include "chrome/browser/chromeos/file_system_provider/provided_file_system_info.h"
#include "chrome/browser/chromeos/file_system_provider/provided_file_system_interface.h"
#include "chrome/browser/chromeos/file_system_provider/request_manager.h"
#include "chrome/browser/chromeos/file_system_provider/request_value.h"
#include "chrome/browser/chromeos/file_system_provider/service.h"
#include "chrome/common/extensions/api/file_system_provider.h"
#include "chrome/common/extensions/api/file_system_provider_internal.h"
#include "storage/browser/fileapi/watcher_manager.h"
using chromeos::file_system_provider::MountOptions;
using chromeos::file_system_provider::OpenedFiles;
using chromeos::file_system_provider::ProvidedFileSystemInfo;
using chromeos::file_system_provider::ProvidedFileSystemInterface;
using chromeos::file_system_provider::ProvidedFileSystemObserver;
using chromeos::file_system_provider::RequestValue;
using chromeos::file_system_provider::Service;
using chromeos::file_system_provider::Watchers;
namespace extensions {
namespace {
// Converts the change type from the IDL type to a native type. |changed_type|
// must be specified (not CHANGE_TYPE_NONE).
storage::WatcherManager::ChangeType ParseChangeType(
const api::file_system_provider::ChangeType& change_type) {
switch (change_type) {
case api::file_system_provider::CHANGE_TYPE_CHANGED:
return storage::WatcherManager::CHANGED;
case api::file_system_provider::CHANGE_TYPE_DELETED:
return storage::WatcherManager::DELETED;
default:
break;
}
NOTREACHED();
return storage::WatcherManager::CHANGED;
}
// Convert the change from the IDL type to a native type. The reason IDL types
// are not used is since they are imperfect, eg. paths are stored as strings.
ProvidedFileSystemObserver::Change ParseChange(
const api::file_system_provider::Change& change) {
ProvidedFileSystemObserver::Change result;
result.entry_path = base::FilePath::FromUTF8Unsafe(change.entry_path);
result.change_type = ParseChangeType(change.change_type);
return result;
}
// Converts a list of child changes from the IDL type to a native type.
std::unique_ptr<ProvidedFileSystemObserver::Changes> ParseChanges(
const std::vector<api::file_system_provider::Change>& changes) {
std::unique_ptr<ProvidedFileSystemObserver::Changes> results(
new ProvidedFileSystemObserver::Changes);
for (const auto& change : changes) {
results->push_back(ParseChange(change));
}
return results;
}
// Fills the IDL's FileSystemInfo with FSP's ProvidedFileSystemInfo and
// Watchers.
void FillFileSystemInfo(const ProvidedFileSystemInfo& file_system_info,
const Watchers& watchers,
const OpenedFiles& opened_files,
api::file_system_provider::FileSystemInfo* output) {
using api::file_system_provider::Watcher;
using api::file_system_provider::OpenedFile;
output->file_system_id = file_system_info.file_system_id();
output->display_name = file_system_info.display_name();
output->writable = file_system_info.writable();
output->opened_files_limit = file_system_info.opened_files_limit();
for (const auto& watcher : watchers) {
Watcher watcher_item;
watcher_item.entry_path = watcher.second.entry_path.value();
watcher_item.recursive = watcher.second.recursive;
if (!watcher.second.last_tag.empty())
watcher_item.last_tag.reset(new std::string(watcher.second.last_tag));
output->watchers.push_back(std::move(watcher_item));
}
for (const auto& opened_file : opened_files) {
OpenedFile opened_file_item;
opened_file_item.open_request_id = opened_file.first;
opened_file_item.file_path = opened_file.second.file_path.value();
switch (opened_file.second.mode) {
case chromeos::file_system_provider::OPEN_FILE_MODE_READ:
opened_file_item.mode =
extensions::api::file_system_provider::OPEN_FILE_MODE_READ;
break;
case chromeos::file_system_provider::OPEN_FILE_MODE_WRITE:
opened_file_item.mode =
extensions::api::file_system_provider::OPEN_FILE_MODE_WRITE;
break;
}
output->opened_files.push_back(std::move(opened_file_item));
}
}
} // namespace
ExtensionFunction::ResponseAction FileSystemProviderMountFunction::Run() {
using api::file_system_provider::Mount::Params;
const std::unique_ptr<Params> params(Params::Create(*args_));
EXTENSION_FUNCTION_VALIDATE(params);
// It's an error if the file system Id is empty.
if (params->options.file_system_id.empty()) {
return RespondNow(
Error(FileErrorToString(base::File::FILE_ERROR_INVALID_OPERATION)));
}
// It's an error if the display name is empty.
if (params->options.display_name.empty()) {
return RespondNow(
Error(FileErrorToString(base::File::FILE_ERROR_INVALID_OPERATION)));
}
// If the opened files limit is set, then it must be larger or equal than 0.
if (params->options.opened_files_limit.get() &&
*params->options.opened_files_limit.get() < 0) {
return RespondNow(
Error(FileErrorToString(base::File::FILE_ERROR_INVALID_OPERATION)));
}
Service* const service =
Service::Get(Profile::FromBrowserContext(browser_context()));
DCHECK(service);
MountOptions options;
options.file_system_id = params->options.file_system_id;
options.display_name = params->options.display_name;
options.writable = params->options.writable != nullptr;
options.opened_files_limit = params->options.opened_files_limit.get()
? *params->options.opened_files_limit.get()
: 0;
options.supports_notify_tag = params->options.supports_notify_tag != nullptr;
const base::File::Error result =
service->MountFileSystem(extension_id(), options);
if (result != base::File::FILE_OK)
return RespondNow(Error(FileErrorToString(result)));
return RespondNow(NoArguments());
}
ExtensionFunction::ResponseAction FileSystemProviderUnmountFunction::Run() {
using api::file_system_provider::Unmount::Params;
std::unique_ptr<Params> params(Params::Create(*args_));
EXTENSION_FUNCTION_VALIDATE(params);
Service* const service =
Service::Get(Profile::FromBrowserContext(browser_context()));
DCHECK(service);
const base::File::Error result =
service->UnmountFileSystem(extension_id(), params->options.file_system_id,
Service::UNMOUNT_REASON_USER);
if (result != base::File::FILE_OK)
return RespondNow(Error(FileErrorToString(result)));
return RespondNow(NoArguments());
}
ExtensionFunction::ResponseAction FileSystemProviderGetAllFunction::Run() {
using api::file_system_provider::FileSystemInfo;
Service* const service =
Service::Get(Profile::FromBrowserContext(browser_context()));
DCHECK(service);
const std::vector<ProvidedFileSystemInfo> file_systems =
service->GetProvidedFileSystemInfoList();
std::vector<FileSystemInfo> items;
for (const auto& file_system_info : file_systems) {
if (file_system_info.extension_id() == extension_id()) {
FileSystemInfo item;
chromeos::file_system_provider::ProvidedFileSystemInterface* const
file_system =
service->GetProvidedFileSystem(file_system_info.extension_id(),
file_system_info.file_system_id());
DCHECK(file_system);
FillFileSystemInfo(file_system_info, *file_system->GetWatchers(),
file_system->GetOpenedFiles(), &item);
items.push_back(std::move(item));
}
}
return RespondNow(
ArgumentList(api::file_system_provider::GetAll::Results::Create(items)));
}
ExtensionFunction::ResponseAction FileSystemProviderGetFunction::Run() {
using api::file_system_provider::Get::Params;
std::unique_ptr<Params> params(Params::Create(*args_));
EXTENSION_FUNCTION_VALIDATE(params);
using api::file_system_provider::FileSystemInfo;
Service* const service =
Service::Get(Profile::FromBrowserContext(browser_context()));
DCHECK(service);
chromeos::file_system_provider::ProvidedFileSystemInterface* const
file_system = service->GetProvidedFileSystem(extension_id(),
params->file_system_id);
if (!file_system) {
return RespondNow(
Error(FileErrorToString(base::File::FILE_ERROR_NOT_FOUND)));
}
FileSystemInfo file_system_info;
FillFileSystemInfo(file_system->GetFileSystemInfo(),
*file_system->GetWatchers(), file_system->GetOpenedFiles(),
&file_system_info);
return RespondNow(ArgumentList(
api::file_system_provider::Get::Results::Create(file_system_info)));
}
bool FileSystemProviderNotifyFunction::RunAsync() {
using api::file_system_provider::Notify::Params;
std::unique_ptr<Params> params(Params::Create(*args_));
EXTENSION_FUNCTION_VALIDATE(params);
Service* const service = Service::Get(GetProfile());
DCHECK(service);
ProvidedFileSystemInterface* const file_system =
service->GetProvidedFileSystem(extension_id(),
params->options.file_system_id);
if (!file_system) {
SetError(FileErrorToString(base::File::FILE_ERROR_NOT_FOUND));
return false;
}
file_system->Notify(
base::FilePath::FromUTF8Unsafe(params->options.observed_path),
params->options.recursive, ParseChangeType(params->options.change_type),
params->options.changes.get()
? ParseChanges(*params->options.changes.get())
: base::WrapUnique(new ProvidedFileSystemObserver::Changes),
params->options.tag.get() ? *params->options.tag.get() : "",
base::Bind(&FileSystemProviderNotifyFunction::OnNotifyCompleted, this));
return true;
}
void FileSystemProviderNotifyFunction::OnNotifyCompleted(
base::File::Error result) {
if (result != base::File::FILE_OK) {
SetError(FileErrorToString(result));
SendResponse(false);
return;
}
SendResponse(true);
}
ExtensionFunction::ResponseAction
FileSystemProviderInternalUnmountRequestedSuccessFunction::Run() {
using api::file_system_provider_internal::UnmountRequestedSuccess::Params;
std::unique_ptr<Params> params(Params::Create(*args_));
EXTENSION_FUNCTION_VALIDATE(params);
return FulfillRequest(
RequestValue::CreateForUnmountSuccess(std::move(params)),
false /* has_more */);
}
ExtensionFunction::ResponseAction
FileSystemProviderInternalGetMetadataRequestedSuccessFunction::Run() {
using api::file_system_provider_internal::GetMetadataRequestedSuccess::Params;
std::unique_ptr<Params> params(Params::Create(*args_));
EXTENSION_FUNCTION_VALIDATE(params);
return FulfillRequest(
RequestValue::CreateForGetMetadataSuccess(std::move(params)),
false /* has_more */);
}
ExtensionFunction::ResponseAction
FileSystemProviderInternalGetActionsRequestedSuccessFunction::Run() {
using api::file_system_provider_internal::GetActionsRequestedSuccess::Params;
std::unique_ptr<Params> params(Params::Create(*args_));
EXTENSION_FUNCTION_VALIDATE(params);
return FulfillRequest(
RequestValue::CreateForGetActionsSuccess(std::move(params)),
false /* has_more */);
}
ExtensionFunction::ResponseAction
FileSystemProviderInternalReadDirectoryRequestedSuccessFunction::Run() {
using api::file_system_provider_internal::ReadDirectoryRequestedSuccess::
Params;
std::unique_ptr<Params> params(Params::Create(*args_));
EXTENSION_FUNCTION_VALIDATE(params);
const bool has_more = params->has_more;
return FulfillRequest(
RequestValue::CreateForReadDirectorySuccess(std::move(params)), has_more);
}
ExtensionFunction::ResponseAction
FileSystemProviderInternalReadFileRequestedSuccessFunction::Run() {
TRACE_EVENT0("file_system_provider", "ReadFileRequestedSuccess");
using api::file_system_provider_internal::ReadFileRequestedSuccess::Params;
std::unique_ptr<Params> params(Params::Create(*args_));
EXTENSION_FUNCTION_VALIDATE(params);
const bool has_more = params->has_more;
return FulfillRequest(
RequestValue::CreateForReadFileSuccess(std::move(params)), has_more);
}
ExtensionFunction::ResponseAction
FileSystemProviderInternalOperationRequestedSuccessFunction::Run() {
using api::file_system_provider_internal::OperationRequestedSuccess::Params;
std::unique_ptr<Params> params(Params::Create(*args_));
EXTENSION_FUNCTION_VALIDATE(params);
return FulfillRequest(
std::unique_ptr<RequestValue>(
RequestValue::CreateForOperationSuccess(std::move(params))),
false /* has_more */);
}
ExtensionFunction::ResponseAction
FileSystemProviderInternalOperationRequestedErrorFunction::Run() {
using api::file_system_provider_internal::OperationRequestedError::Params;
std::unique_ptr<Params> params(Params::Create(*args_));
EXTENSION_FUNCTION_VALIDATE(params);
const base::File::Error error = ProviderErrorToFileError(params->error);
return RejectRequest(RequestValue::CreateForOperationError(std::move(params)),
error);
}
} // namespace extensions