This source file includes following definitions.
- TryToCopyLocally
- UpdateLocalStateForServerSideOperation
- UpdateLocalStateForScheduleTransfer
- PrepareTransferFileFromLocalToRemote
- LocalWorkForTransferJsonGdocFile
- weak_ptr_factory_
- CopyAfterTryToCopyLocally
- TransferFileFromLocalToRemote
- TransferFileFromLocalToRemoteAfterPrepare
- TransferJsonGdocFileAfterLocalWork
- CopyResourceOnServer
- UpdateAfterServerSideOperation
- UpdateAfterLocalStateUpdate
- ScheduleTransferRegularFile
- ScheduleTransferRegularFileAfterCreate
- ScheduleTransferRegularFileAfterUpdateLocalState
#include "chrome/browser/chromeos/drive/file_system/copy_operation.h"
#include <string>
#include "base/file_util.h"
#include "base/task_runner_util.h"
#include "chrome/browser/chromeos/drive/drive.pb.h"
#include "chrome/browser/chromeos/drive/file_cache.h"
#include "chrome/browser/chromeos/drive/file_system/create_file_operation.h"
#include "chrome/browser/chromeos/drive/file_system/operation_observer.h"
#include "chrome/browser/chromeos/drive/file_system_util.h"
#include "chrome/browser/chromeos/drive/job_scheduler.h"
#include "chrome/browser/chromeos/drive/resource_entry_conversion.h"
#include "chrome/browser/chromeos/drive/resource_metadata.h"
#include "chrome/browser/drive/drive_api_util.h"
#include "content/public/browser/browser_thread.h"
#include "google_apis/drive/drive_api_parser.h"
using content::BrowserThread;
namespace drive {
namespace file_system {
struct CopyOperation::CopyParams {
base::FilePath src_file_path;
base::FilePath dest_file_path;
bool preserve_last_modified;
FileOperationCallback callback;
ResourceEntry src_entry;
ResourceEntry parent_entry;
};
enum JsonGdocLocationType {
NOT_IN_METADATA,
IS_ORPHAN,
HAS_PARENT,
};
struct CopyOperation::TransferJsonGdocParams {
TransferJsonGdocParams(const FileOperationCallback& callback,
const std::string& resource_id,
const ResourceEntry& parent_entry,
const std::string& new_title)
: callback(callback),
resource_id(resource_id),
parent_resource_id(parent_entry.resource_id()),
parent_local_id(parent_entry.local_id()),
new_title(new_title),
location_type(NOT_IN_METADATA) {
}
const FileOperationCallback callback;
const std::string resource_id;
const std::string parent_resource_id;
const std::string parent_local_id;
const std::string new_title;
JsonGdocLocationType location_type;
std::string local_id;
base::FilePath changed_path;
};
namespace {
FileError TryToCopyLocally(internal::ResourceMetadata* metadata,
internal::FileCache* cache,
CopyOperation::CopyParams* params,
std::vector<std::string>* updated_local_ids,
bool* directory_changed,
bool* should_copy_on_server) {
FileError error = metadata->GetResourceEntryByPath(params->src_file_path,
¶ms->src_entry);
if (error != FILE_ERROR_OK)
return error;
error = metadata->GetResourceEntryByPath(params->dest_file_path.DirName(),
¶ms->parent_entry);
if (error != FILE_ERROR_OK)
return error;
if (!params->parent_entry.file_info().is_directory())
return FILE_ERROR_NOT_A_DIRECTORY;
if (params->src_entry.file_info().is_directory())
return FILE_ERROR_NOT_A_FILE;
ResourceEntry dest_entry;
error = metadata->GetResourceEntryByPath(params->dest_file_path, &dest_entry);
switch (error) {
case FILE_ERROR_OK:
if (dest_entry.file_info().is_directory())
return FILE_ERROR_INVALID_OPERATION;
dest_entry.set_parent_local_id(util::kDriveTrashDirLocalId);
error = metadata->RefreshEntry(dest_entry);
if (error != FILE_ERROR_OK)
return error;
updated_local_ids->push_back(dest_entry.local_id());
*directory_changed = true;
break;
case FILE_ERROR_NOT_FOUND:
break;
default:
return error;
}
FileCacheEntry cache_entry;
cache->GetCacheEntry(params->src_entry.local_id(), &cache_entry);
if (!cache_entry.is_present() && !params->src_entry.resource_id().empty()) {
*should_copy_on_server = true;
return FILE_ERROR_OK;
}
ResourceEntry entry;
const int64 now = base::Time::Now().ToInternalValue();
entry.set_title(params->dest_file_path.BaseName().AsUTF8Unsafe());
entry.set_parent_local_id(params->parent_entry.local_id());
entry.mutable_file_specific_info()->set_content_mime_type(
params->src_entry.file_specific_info().content_mime_type());
entry.set_metadata_edit_state(ResourceEntry::DIRTY);
entry.mutable_file_info()->set_last_modified(
params->preserve_last_modified ?
params->src_entry.file_info().last_modified() : now);
entry.mutable_file_info()->set_last_accessed(now);
std::string local_id;
error = metadata->AddEntry(entry, &local_id);
if (error != FILE_ERROR_OK)
return error;
updated_local_ids->push_back(local_id);
*directory_changed = true;
if (!cache_entry.is_present()) {
DCHECK(params->src_entry.resource_id().empty());
return FILE_ERROR_OK;
}
base::FilePath cache_file_path;
error = cache->GetFile(params->src_entry.local_id(), &cache_file_path);
if (error != FILE_ERROR_OK)
return error;
return cache->Store(local_id, std::string(), cache_file_path,
internal::FileCache::FILE_OPERATION_COPY);
}
FileError UpdateLocalStateForServerSideOperation(
internal::ResourceMetadata* metadata,
scoped_ptr<google_apis::ResourceEntry> resource_entry,
base::FilePath* file_path) {
DCHECK(resource_entry);
ResourceEntry entry;
std::string parent_resource_id;
if (!ConvertToResourceEntry(*resource_entry, &entry, &parent_resource_id) ||
parent_resource_id.empty())
return FILE_ERROR_NOT_A_FILE;
std::string parent_local_id;
FileError error = metadata->GetIdByResourceId(parent_resource_id,
&parent_local_id);
if (error != FILE_ERROR_OK)
return error;
entry.set_parent_local_id(parent_local_id);
std::string local_id;
error = metadata->AddEntry(entry, &local_id);
if (error == FILE_ERROR_EXISTS)
error = metadata->GetIdByResourceId(entry.resource_id(), &local_id);
if (error == FILE_ERROR_OK)
*file_path = metadata->GetFilePath(local_id);
return error;
}
FileError UpdateLocalStateForScheduleTransfer(
internal::ResourceMetadata* metadata,
internal::FileCache* cache,
const base::FilePath& local_src_path,
const base::FilePath& remote_dest_path,
std::string* local_id) {
FileError error = metadata->GetIdByPath(remote_dest_path, local_id);
if (error != FILE_ERROR_OK)
return error;
ResourceEntry entry;
error = metadata->GetResourceEntryById(*local_id, &entry);
if (error != FILE_ERROR_OK)
return error;
return cache->Store(*local_id, std::string(), local_src_path,
internal::FileCache::FILE_OPERATION_COPY);
}
FileError PrepareTransferFileFromLocalToRemote(
internal::ResourceMetadata* metadata,
const base::FilePath& local_src_path,
const base::FilePath& remote_dest_path,
std::string* gdoc_resource_id,
ResourceEntry* parent_entry) {
FileError error = metadata->GetResourceEntryByPath(
remote_dest_path.DirName(), parent_entry);
if (error != FILE_ERROR_OK)
return error;
if (!parent_entry->file_info().is_directory())
return FILE_ERROR_NOT_A_DIRECTORY;
if (util::HasGDocFileExtension(local_src_path))
*gdoc_resource_id = util::ReadResourceIdFromGDocFile(local_src_path);
return FILE_ERROR_OK;
}
FileError LocalWorkForTransferJsonGdocFile(
internal::ResourceMetadata* metadata,
CopyOperation::TransferJsonGdocParams* params) {
std::string local_id;
FileError error = metadata->GetIdByResourceId(params->resource_id, &local_id);
if (error != FILE_ERROR_OK) {
params->location_type = NOT_IN_METADATA;
return error == FILE_ERROR_NOT_FOUND ? FILE_ERROR_OK : error;
}
ResourceEntry entry;
error = metadata->GetResourceEntryById(local_id, &entry);
if (error != FILE_ERROR_OK)
return error;
params->local_id = entry.local_id();
if (entry.parent_local_id() == util::kDriveOtherDirLocalId) {
params->location_type = IS_ORPHAN;
entry.set_title(params->new_title);
entry.set_parent_local_id(params->parent_local_id);
entry.set_metadata_edit_state(ResourceEntry::DIRTY);
error = metadata->RefreshEntry(entry);
if (error == FILE_ERROR_OK)
params->changed_path = metadata->GetFilePath(local_id);
return error;
}
params->location_type = HAS_PARENT;
return FILE_ERROR_OK;
}
}
CopyOperation::CopyOperation(base::SequencedTaskRunner* blocking_task_runner,
OperationObserver* observer,
JobScheduler* scheduler,
internal::ResourceMetadata* metadata,
internal::FileCache* cache,
const ResourceIdCanonicalizer& id_canonicalizer)
: blocking_task_runner_(blocking_task_runner),
observer_(observer),
scheduler_(scheduler),
metadata_(metadata),
cache_(cache),
id_canonicalizer_(id_canonicalizer),
create_file_operation_(new CreateFileOperation(blocking_task_runner,
observer,
metadata)),
weak_ptr_factory_(this) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
}
CopyOperation::~CopyOperation() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
}
void CopyOperation::Copy(const base::FilePath& src_file_path,
const base::FilePath& dest_file_path,
bool preserve_last_modified,
const FileOperationCallback& callback) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
DCHECK(!callback.is_null());
CopyParams* params = new CopyParams;
params->src_file_path = src_file_path;
params->dest_file_path = dest_file_path;
params->preserve_last_modified = preserve_last_modified;
params->callback = callback;
std::vector<std::string>* updated_local_ids = new std::vector<std::string>;
bool* directory_changed = new bool(false);
bool* should_copy_on_server = new bool(false);
base::PostTaskAndReplyWithResult(
blocking_task_runner_.get(),
FROM_HERE,
base::Bind(&TryToCopyLocally, metadata_, cache_, params,
updated_local_ids, directory_changed, should_copy_on_server),
base::Bind(&CopyOperation::CopyAfterTryToCopyLocally,
weak_ptr_factory_.GetWeakPtr(), base::Owned(params),
base::Owned(updated_local_ids), base::Owned(directory_changed),
base::Owned(should_copy_on_server)));
}
void CopyOperation::CopyAfterTryToCopyLocally(
const CopyParams* params,
const std::vector<std::string>* updated_local_ids,
const bool* directory_changed,
const bool* should_copy_on_server,
FileError error) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
DCHECK(!params->callback.is_null());
for (size_t i = 0; i < updated_local_ids->size(); ++i)
observer_->OnEntryUpdatedByOperation((*updated_local_ids)[i]);
if (*directory_changed)
observer_->OnDirectoryChangedByOperation(params->dest_file_path.DirName());
if (error != FILE_ERROR_OK || !*should_copy_on_server) {
params->callback.Run(error);
return;
}
base::FilePath new_title = params->dest_file_path.BaseName();
if (params->src_entry.file_specific_info().is_hosted_document()) {
new_title = new_title.RemoveExtension();
}
base::Time last_modified =
params->preserve_last_modified ?
base::Time::FromInternalValue(
params->src_entry.file_info().last_modified()) : base::Time();
CopyResourceOnServer(
params->src_entry.resource_id(), params->parent_entry.resource_id(),
new_title.AsUTF8Unsafe(), last_modified, params->callback);
}
void CopyOperation::TransferFileFromLocalToRemote(
const base::FilePath& local_src_path,
const base::FilePath& remote_dest_path,
const FileOperationCallback& callback) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
DCHECK(!callback.is_null());
std::string* gdoc_resource_id = new std::string;
ResourceEntry* parent_entry = new ResourceEntry;
base::PostTaskAndReplyWithResult(
blocking_task_runner_.get(),
FROM_HERE,
base::Bind(
&PrepareTransferFileFromLocalToRemote,
metadata_, local_src_path, remote_dest_path,
gdoc_resource_id, parent_entry),
base::Bind(
&CopyOperation::TransferFileFromLocalToRemoteAfterPrepare,
weak_ptr_factory_.GetWeakPtr(),
local_src_path, remote_dest_path, callback,
base::Owned(gdoc_resource_id), base::Owned(parent_entry)));
}
void CopyOperation::TransferFileFromLocalToRemoteAfterPrepare(
const base::FilePath& local_src_path,
const base::FilePath& remote_dest_path,
const FileOperationCallback& callback,
std::string* gdoc_resource_id,
ResourceEntry* parent_entry,
FileError error) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
DCHECK(!callback.is_null());
if (error != FILE_ERROR_OK) {
callback.Run(error);
return;
}
if (gdoc_resource_id->empty()) {
ScheduleTransferRegularFile(local_src_path, remote_dest_path, callback);
return;
}
const std::string canonicalized_resource_id =
id_canonicalizer_.Run(*gdoc_resource_id);
const std::string new_title =
remote_dest_path.BaseName().RemoveExtension().AsUTF8Unsafe();
TransferJsonGdocParams* params = new TransferJsonGdocParams(
callback, canonicalized_resource_id, *parent_entry, new_title);
base::PostTaskAndReplyWithResult(
blocking_task_runner_.get(),
FROM_HERE,
base::Bind(&LocalWorkForTransferJsonGdocFile, metadata_, params),
base::Bind(&CopyOperation::TransferJsonGdocFileAfterLocalWork,
weak_ptr_factory_.GetWeakPtr(), base::Owned(params)));
}
void CopyOperation::TransferJsonGdocFileAfterLocalWork(
TransferJsonGdocParams* params,
FileError error) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
if (error != FILE_ERROR_OK) {
params->callback.Run(error);
return;
}
switch (params->location_type) {
case HAS_PARENT:
CopyResourceOnServer(params->resource_id,
params->parent_resource_id,
params->new_title,
base::Time(),
params->callback);
break;
case IS_ORPHAN:
DCHECK(!params->changed_path.empty());
observer_->OnEntryUpdatedByOperation(params->local_id);
observer_->OnDirectoryChangedByOperation(params->changed_path.DirName());
params->callback.Run(error);
break;
case NOT_IN_METADATA:
scheduler_->UpdateResource(
params->resource_id,
params->parent_resource_id,
params->new_title,
base::Time(),
base::Time(),
ClientContext(USER_INITIATED),
base::Bind(&CopyOperation::UpdateAfterServerSideOperation,
weak_ptr_factory_.GetWeakPtr(),
params->callback));
break;
}
}
void CopyOperation::CopyResourceOnServer(
const std::string& resource_id,
const std::string& parent_resource_id,
const std::string& new_title,
const base::Time& last_modified,
const FileOperationCallback& callback) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
DCHECK(!callback.is_null());
scheduler_->CopyResource(
resource_id, parent_resource_id, new_title, last_modified,
base::Bind(&CopyOperation::UpdateAfterServerSideOperation,
weak_ptr_factory_.GetWeakPtr(),
callback));
}
void CopyOperation::UpdateAfterServerSideOperation(
const FileOperationCallback& callback,
google_apis::GDataErrorCode status,
scoped_ptr<google_apis::ResourceEntry> resource_entry) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
DCHECK(!callback.is_null());
FileError error = GDataToFileError(status);
if (error != FILE_ERROR_OK) {
callback.Run(error);
return;
}
base::FilePath* file_path = new base::FilePath;
base::PostTaskAndReplyWithResult(
blocking_task_runner_.get(),
FROM_HERE,
base::Bind(&UpdateLocalStateForServerSideOperation,
metadata_, base::Passed(&resource_entry), file_path),
base::Bind(&CopyOperation::UpdateAfterLocalStateUpdate,
weak_ptr_factory_.GetWeakPtr(),
callback, base::Owned(file_path)));
}
void CopyOperation::UpdateAfterLocalStateUpdate(
const FileOperationCallback& callback,
base::FilePath* file_path,
FileError error) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
DCHECK(!callback.is_null());
if (error == FILE_ERROR_OK)
observer_->OnDirectoryChangedByOperation(file_path->DirName());
callback.Run(error);
}
void CopyOperation::ScheduleTransferRegularFile(
const base::FilePath& local_src_path,
const base::FilePath& remote_dest_path,
const FileOperationCallback& callback) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
DCHECK(!callback.is_null());
create_file_operation_->CreateFile(
remote_dest_path,
false,
std::string(),
base::Bind(&CopyOperation::ScheduleTransferRegularFileAfterCreate,
weak_ptr_factory_.GetWeakPtr(),
local_src_path, remote_dest_path, callback));
}
void CopyOperation::ScheduleTransferRegularFileAfterCreate(
const base::FilePath& local_src_path,
const base::FilePath& remote_dest_path,
const FileOperationCallback& callback,
FileError error) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
DCHECK(!callback.is_null());
if (error != FILE_ERROR_OK) {
callback.Run(error);
return;
}
std::string* local_id = new std::string;
base::PostTaskAndReplyWithResult(
blocking_task_runner_.get(),
FROM_HERE,
base::Bind(
&UpdateLocalStateForScheduleTransfer,
metadata_, cache_, local_src_path, remote_dest_path, local_id),
base::Bind(
&CopyOperation::ScheduleTransferRegularFileAfterUpdateLocalState,
weak_ptr_factory_.GetWeakPtr(), callback, remote_dest_path,
base::Owned(local_id)));
}
void CopyOperation::ScheduleTransferRegularFileAfterUpdateLocalState(
const FileOperationCallback& callback,
const base::FilePath& remote_dest_path,
std::string* local_id,
FileError error) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
DCHECK(!callback.is_null());
if (error == FILE_ERROR_OK) {
observer_->OnDirectoryChangedByOperation(remote_dest_path.DirName());
observer_->OnEntryUpdatedByOperation(*local_id);
}
callback.Run(error);
}
}
}