blob: c89dad722b3a2f13a2a048938597b2adb3cfeeac [file] [log] [blame]
// Copyright (c) 2012 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/extensions/api/sync_file_system/sync_file_system_api.h"
#include <memory>
#include <string>
#include <utility>
#include "base/bind.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/strings/stringprintf.h"
#include "chrome/browser/extensions/api/sync_file_system/extension_sync_event_observer.h"
#include "chrome/browser/extensions/api/sync_file_system/sync_file_system_api_helpers.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/sync_file_system/sync_file_status.h"
#include "chrome/browser/sync_file_system/sync_file_system_service.h"
#include "chrome/browser/sync_file_system/sync_file_system_service_factory.h"
#include "chrome/common/extensions/api/sync_file_system.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/storage_partition.h"
#include "content/public/common/content_client.h"
#include "storage/browser/fileapi/file_system_context.h"
#include "storage/browser/fileapi/file_system_url.h"
#include "storage/browser/quota/quota_manager.h"
#include "storage/common/fileapi/file_system_types.h"
#include "storage/common/fileapi/file_system_util.h"
using content::BrowserContext;
using content::BrowserThread;
using sync_file_system::ConflictResolutionPolicy;
using sync_file_system::SyncFileStatus;
using sync_file_system::SyncFileSystemServiceFactory;
using sync_file_system::SyncStatusCode;
namespace extensions {
namespace {
// Error messages.
const char kErrorMessage[] = "%s (error code: %d).";
const char kUnsupportedConflictResolutionPolicy[] =
"Policy %s is not supported.";
sync_file_system::SyncFileSystemService* GetSyncFileSystemService(
Profile* profile) {
sync_file_system::SyncFileSystemService* service =
SyncFileSystemServiceFactory::GetForProfile(profile);
if (!service)
return nullptr;
ExtensionSyncEventObserver* observer =
ExtensionSyncEventObserver::GetFactoryInstance()->Get(profile);
if (!observer)
return nullptr;
observer->InitializeForService(service);
return service;
}
std::string ErrorToString(SyncStatusCode code) {
return base::StringPrintf(
kErrorMessage,
sync_file_system::SyncStatusCodeToString(code),
static_cast<int>(code));
}
} // namespace
bool SyncFileSystemDeleteFileSystemFunction::RunAsync() {
std::string url;
EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &url));
scoped_refptr<storage::FileSystemContext> file_system_context =
BrowserContext::GetStoragePartition(
GetProfile(), render_frame_host()->GetSiteInstance())
->GetFileSystemContext();
storage::FileSystemURL file_system_url(
file_system_context->CrackURL(GURL(url)));
BrowserThread::PostTask(
BrowserThread::IO,
FROM_HERE,
Bind(&storage::FileSystemContext::DeleteFileSystem,
file_system_context,
source_url().GetOrigin(),
file_system_url.type(),
Bind(&SyncFileSystemDeleteFileSystemFunction::DidDeleteFileSystem,
this)));
return true;
}
void SyncFileSystemDeleteFileSystemFunction::DidDeleteFileSystem(
base::File::Error error) {
// Repost to switch from IO thread to UI thread for SendResponse().
if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
BrowserThread::PostTask(
BrowserThread::UI,
FROM_HERE,
Bind(&SyncFileSystemDeleteFileSystemFunction::DidDeleteFileSystem, this,
error));
return;
}
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (error != base::File::FILE_OK) {
error_ = ErrorToString(sync_file_system::FileErrorToSyncStatusCode(error));
SetResult(base::MakeUnique<base::FundamentalValue>(false));
SendResponse(false);
return;
}
SetResult(base::MakeUnique<base::FundamentalValue>(true));
SendResponse(true);
}
bool SyncFileSystemRequestFileSystemFunction::RunAsync() {
// SyncFileSystem initialization is done in OpenFileSystem below, but we call
// GetSyncFileSystemService here too to initialize sync event observer for
// extensions API.
if (!GetSyncFileSystemService(GetProfile()))
return false;
// Initializes sync context for this extension and continue to open
// a new file system.
BrowserThread::PostTask(BrowserThread::IO,
FROM_HERE,
Bind(&storage::FileSystemContext::OpenFileSystem,
GetFileSystemContext(),
source_url().GetOrigin(),
storage::kFileSystemTypeSyncable,
storage::OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT,
base::Bind(&self::DidOpenFileSystem, this)));
return true;
}
storage::FileSystemContext*
SyncFileSystemRequestFileSystemFunction::GetFileSystemContext() {
DCHECK(render_frame_host());
return BrowserContext::GetStoragePartition(
GetProfile(), render_frame_host()->GetSiteInstance())
->GetFileSystemContext();
}
void SyncFileSystemRequestFileSystemFunction::DidOpenFileSystem(
const GURL& root_url,
const std::string& file_system_name,
base::File::Error error) {
// Repost to switch from IO thread to UI thread for SendResponse().
if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE,
Bind(&SyncFileSystemRequestFileSystemFunction::DidOpenFileSystem,
this, root_url, file_system_name, error));
return;
}
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (error != base::File::FILE_OK) {
error_ = ErrorToString(sync_file_system::FileErrorToSyncStatusCode(error));
SendResponse(false);
return;
}
std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
dict->SetString("name", file_system_name);
dict->SetString("root", root_url.spec());
SetResult(std::move(dict));
SendResponse(true);
}
bool SyncFileSystemGetFileStatusFunction::RunAsync() {
std::string url;
EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &url));
scoped_refptr<storage::FileSystemContext> file_system_context =
BrowserContext::GetStoragePartition(
GetProfile(), render_frame_host()->GetSiteInstance())
->GetFileSystemContext();
storage::FileSystemURL file_system_url(
file_system_context->CrackURL(GURL(url)));
sync_file_system::SyncFileSystemService* sync_file_system_service =
GetSyncFileSystemService(GetProfile());
if (!sync_file_system_service)
return false;
sync_file_system_service->GetFileSyncStatus(
file_system_url,
Bind(&SyncFileSystemGetFileStatusFunction::DidGetFileStatus, this));
return true;
}
void SyncFileSystemGetFileStatusFunction::DidGetFileStatus(
const SyncStatusCode sync_status_code,
const SyncFileStatus sync_file_status) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (sync_status_code != sync_file_system::SYNC_STATUS_OK) {
error_ = ErrorToString(sync_status_code);
SendResponse(false);
return;
}
// Convert from C++ to JavaScript enum.
results_ = api::sync_file_system::GetFileStatus::Results::Create(
SyncFileStatusToExtensionEnum(sync_file_status));
SendResponse(true);
}
SyncFileSystemGetFileStatusesFunction::SyncFileSystemGetFileStatusesFunction() {
}
SyncFileSystemGetFileStatusesFunction::~SyncFileSystemGetFileStatusesFunction(
) {}
bool SyncFileSystemGetFileStatusesFunction::RunAsync() {
// All FileEntries converted into array of URL Strings in JS custom bindings.
base::ListValue* file_entry_urls = NULL;
EXTENSION_FUNCTION_VALIDATE(args_->GetList(0, &file_entry_urls));
scoped_refptr<storage::FileSystemContext> file_system_context =
BrowserContext::GetStoragePartition(
GetProfile(), render_frame_host()->GetSiteInstance())
->GetFileSystemContext();
// Map each file path->SyncFileStatus in the callback map.
// TODO(calvinlo): Overload GetFileSyncStatus to take in URL array.
num_expected_results_ = file_entry_urls->GetSize();
num_results_received_ = 0;
file_sync_statuses_.clear();
sync_file_system::SyncFileSystemService* sync_file_system_service =
GetSyncFileSystemService(GetProfile());
if (!sync_file_system_service)
return false;
for (unsigned int i = 0; i < num_expected_results_; i++) {
std::string url;
file_entry_urls->GetString(i, &url);
storage::FileSystemURL file_system_url(
file_system_context->CrackURL(GURL(url)));
sync_file_system_service->GetFileSyncStatus(
file_system_url,
Bind(&SyncFileSystemGetFileStatusesFunction::DidGetFileStatus,
this, file_system_url));
}
return true;
}
void SyncFileSystemGetFileStatusesFunction::DidGetFileStatus(
const storage::FileSystemURL& file_system_url,
SyncStatusCode sync_status_code,
SyncFileStatus sync_file_status) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
num_results_received_++;
DCHECK_LE(num_results_received_, num_expected_results_);
file_sync_statuses_[file_system_url] =
std::make_pair(sync_status_code, sync_file_status);
// Keep mapping file statuses until all of them have been received.
// TODO(calvinlo): Get rid of this check when batch version of
// GetFileSyncStatus(GURL urls[]); is added.
if (num_results_received_ < num_expected_results_)
return;
// All results received. Dump array of statuses into extension enum values.
// Note that the enum types need to be set as strings manually as the
// autogenerated Results::Create function thinks the enum values should be
// returned as int values.
std::unique_ptr<base::ListValue> status_array(new base::ListValue());
for (URLToStatusMap::iterator it = file_sync_statuses_.begin();
it != file_sync_statuses_.end(); ++it) {
std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
storage::FileSystemURL url = it->first;
SyncStatusCode file_error = it->second.first;
api::sync_file_system::FileStatus file_status =
SyncFileStatusToExtensionEnum(it->second.second);
dict->Set("entry", CreateDictionaryValueForFileSystemEntry(
url, sync_file_system::SYNC_FILE_TYPE_FILE));
dict->SetString("status", ToString(file_status));
if (file_error == sync_file_system::SYNC_STATUS_OK)
continue;
dict->SetString("error", ErrorToString(file_error));
status_array->Append(std::move(dict));
}
SetResult(std::move(status_array));
SendResponse(true);
}
bool SyncFileSystemGetUsageAndQuotaFunction::RunAsync() {
std::string url;
EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &url));
scoped_refptr<storage::FileSystemContext> file_system_context =
BrowserContext::GetStoragePartition(
GetProfile(), render_frame_host()->GetSiteInstance())
->GetFileSystemContext();
storage::FileSystemURL file_system_url(
file_system_context->CrackURL(GURL(url)));
scoped_refptr<storage::QuotaManager> quota_manager =
BrowserContext::GetStoragePartition(
GetProfile(), render_frame_host()->GetSiteInstance())
->GetQuotaManager();
BrowserThread::PostTask(
BrowserThread::IO,
FROM_HERE,
Bind(&storage::QuotaManager::GetUsageAndQuotaForWebApps,
quota_manager,
source_url().GetOrigin(),
storage::FileSystemTypeToQuotaStorageType(file_system_url.type()),
Bind(&SyncFileSystemGetUsageAndQuotaFunction::DidGetUsageAndQuota,
this)));
return true;
}
void SyncFileSystemGetUsageAndQuotaFunction::DidGetUsageAndQuota(
storage::QuotaStatusCode status,
int64_t usage,
int64_t quota) {
// Repost to switch from IO thread to UI thread for SendResponse().
if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
BrowserThread::PostTask(
BrowserThread::UI,
FROM_HERE,
Bind(&SyncFileSystemGetUsageAndQuotaFunction::DidGetUsageAndQuota, this,
status, usage, quota));
return;
}
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (status != storage::kQuotaStatusOk) {
error_ = QuotaStatusCodeToString(status);
SendResponse(false);
return;
}
api::sync_file_system::StorageInfo info;
info.usage_bytes = usage;
info.quota_bytes = quota;
results_ = api::sync_file_system::GetUsageAndQuota::Results::Create(info);
SendResponse(true);
}
bool SyncFileSystemSetConflictResolutionPolicyFunction::RunSync() {
std::string policy_string;
EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &policy_string));
ConflictResolutionPolicy policy = ExtensionEnumToConflictResolutionPolicy(
api::sync_file_system::ParseConflictResolutionPolicy(policy_string));
if (policy != sync_file_system::CONFLICT_RESOLUTION_POLICY_LAST_WRITE_WIN) {
SetError(base::StringPrintf(kUnsupportedConflictResolutionPolicy,
policy_string.c_str()));
return false;
}
return true;
}
bool SyncFileSystemGetConflictResolutionPolicyFunction::RunSync() {
SetResult(base::MakeUnique<base::StringValue>(api::sync_file_system::ToString(
api::sync_file_system::CONFLICT_RESOLUTION_POLICY_LAST_WRITE_WIN)));
return true;
}
bool SyncFileSystemGetServiceStatusFunction::RunSync() {
sync_file_system::SyncFileSystemService* service =
GetSyncFileSystemService(GetProfile());
if (!service)
return false;
results_ = api::sync_file_system::GetServiceStatus::Results::Create(
SyncServiceStateToExtensionEnum(service->GetSyncServiceState()));
return true;
}
} // namespace extensions