#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_change.h"
#include "chrome/browser/chromeos/drive/file_system/create_file_operation.h"
-#include "chrome/browser/chromeos/drive/file_system/download_operation.h"
-#include "chrome/browser/chromeos/drive/file_system/move_operation.h"
-#include "chrome/browser/chromeos/drive/file_system/operation_observer.h"
+#include "chrome/browser/chromeos/drive/file_system/operation_delegate.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 for categorizing where a gdoc represented by a JSON file exists.
+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) {
+ }
+ // Parameters supplied or calculated from operation arguments.
+ 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;
+
+ // Values computed during operation.
+ JsonGdocLocationType location_type; // types where the gdoc file is located.
+ std::string local_id; // the local_id of the file (if exists in metadata.)
+ base::FilePath changed_path;
+};
+
namespace {
-FileError PrepareCopy(internal::ResourceMetadata* metadata,
- const base::FilePath& src_path,
- const base::FilePath& dest_path,
- ResourceEntry* src_entry,
- std::string* parent_resource_id) {
- FileError error = metadata->GetResourceEntryByPath(src_path, src_entry);
+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;
- ResourceEntry parent_entry;
- error = metadata->GetResourceEntryByPath(dest_path.DirName(), &parent_entry);
+ error = metadata->GetResourceEntryByPath(params->dest_file_path.DirName(),
+ ¶ms->parent_entry);
if (error != FILE_ERROR_OK)
return error;
- if (!parent_entry.file_info().is_directory())
+ if (!params->parent_entry.file_info().is_directory())
return FILE_ERROR_NOT_A_DIRECTORY;
// Drive File System doesn't support recursive copy.
- if (src_entry->file_info().is_directory())
+ if (params->src_entry.file_info().is_directory())
return FILE_ERROR_NOT_A_FILE;
- *parent_resource_id = parent_entry.resource_id();
- return FILE_ERROR_OK;
-}
+ // Check destination.
+ ResourceEntry dest_entry;
+ error = metadata->GetResourceEntryByPath(params->dest_file_path, &dest_entry);
+ switch (error) {
+ case FILE_ERROR_OK:
+ // File API spec says it is an error to try to "copy a file to a path
+ // occupied by a directory".
+ if (dest_entry.file_info().is_directory())
+ return FILE_ERROR_INVALID_OPERATION;
+
+ // Move the existing entry to the trash.
+ 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;
+ }
+
+ // If the cache file is not present and the entry exists on the server,
+ // server side copy should be used.
+ if (!params->src_entry.file_specific_info().cache_state().is_present() &&
+ !params->src_entry.resource_id().empty()) {
+ *should_copy_on_server = true;
+ return FILE_ERROR_OK;
+ }
+
+ // Copy locally.
+ 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.set_modification_date(base::Time::Now().ToInternalValue());
+ 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 (!params->src_entry.file_specific_info().cache_state().is_present()) {
+ DCHECK(params->src_entry.resource_id().empty());
+ // Locally created empty file may have no cache file.
+ 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;
-int64 GetFileSize(const base::FilePath& file_path) {
- int64 file_size;
- if (!file_util::GetFileSize(file_path, &file_size))
- return -1;
- return file_size;
+ return cache->Store(local_id, std::string(), cache_file_path,
+ internal::FileCache::FILE_OPERATION_COPY);
}
-// Stores the copied entry and returns its path.
-FileError UpdateLocalStateForServerSideCopy(
+// Stores the entry returned from the server and returns its path.
+FileError UpdateLocalStateForServerSideOperation(
internal::ResourceMetadata* metadata,
- scoped_ptr<google_apis::ResourceEntry> resource_entry,
+ scoped_ptr<google_apis::FileResource> file_resource,
+ ResourceEntry* entry,
base::FilePath* file_path) {
- DCHECK(resource_entry);
+ DCHECK(file_resource);
- ResourceEntry entry;
std::string parent_resource_id;
- if (!ConvertToResourceEntry(*resource_entry, &entry, &parent_resource_id))
+ if (!ConvertFileResourceToResourceEntry(
+ *file_resource, entry, &parent_resource_id) ||
+ parent_resource_id.empty())
return FILE_ERROR_NOT_A_FILE;
std::string parent_local_id;
&parent_local_id);
if (error != FILE_ERROR_OK)
return error;
- entry.set_parent_local_id(parent_local_id);
+ entry->set_parent_local_id(parent_local_id);
std::string local_id;
- error = metadata->AddEntry(entry, &local_id);
+ error = metadata->AddEntry(*entry, &local_id);
// Depending on timing, the metadata may have inserted via change list
// already. So, FILE_ERROR_EXISTS is not an error.
if (error == FILE_ERROR_EXISTS)
- error = metadata->GetIdByResourceId(entry.resource_id(), &local_id);
+ error = metadata->GetIdByResourceId(entry->resource_id(), &local_id);
- if (error == FILE_ERROR_OK)
- *file_path = metadata->GetFilePath(local_id);
+ if (error != FILE_ERROR_OK)
+ return error;
- return error;
+ return metadata->GetFilePath(local_id, file_path);
}
// Stores the file at |local_file_path| to the cache as a content of entry at
internal::FileCache* cache,
const base::FilePath& local_src_path,
const base::FilePath& remote_dest_path,
+ ResourceEntry* entry,
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;
-
- error = cache->Store(
- *local_id, entry.file_specific_info().md5(), local_src_path,
- internal::FileCache::FILE_OPERATION_COPY);
+ error = metadata->GetResourceEntryById(*local_id, entry);
if (error != FILE_ERROR_OK)
return error;
- error = cache->MarkDirty(*local_id);
- if (error != FILE_ERROR_OK)
- return error;
-
- return FILE_ERROR_OK;
+ return cache->Store(*local_id, std::string(), local_src_path,
+ internal::FileCache::FILE_OPERATION_COPY);
}
// Gets the file size of the |local_path|, and the ResourceEntry for the parent
const base::FilePath& local_src_path,
const base::FilePath& remote_dest_path,
std::string* gdoc_resource_id,
- std::string* parent_resource_id) {
- ResourceEntry parent_entry;
+ ResourceEntry* parent_entry) {
FileError error = metadata->GetResourceEntryByPath(
- remote_dest_path.DirName(), &parent_entry);
+ remote_dest_path.DirName(), parent_entry);
if (error != FILE_ERROR_OK)
return error;
// The destination's parent must be a directory.
- if (!parent_entry.file_info().is_directory())
+ if (!parent_entry->file_info().is_directory())
return FILE_ERROR_NOT_A_DIRECTORY;
// Try to parse GDoc File and extract the resource id, if necessary.
// Failing isn't problem. It'd be handled as a regular file, then.
- if (util::HasGDocFileExtension(local_src_path)) {
+ if (util::HasHostedDocumentExtension(local_src_path))
*gdoc_resource_id = util::ReadResourceIdFromGDocFile(local_src_path);
- *parent_resource_id = parent_entry.resource_id();
+ return FILE_ERROR_OK;
+}
+
+// Performs local work before server-side work for transferring JSON-represented
+// gdoc files.
+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);
+ entry.set_modification_date(base::Time::Now().ToInternalValue());
+ error = metadata->RefreshEntry(entry);
+ if (error != FILE_ERROR_OK)
+ return error;
+ return metadata->GetFilePath(local_id, ¶ms->changed_path);
}
+ params->location_type = HAS_PARENT;
return FILE_ERROR_OK;
}
} // namespace
-struct CopyOperation::CopyParams {
- base::FilePath src_file_path;
- base::FilePath dest_file_path;
- bool preserve_last_modified;
- FileOperationCallback callback;
-};
-
CopyOperation::CopyOperation(base::SequencedTaskRunner* blocking_task_runner,
- OperationObserver* observer,
+ OperationDelegate* delegate,
JobScheduler* scheduler,
internal::ResourceMetadata* metadata,
- internal::FileCache* cache,
- DriveServiceInterface* drive_service,
- const base::FilePath& temporary_file_directory)
+ internal::FileCache* cache)
: blocking_task_runner_(blocking_task_runner),
- observer_(observer),
+ delegate_(delegate),
scheduler_(scheduler),
metadata_(metadata),
cache_(cache),
- drive_service_(drive_service),
create_file_operation_(new CreateFileOperation(blocking_task_runner,
- observer,
- scheduler,
- metadata,
- cache)),
- download_operation_(new DownloadOperation(blocking_task_runner,
- observer,
- scheduler,
- metadata,
- cache,
- temporary_file_directory)),
- move_operation_(new MoveOperation(blocking_task_runner,
- observer,
- scheduler,
- metadata)),
+ delegate,
+ metadata)),
weak_ptr_factory_(this) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
}
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
DCHECK(!callback.is_null());
- CopyParams params;
- params.src_file_path = src_file_path;
- params.dest_file_path = dest_file_path;
- params.preserve_last_modified = preserve_last_modified;
- params.callback = callback;
+ 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;
- ResourceEntry* src_entry = new ResourceEntry;
- std::string* parent_resource_id = new std::string;
+ 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(&PrepareCopy,
- metadata_, src_file_path, dest_file_path,
- src_entry, parent_resource_id),
- base::Bind(&CopyOperation::CopyAfterPrepare,
- weak_ptr_factory_.GetWeakPtr(), params,
- base::Owned(src_entry), base::Owned(parent_resource_id)));
+ 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::CopyAfterPrepare(
- const CopyParams& params,
- ResourceEntry* src_entry,
- std::string* parent_resource_id,
+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());
+ DCHECK(!params->callback.is_null());
+
+ for (size_t i = 0; i < updated_local_ids->size(); ++i)
+ delegate_->OnEntryUpdatedByOperation((*updated_local_ids)[i]);
+
+ if (*directory_changed) {
+ FileChange changed_file;
+ DCHECK(!params->src_entry.file_info().is_directory());
+ changed_file.Update(params->dest_file_path,
+ FileChange::FILE_TYPE_FILE,
+ FileChange::ADD_OR_UPDATE);
+ delegate_->OnFileChangedByOperation(changed_file);
+ }
- if (error != FILE_ERROR_OK) {
- params.callback.Run(error);
+ if (error != FILE_ERROR_OK || !*should_copy_on_server) {
+ params->callback.Run(error);
return;
}
- // If Drive API v2 is enabled, we can copy resources on server side.
- if (util::IsDriveV2ApiEnabled()) {
- base::FilePath new_title = params.dest_file_path.BaseName();
- if (src_entry->file_specific_info().is_hosted_document()) {
- // Drop the document extension, which should not be in the title.
- // TODO(yoshiki): Remove this code with crbug.com/223304.
- new_title = new_title.RemoveExtension();
- }
-
- base::Time last_modified =
- params.preserve_last_modified ?
- base::Time::FromInternalValue(src_entry->file_info().last_modified()) :
- base::Time();
-
- CopyResourceOnServer(
- src_entry->resource_id(), *parent_resource_id,
- new_title.AsUTF8Unsafe(), last_modified, params.callback);
- return;
+ if (params->parent_entry.resource_id().empty()) {
+ // Parent entry may be being synced.
+ const bool waiting = delegate_->WaitForSyncComplete(
+ params->parent_entry.local_id(),
+ base::Bind(&CopyOperation::CopyAfterParentSync,
+ weak_ptr_factory_.GetWeakPtr(), *params));
+ if (!waiting)
+ params->callback.Run(FILE_ERROR_NOT_FOUND);
+ } else {
+ CopyAfterGetParentResourceId(*params, ¶ms->parent_entry, FILE_ERROR_OK);
}
+}
+
+void CopyOperation::CopyAfterParentSync(const CopyParams& params,
+ FileError error) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ DCHECK(!params.callback.is_null());
- // Here after GData WAPI's code.
- if (src_entry->file_specific_info().is_hosted_document()) {
- // For hosted documents, GData WAPI copies it on server.
- CopyHostedDocument(
- src_entry->resource_id(),
- // Drop the document extension, which should not be in the title.
- // TODO(yoshiki): Remove this code with crbug.com/223304.
- params.dest_file_path.RemoveExtension(),
- params.callback);
+ if (error != FILE_ERROR_OK) {
+ params.callback.Run(error);
return;
}
- // For regular files, download the content, and then re-upload.
- // Note that upload is done later by SyncClient.
- download_operation_->EnsureFileDownloadedByPath(
- params.src_file_path,
- ClientContext(USER_INITIATED),
- GetFileContentInitializedCallback(),
- google_apis::GetContentCallback(),
- base::Bind(&CopyOperation::CopyAfterDownload,
- weak_ptr_factory_.GetWeakPtr(),
- params.dest_file_path, params.callback));
+ ResourceEntry* parent = new ResourceEntry;
+ base::PostTaskAndReplyWithResult(
+ blocking_task_runner_,
+ FROM_HERE,
+ base::Bind(&internal::ResourceMetadata::GetResourceEntryById,
+ base::Unretained(metadata_),
+ params.parent_entry.local_id(), parent),
+ base::Bind(&CopyOperation::CopyAfterGetParentResourceId,
+ weak_ptr_factory_.GetWeakPtr(), params, base::Owned(parent)));
}
-void CopyOperation::CopyAfterDownload(
- const base::FilePath& dest_file_path,
- const FileOperationCallback& callback,
- FileError error,
- const base::FilePath& local_file_path,
- scoped_ptr<ResourceEntry> entry) {
+void CopyOperation::CopyAfterGetParentResourceId(const CopyParams& params,
+ const ResourceEntry* parent,
+ FileError error) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- DCHECK(!callback.is_null());
+ DCHECK(!params.callback.is_null());
if (error != FILE_ERROR_OK) {
- callback.Run(error);
+ params.callback.Run(error);
return;
}
- // This callback is only triggered for a regular file via Copy().
- DCHECK(entry && !entry->file_specific_info().is_hosted_document());
- ScheduleTransferRegularFile(local_file_path, dest_file_path, callback);
+ base::FilePath new_title = params.dest_file_path.BaseName();
+ if (params.src_entry.file_specific_info().is_hosted_document()) {
+ // Drop the document extension, which should not be in the title.
+ // TODO(yoshiki): Remove this code with crbug.com/223304.
+ 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(), parent->resource_id(),
+ new_title.AsUTF8Unsafe(), last_modified, params.callback);
}
void CopyOperation::TransferFileFromLocalToRemote(
DCHECK(!callback.is_null());
std::string* gdoc_resource_id = new std::string;
- std::string* parent_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_resource_id),
+ 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_resource_id)));
+ base::Owned(gdoc_resource_id), base::Owned(parent_entry)));
}
void CopyOperation::TransferFileFromLocalToRemoteAfterPrepare(
const base::FilePath& remote_dest_path,
const FileOperationCallback& callback,
std::string* gdoc_resource_id,
- std::string* parent_resource_id,
+ ResourceEntry* parent_entry,
FileError error) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
DCHECK(!callback.is_null());
return;
}
- // This is uploading a JSON file representing a hosted document.
- // Copy the document on the Drive server.
-
// GDoc file may contain a resource ID in the old format.
const std::string canonicalized_resource_id =
- drive_service_->GetResourceIdCanonicalizer().Run(*gdoc_resource_id);
-
- // If Drive API v2 is enabled, we can copy resources on server side.
- if (util::IsDriveV2ApiEnabled()) {
- CopyResourceOnServer(
- *gdoc_resource_id, *parent_resource_id,
- // Drop the document extension, which should not be in the title.
- // TODO(yoshiki): Remove this code with crbug.com/223304.
- remote_dest_path.BaseName().RemoveExtension().AsUTF8Unsafe(),
- base::Time(),
- callback);
+ util::CanonicalizeResourceId(*gdoc_resource_id);
+
+ // Drop the document extension, which should not be in the title.
+ // TODO(yoshiki): Remove this code with crbug.com/223304.
+ const std::string new_title =
+ remote_dest_path.BaseName().RemoveExtension().AsUTF8Unsafe();
+
+ // This is uploading a JSON file representing a hosted document.
+ 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;
}
- CopyHostedDocument(
- canonicalized_resource_id,
- // Drop the document extension, which should not be
- // in the document title.
- // TODO(yoshiki): Remove this code with crbug.com/223304.
- remote_dest_path.RemoveExtension(),
- callback);
+ switch (params->location_type) {
+ // When |resource_id| is found in the local metadata and it has a specific
+ // parent folder, we assume the user's intention is to copy the document and
+ // thus perform the server-side copy operation.
+ case HAS_PARENT:
+ CopyResourceOnServer(params->resource_id,
+ params->parent_resource_id,
+ params->new_title,
+ base::Time(),
+ params->callback);
+ break;
+ // When |resource_id| has no parent, we just set the new destination folder
+ // as the parent, for sharing the document between the original source.
+ // This reparenting is already done in LocalWorkForTransferJsonGdocFile().
+ case IS_ORPHAN: {
+ DCHECK(!params->changed_path.empty());
+ delegate_->OnEntryUpdatedByOperation(params->local_id);
+
+ FileChange changed_file;
+ changed_file.Update(
+ params->changed_path,
+ FileChange::FILE_TYPE_FILE, // This must be a hosted document.
+ FileChange::ADD_OR_UPDATE);
+ delegate_->OnFileChangedByOperation(changed_file);
+ params->callback.Run(error);
+ break;
+ }
+ // When the |resource_id| is not in the local metadata, assume it to be a
+ // document just now shared on the server but not synced locally.
+ // Same as the IS_ORPHAN case, we want to deal the case by setting parent,
+ // but this time we need to resort to server side operation.
+ 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(
scheduler_->CopyResource(
resource_id, parent_resource_id, new_title, last_modified,
- base::Bind(&CopyOperation::CopyResourceOnServerAfterServerSideCopy,
+ base::Bind(&CopyOperation::UpdateAfterServerSideOperation,
weak_ptr_factory_.GetWeakPtr(),
callback));
}
-void CopyOperation::CopyResourceOnServerAfterServerSideCopy(
+void CopyOperation::UpdateAfterServerSideOperation(
const FileOperationCallback& callback,
google_apis::GDataErrorCode status,
- scoped_ptr<google_apis::ResourceEntry> resource_entry) {
+ scoped_ptr<google_apis::FileResource> entry) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
DCHECK(!callback.is_null());
return;
}
+ ResourceEntry* resource_entry = new ResourceEntry;
+
// The copy on the server side is completed successfully. Update the local
// metadata.
base::FilePath* file_path = new base::FilePath;
base::PostTaskAndReplyWithResult(
blocking_task_runner_.get(),
FROM_HERE,
- base::Bind(&UpdateLocalStateForServerSideCopy,
- metadata_, base::Passed(&resource_entry), file_path),
- base::Bind(&CopyOperation::CopyResourceOnServerAfterUpdateLocalState,
+ base::Bind(&UpdateLocalStateForServerSideOperation,
+ metadata_,
+ base::Passed(&entry),
+ resource_entry,
+ file_path),
+ base::Bind(&CopyOperation::UpdateAfterLocalStateUpdate,
weak_ptr_factory_.GetWeakPtr(),
- callback, base::Owned(file_path)));
+ callback,
+ base::Owned(file_path),
+ base::Owned(resource_entry)));
}
-void CopyOperation::CopyResourceOnServerAfterUpdateLocalState(
+void CopyOperation::UpdateAfterLocalStateUpdate(
const FileOperationCallback& callback,
base::FilePath* file_path,
+ const ResourceEntry* entry,
FileError error) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
DCHECK(!callback.is_null());
- if (error == FILE_ERROR_OK)
- observer_->OnDirectoryChangedByOperation(file_path->DirName());
+ if (error == FILE_ERROR_OK) {
+ FileChange changed_file;
+ changed_file.Update(*file_path, *entry, FileChange::ADD_OR_UPDATE);
+ delegate_->OnFileChangedByOperation(changed_file);
+ }
callback.Run(error);
}
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
DCHECK(!callback.is_null());
- base::PostTaskAndReplyWithResult(
- blocking_task_runner_.get(),
- FROM_HERE,
- base::Bind(&GetFileSize, local_src_path),
- base::Bind(&CopyOperation::ScheduleTransferRegularFileAfterGetFileSize,
- weak_ptr_factory_.GetWeakPtr(),
- local_src_path, remote_dest_path, callback));
-}
-
-void CopyOperation::ScheduleTransferRegularFileAfterGetFileSize(
- const base::FilePath& local_src_path,
- const base::FilePath& remote_dest_path,
- const FileOperationCallback& callback,
- int64 local_file_size) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- DCHECK(!callback.is_null());
-
- if (local_file_size < 0) {
- callback.Run(FILE_ERROR_NOT_FOUND);
- return;
- }
-
- // For regular files, check the server-side quota whether sufficient space
- // is available for the file to be uploaded.
- scheduler_->GetAboutResource(
- base::Bind(
- &CopyOperation::ScheduleTransferRegularFileAfterGetAboutResource,
- weak_ptr_factory_.GetWeakPtr(),
- local_src_path, remote_dest_path, callback, local_file_size));
-}
-
-void CopyOperation::ScheduleTransferRegularFileAfterGetAboutResource(
- const base::FilePath& local_src_path,
- const base::FilePath& remote_dest_path,
- const FileOperationCallback& callback,
- int64 local_file_size,
- google_apis::GDataErrorCode status,
- scoped_ptr<google_apis::AboutResource> about_resource) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- DCHECK(!callback.is_null());
-
- FileError error = GDataToFileError(status);
- if (error != FILE_ERROR_OK) {
- callback.Run(error);
- return;
- }
-
- DCHECK(about_resource);
- const int64 space =
- about_resource->quota_bytes_total() - about_resource->quota_bytes_used();
- if (space < local_file_size) {
- callback.Run(FILE_ERROR_NO_SERVER_SPACE);
- return;
- }
-
create_file_operation_->CreateFile(
remote_dest_path,
- true, // Exclusive (i.e. fail if a file already exists).
+ false, // Not exclusive (OK even if a file already exists).
std::string(), // no specific mime type; CreateFile should guess it.
base::Bind(&CopyOperation::ScheduleTransferRegularFileAfterCreate,
weak_ptr_factory_.GetWeakPtr(),
}
std::string* local_id = new std::string;
+ ResourceEntry* entry = new ResourceEntry;
base::PostTaskAndReplyWithResult(
blocking_task_runner_.get(),
FROM_HERE,
- base::Bind(
- &UpdateLocalStateForScheduleTransfer,
- metadata_, cache_, local_src_path, remote_dest_path, local_id),
+ base::Bind(&UpdateLocalStateForScheduleTransfer,
+ metadata_,
+ cache_,
+ local_src_path,
+ remote_dest_path,
+ entry,
+ local_id),
base::Bind(
&CopyOperation::ScheduleTransferRegularFileAfterUpdateLocalState,
- weak_ptr_factory_.GetWeakPtr(), callback, base::Owned(local_id)));
+ weak_ptr_factory_.GetWeakPtr(),
+ callback,
+ remote_dest_path,
+ base::Owned(entry),
+ base::Owned(local_id)));
}
void CopyOperation::ScheduleTransferRegularFileAfterUpdateLocalState(
const FileOperationCallback& callback,
+ const base::FilePath& remote_dest_path,
+ const ResourceEntry* entry,
std::string* local_id,
FileError error) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
DCHECK(!callback.is_null());
- if (error == FILE_ERROR_OK)
- observer_->OnCacheFileUploadNeededByOperation(*local_id);
- callback.Run(error);
-}
-
-void CopyOperation::CopyHostedDocument(
- const std::string& resource_id,
- const base::FilePath& dest_path,
- const FileOperationCallback& callback) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- DCHECK(!callback.is_null());
-
- scheduler_->CopyHostedDocument(
- resource_id,
- dest_path.BaseName().AsUTF8Unsafe(),
- base::Bind(&CopyOperation::CopyHostedDocumentAfterServerSideCopy,
- weak_ptr_factory_.GetWeakPtr(), dest_path, callback));
-}
-
-void CopyOperation::CopyHostedDocumentAfterServerSideCopy(
- const base::FilePath& dest_path,
- 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;
- }
-
- // The document is copied into the root directory (temporarily) on the server,
- // so we should first update the local metadata, then move it to the
- // destination directory.
- base::FilePath* file_path = new base::FilePath;
- base::PostTaskAndReplyWithResult(
- blocking_task_runner_.get(),
- FROM_HERE,
- base::Bind(&UpdateLocalStateForServerSideCopy,
- metadata_, base::Passed(&resource_entry), file_path),
- base::Bind(&CopyOperation::CopyHostedDocumentAfterUpdateLocalState,
- weak_ptr_factory_.GetWeakPtr(),
- dest_path, callback, base::Owned(file_path)));
-}
-
-void CopyOperation::CopyHostedDocumentAfterUpdateLocalState(
- const base::FilePath& dest_path,
- const FileOperationCallback& callback,
- base::FilePath* file_path,
- FileError error) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- DCHECK(!callback.is_null());
-
- // If an error is found, or the destination directory is mydrive root
- // (i.e. it is unnecessary to move the copied document), return the status.
- if (error != FILE_ERROR_OK ||
- dest_path.DirName() == util::GetDriveMyDriveRootPath()) {
- callback.Run(error);
- return;
+ if (error == FILE_ERROR_OK) {
+ FileChange changed_file;
+ changed_file.Update(remote_dest_path, *entry, FileChange::ADD_OR_UPDATE);
+ delegate_->OnFileChangedByOperation(changed_file);
+ delegate_->OnEntryUpdatedByOperation(*local_id);
}
-
- DCHECK_EQ(util::GetDriveMyDriveRootPath().value(),
- file_path->DirName().value()) << file_path->value();
-
- move_operation_->Move(*file_path, dest_path, false, callback);
+ callback.Run(error);
}
} // namespace file_system